aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorPer Abrahamsen1997-04-07 13:42:59 +0000
committerPer Abrahamsen1997-04-07 13:42:59 +0000
commitd543e20b611fc289b174aa82cab940d873a586ff (patch)
treee386ce6041bb396f332302365940779dfa76e33a /lisp
parent383ebe4953db084cb3695eba5486e1c905907eb5 (diff)
downloademacs-d543e20b611fc289b174aa82cab940d873a586ff.tar.gz
emacs-d543e20b611fc289b174aa82cab940d873a586ff.zip
Initial revision
Diffstat (limited to 'lisp')
-rw-r--r--lisp/cus-edit.el1993
-rw-r--r--lisp/cus-face.el590
-rw-r--r--lisp/custom.el2726
-rw-r--r--lisp/wid-browse.el232
-rw-r--r--lisp/wid-edit.el2542
-rw-r--r--lisp/widget.el76
6 files changed, 5726 insertions, 2433 deletions
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
new file mode 100644
index 00000000000..0327c7aa286
--- /dev/null
+++ b/lisp/cus-edit.el
@@ -0,0 +1,1993 @@
1;;; cus-edit.el --- Tools for customization Emacs.
2;;
3;; Copyright (C) 1996, 1997 Free Software Foundation, Inc.
4;;
5;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6;; Keywords: help, faces
7;; Version: 1.71
8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
9
10;;; Commentary:
11;;
12;; See `custom.el'.
13
14;;; Code:
15
16(require 'cus-face)
17(require 'wid-edit)
18(require 'easymenu)
19
20(define-widget-keywords :custom-prefixes :custom-menu :custom-show
21 :custom-magic :custom-state :custom-level :custom-form
22 :custom-set :custom-save :custom-reset-current :custom-reset-saved
23 :custom-reset-factory)
24
25;;; Customization Groups.
26
27(defgroup emacs nil
28 "Customization of the One True Editor."
29 :link '(custom-manual "(emacs)Top"))
30
31;; Most of these groups are stolen from `finder.el',
32(defgroup editing nil
33 "Basic text editing facilities."
34 :group 'emacs)
35
36(defgroup abbrev nil
37 "Abbreviation handling, typing shortcuts, macros."
38 :tag "Abbreviations"
39 :group 'editing)
40
41(defgroup matching nil
42 "Various sorts of searching and matching."
43 :group 'editing)
44
45(defgroup emulations nil
46 "Emulations of other editors."
47 :group 'editing)
48
49(defgroup mouse nil
50 "Mouse support."
51 :group 'editing)
52
53(defgroup outlines nil
54 "Support for hierarchical outlining."
55 :group 'editing)
56
57(defgroup external nil
58 "Interfacing to external utilities."
59 :group 'emacs)
60
61(defgroup bib nil
62 "Code related to the `bib' bibliography processor."
63 :tag "Bibliography"
64 :group 'external)
65
66(defgroup processes nil
67 "Process, subshell, compilation, and job control support."
68 :group 'external
69 :group 'development)
70
71(defgroup programming nil
72 "Support for programming in other languages."
73 :group 'emacs)
74
75(defgroup languages nil
76 "Specialized modes for editing programming languages."
77 :group 'programming)
78
79(defgroup lisp nil
80 "Lisp support, including Emacs Lisp."
81 :group 'languages
82 :group 'development)
83
84(defgroup c nil
85 "Support for the C language and related languages."
86 :group 'languages)
87
88(defgroup tools nil
89 "Programming tools."
90 :group 'programming)
91
92(defgroup oop nil
93 "Support for object-oriented programming."
94 :group 'programming)
95
96(defgroup applications nil
97 "Applications written in Emacs."
98 :group 'emacs)
99
100(defgroup calendar nil
101 "Calendar and time management support."
102 :group 'applications)
103
104(defgroup mail nil
105 "Modes for electronic-mail handling."
106 :group 'applications)
107
108(defgroup news nil
109 "Support for netnews reading and posting."
110 :group 'applications)
111
112(defgroup games nil
113 "Games, jokes and amusements."
114 :group 'applications)
115
116(defgroup development nil
117 "Support for further development of Emacs."
118 :group 'emacs)
119
120(defgroup docs nil
121 "Support for Emacs documentation."
122 :group 'development)
123
124(defgroup extensions nil
125 "Emacs Lisp language extensions."
126 :group 'development)
127
128(defgroup internal nil
129 "Code for Emacs internals, build process, defaults."
130 :group 'development)
131
132(defgroup maint nil
133 "Maintenance aids for the Emacs development group."
134 :tag "Maintenance"
135 :group 'development)
136
137(defgroup environment nil
138 "Fitting Emacs with its environment."
139 :group 'emacs)
140
141(defgroup comm nil
142 "Communications, networking, remote access to files."
143 :tag "Communication"
144 :group 'environment)
145
146(defgroup hardware nil
147 "Support for interfacing with exotic hardware."
148 :group 'environment)
149
150(defgroup terminals nil
151 "Support for terminal types."
152 :group 'environment)
153
154(defgroup unix nil
155 "Front-ends/assistants for, or emulators of, UNIX features."
156 :group 'environment)
157
158(defgroup vms nil
159 "Support code for vms."
160 :group 'environment)
161
162(defgroup i18n nil
163 "Internationalization and alternate character-set support."
164 :group 'environment
165 :group 'editing)
166
167(defgroup frames nil
168 "Support for Emacs frames and window systems."
169 :group 'environment)
170
171(defgroup data nil
172 "Support editing files of data."
173 :group 'emacs)
174
175(defgroup wp nil
176 "Word processing."
177 :group 'emacs)
178
179(defgroup tex nil
180 "Code related to the TeX formatter."
181 :group 'wp)
182
183(defgroup faces nil
184 "Support for multiple fonts."
185 :group 'emacs)
186
187(defgroup hypermedia nil
188 "Support for links between text or other media types."
189 :group 'emacs)
190
191(defgroup help nil
192 "Support for on-line help systems."
193 :group 'emacs)
194
195(defgroup local nil
196 "Code local to your site."
197 :group 'emacs)
198
199(defgroup customize '((widgets custom-group))
200 "Customization of the Customization support."
201 :link '(custom-manual "(custom)Top")
202 :link '(url-link :tag "Development Page"
203 "http://www.dina.kvl.dk/~abraham/custom/")
204 :prefix "custom-"
205 :group 'help
206 :group 'faces)
207
208;;; Utilities.
209
210(defun custom-quote (sexp)
211 "Quote SEXP iff it is not self quoting."
212 (if (or (memq sexp '(t nil))
213 (and (symbolp sexp)
214 (eq (aref (symbol-name sexp) 0) ?:))
215 (and (listp sexp)
216 (memq (car sexp) '(lambda)))
217 (stringp sexp)
218 (numberp sexp)
219 (and (fboundp 'characterp)
220 (characterp sexp)))
221 sexp
222 (list 'quote sexp)))
223
224(defun custom-split-regexp-maybe (regexp)
225 "If REGEXP is a string, split it to a list at `\\|'.
226You can get the original back with from the result with:
227 (mapconcat 'identity result \"\\|\")
228
229IF REGEXP is not a string, return it unchanged."
230 (if (stringp regexp)
231 (let ((start 0)
232 all)
233 (while (string-match "\\\\|" regexp start)
234 (setq all (cons (substring regexp start (match-beginning 0)) all)
235 start (match-end 0)))
236 (nreverse (cons (substring regexp start) all)))
237 regexp))
238
239(defvar custom-prefix-list nil
240 "List of prefixes that should be ignored by `custom-unlispify'")
241
242(defcustom custom-unlispify-menu-entries t
243 "Display menu entries as words instead of symbols if non nil."
244 :group 'customize
245 :type 'boolean)
246
247(defun custom-unlispify-menu-entry (symbol &optional no-suffix)
248 "Convert symbol into a menu entry."
249 (cond ((not custom-unlispify-menu-entries)
250 (symbol-name symbol))
251 ((get symbol 'custom-tag)
252 (if no-suffix
253 (get symbol 'custom-tag)
254 (concat (get symbol 'custom-tag) "...")))
255 (t
256 (save-excursion
257 (set-buffer (get-buffer-create " *Custom-Work*"))
258 (erase-buffer)
259 (princ symbol (current-buffer))
260 (goto-char (point-min))
261 (let ((prefixes custom-prefix-list)
262 prefix)
263 (while prefixes
264 (setq prefix (car prefixes))
265 (if (search-forward prefix (+ (point) (length prefix)) t)
266 (progn
267 (setq prefixes nil)
268 (delete-region (point-min) (point)))
269 (setq prefixes (cdr prefixes)))))
270 (subst-char-in-region (point-min) (point-max) ?- ?\ t)
271 (capitalize-region (point-min) (point-max))
272 (unless no-suffix
273 (goto-char (point-max))
274 (insert "..."))
275 (buffer-string)))))
276
277(defcustom custom-unlispify-tag-names t
278 "Display tag names as words instead of symbols if non nil."
279 :group 'customize
280 :type 'boolean)
281
282(defun custom-unlispify-tag-name (symbol)
283 "Convert symbol into a menu entry."
284 (let ((custom-unlispify-menu-entries custom-unlispify-tag-names))
285 (custom-unlispify-menu-entry symbol t)))
286
287(defun custom-prefix-add (symbol prefixes)
288 ;; Addd SYMBOL to list of ignored PREFIXES.
289 (cons (or (get symbol 'custom-prefix)
290 (concat (symbol-name symbol) "-"))
291 prefixes))
292
293;;; The Custom Mode.
294
295(defvar custom-options nil
296 "Customization widgets in the current buffer.")
297
298(defvar custom-mode-map nil
299 "Keymap for `custom-mode'.")
300
301(unless custom-mode-map
302 (setq custom-mode-map (make-sparse-keymap))
303 (set-keymap-parent custom-mode-map widget-keymap)
304 (define-key custom-mode-map "q" 'bury-buffer))
305
306(easy-menu-define custom-mode-menu
307 custom-mode-map
308 "Menu used in customization buffers."
309 '("Custom"
310 ["Set" custom-set t]
311 ["Save" custom-save t]
312 ["Reset to Current" custom-reset-current t]
313 ["Reset to Saved" custom-reset-saved t]
314 ["Reset to Factory Settings" custom-reset-factory t]
315 ["Info" (Info-goto-node "(custom)The Customization Buffer") t]))
316
317(defcustom custom-mode-hook nil
318 "Hook called when entering custom-mode."
319 :type 'hook
320 :group 'customize)
321
322(defun custom-mode ()
323 "Major mode for editing customization buffers.
324
325The following commands are available:
326
327\\[widget-forward] Move to next button or editable field.
328\\[widget-backward] Move to previous button or editable field.
329\\[widget-button-click] Activate button under the mouse pointer.
330\\[widget-button-press] Activate button under point.
331\\[custom-set] Set all modifications.
332\\[custom-save] Make all modifications default.
333\\[custom-reset-current] Reset all modified options.
334\\[custom-reset-saved] Reset all modified or set options.
335\\[custom-reset-factory] Reset all options.
336
337Entry to this mode calls the value of `custom-mode-hook'
338if that value is non-nil."
339 (kill-all-local-variables)
340 (setq major-mode 'custom-mode
341 mode-name "Custom")
342 (use-local-map custom-mode-map)
343 (easy-menu-add custom-mode-menu)
344 (make-local-variable 'custom-options)
345 (run-hooks 'custom-mode-hook))
346
347;;; Custom Mode Commands.
348
349(defun custom-set ()
350 "Set changes in all modified options."
351 (interactive)
352 (let ((children custom-options))
353 (mapcar (lambda (child)
354 (when (eq (widget-get child :custom-state) 'modified)
355 (widget-apply child :custom-set)))
356 children)))
357
358(defun custom-save ()
359 "Set all modified group members and save them."
360 (interactive)
361 (let ((children custom-options))
362 (mapcar (lambda (child)
363 (when (memq (widget-get child :custom-state) '(modified set))
364 (widget-apply child :custom-save)))
365 children))
366 (custom-save-all))
367
368(defvar custom-reset-menu
369 '(("Current" . custom-reset-current)
370 ("Saved" . custom-reset-saved)
371 ("Factory Settings" . custom-reset-factory))
372 "Alist of actions for the `Reset' button.
373The key is a string containing the name of the action, the value is a
374lisp function taking the widget as an element which will be called
375when the action is chosen.")
376
377(defun custom-reset (event)
378 "Select item from reset menu."
379 (let* ((completion-ignore-case t)
380 (answer (widget-choose "Reset to"
381 custom-reset-menu
382 event)))
383 (if answer
384 (funcall answer))))
385
386(defun custom-reset-current ()
387 "Reset all modified group members to their current value."
388 (interactive)
389 (let ((children custom-options))
390 (mapcar (lambda (child)
391 (when (eq (widget-get child :custom-state) 'modified)
392 (widget-apply child :custom-reset-current)))
393 children)))
394
395(defun custom-reset-saved ()
396 "Reset all modified or set group members to their saved value."
397 (interactive)
398 (let ((children custom-options))
399 (mapcar (lambda (child)
400 (when (eq (widget-get child :custom-state) 'modified)
401 (widget-apply child :custom-reset-current)))
402 children)))
403
404(defun custom-reset-factory ()
405 "Reset all modified, set, or saved group members to their factory settings."
406 (interactive)
407 (let ((children custom-options))
408 (mapcar (lambda (child)
409 (when (eq (widget-get child :custom-state) 'modified)
410 (widget-apply child :custom-reset-current)))
411 children)))
412
413;;; The Customize Commands
414
415;;;###autoload
416(defun customize (symbol)
417 "Customize SYMBOL, which must be a customization group."
418 (interactive (list (completing-read "Customize group: (default emacs) "
419 obarray
420 (lambda (symbol)
421 (get symbol 'custom-group))
422 t)))
423
424 (when (stringp symbol)
425 (if (string-equal "" symbol)
426 (setq symbol 'emacs)
427 (setq symbol (intern symbol))))
428 (custom-buffer-create (list (list symbol 'custom-group))))
429
430;;;###autoload
431(defun customize-variable (symbol)
432 "Customize SYMBOL, which must be a variable."
433 (interactive
434 ;; Code stolen from `help.el'.
435 (let ((v (variable-at-point))
436 (enable-recursive-minibuffers t)
437 val)
438 (setq val (completing-read
439 (if v
440 (format "Customize variable (default %s): " v)
441 "Customize variable: ")
442 obarray 'boundp t))
443 (list (if (equal val "")
444 v (intern val)))))
445 (custom-buffer-create (list (list symbol 'custom-variable))))
446
447;;;###autoload
448(defun customize-face (&optional symbol)
449 "Customize SYMBOL, which should be a face name or nil.
450If SYMBOL is nil, customize all faces."
451 (interactive (list (completing-read "Customize face: (default all) "
452 obarray 'custom-facep)))
453 (if (or (null symbol) (and (stringp symbol) (zerop (length symbol))))
454 (let ((found nil))
455 (message "Looking for faces...")
456 (mapcar (lambda (symbol)
457 (setq found (cons (list symbol 'custom-face) found)))
458 (face-list))
459 (custom-buffer-create found))
460 (if (stringp symbol)
461 (setq symbol (intern symbol)))
462 (unless (symbolp symbol)
463 (error "Should be a symbol %S" symbol))
464 (custom-buffer-create (list (list symbol 'custom-face)))))
465
466;;;###autoload
467(defun customize-customized ()
468 "Customize all already customized user options."
469 (interactive)
470 (let ((found nil))
471 (mapatoms (lambda (symbol)
472 (and (get symbol 'saved-face)
473 (custom-facep symbol)
474 (setq found (cons (list symbol 'custom-face) found)))
475 (and (get symbol 'saved-value)
476 (boundp symbol)
477 (setq found
478 (cons (list symbol 'custom-variable) found)))))
479 (if found
480 (custom-buffer-create found)
481 (error "No customized user options"))))
482
483;;;###autoload
484(defun customize-apropos (regexp &optional all)
485 "Customize all user options matching REGEXP.
486If ALL (e.g., started with a prefix key), include options which are not
487user-settable."
488 (interactive "sCustomize regexp: \nP")
489 (let ((found nil))
490 (mapatoms (lambda (symbol)
491 (when (string-match regexp (symbol-name symbol))
492 (when (get symbol 'custom-group)
493 (setq found (cons (list symbol 'custom-group) found)))
494 (when (custom-facep symbol)
495 (setq found (cons (list symbol 'custom-face) found)))
496 (when (and (boundp symbol)
497 (or (get symbol 'saved-value)
498 (get symbol 'factory-value)
499 (if all
500 (get symbol 'variable-documentation)
501 (user-variable-p symbol))))
502 (setq found
503 (cons (list symbol 'custom-variable) found))))))
504 (if found
505 (custom-buffer-create found)
506 (error "No matches"))))
507
508;;;###autoload
509(defun custom-buffer-create (options)
510 "Create a buffer containing OPTIONS.
511OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
512SYMBOL is a customization option, and WIDGET is a widget for editing
513that option."
514 (message "Creating customization buffer...")
515 (kill-buffer (get-buffer-create "*Customization*"))
516 (switch-to-buffer (get-buffer-create "*Customization*"))
517 (custom-mode)
518 (widget-insert "This is a customization buffer.
519Push RET or click mouse-2 on the word ")
520 ;; (put-text-property 1 2 'start-open nil)
521 (widget-create 'info-link
522 :tag "help"
523 :help-echo "Read the online help."
524 "(custom)The Customization Buffer")
525 (widget-insert " for more information.\n\n")
526 (setq custom-options
527 (if (= (length options) 1)
528 (mapcar (lambda (entry)
529 (widget-create (nth 1 entry)
530 :custom-state 'unknown
531 :tag (custom-unlispify-tag-name
532 (nth 0 entry))
533 :value (nth 0 entry)))
534 options)
535 (let ((count 0)
536 (length (length options)))
537 (mapcar (lambda (entry)
538 (prog2
539 (message "Creating customization items %2d%%..."
540 (/ (* 100.0 count) length))
541 (widget-create (nth 1 entry)
542 :tag (custom-unlispify-tag-name
543 (nth 0 entry))
544 :value (nth 0 entry))
545 (setq count (1+ count))
546 (unless (eq (preceding-char) ?\n)
547 (widget-insert "\n"))
548 (widget-insert "\n")))
549 options))))
550 (unless (eq (preceding-char) ?\n)
551 (widget-insert "\n"))
552 (widget-insert "\n")
553 (message "Creating customization magic...")
554 (mapcar 'custom-magic-reset custom-options)
555 (message "Creating customization buttons...")
556 (widget-create 'push-button
557 :tag "Set"
558 :help-echo "Set all modifications for this session."
559 :action (lambda (widget &optional event)
560 (custom-set)))
561 (widget-insert " ")
562 (widget-create 'push-button
563 :tag "Save"
564 :help-echo "\
565Make the modifications default for future sessions."
566 :action (lambda (widget &optional event)
567 (custom-save)))
568 (widget-insert " ")
569 (widget-create 'push-button
570 :tag "Reset"
571 :help-echo "Undo all modifications."
572 :action (lambda (widget &optional event)
573 (custom-reset event)))
574 (widget-insert " ")
575 (widget-create 'push-button
576 :tag "Done"
577 :help-echo "Bury the buffer."
578 :action (lambda (widget &optional event)
579 (bury-buffer)
580 ;; Steal button release event.
581 (if (and (fboundp 'button-press-event-p)
582 (fboundp 'next-command-event))
583 ;; XEmacs
584 (and event
585 (button-press-event-p event)
586 (next-command-event))
587 ;; Emacs
588 (when (memq 'down (event-modifiers event))
589 (read-event)))))
590 (widget-insert "\n")
591 (message "Creating customization setup...")
592 (widget-setup)
593 (goto-char (point-min))
594 (message "Creating customization buffer...done"))
595
596;;; Modification of Basic Widgets.
597;;
598;; We add extra properties to the basic widgets needed here. This is
599;; fine, as long as we are careful to stay within out own namespace.
600;;
601;; We want simple widgets to be displayed by default, but complex
602;; widgets to be hidden.
603
604(widget-put (get 'item 'widget-type) :custom-show t)
605(widget-put (get 'editable-field 'widget-type)
606 :custom-show (lambda (widget value)
607 (let ((pp (pp-to-string value)))
608 (cond ((string-match "\n" pp)
609 nil)
610 ((> (length pp) 40)
611 nil)
612 (t t)))))
613(widget-put (get 'menu-choice 'widget-type) :custom-show t)
614
615;;; The `custom-manual' Widget.
616
617(define-widget 'custom-manual 'info-link
618 "Link to the manual entry for this customization option."
619 :help-echo "Read the manual entry for this option."
620 :tag "Manual")
621
622;;; The `custom-magic' Widget.
623
624(defface custom-invalid-face '((((class color))
625 (:foreground "yellow" :background "red"))
626 (t
627 (:bold t :italic t :underline t)))
628 "Face used when the customize item is invalid.")
629
630(defface custom-rogue-face '((((class color))
631 (:foreground "pink" :background "black"))
632 (t
633 (:underline t)))
634 "Face used when the customize item is not defined for customization.")
635
636(defface custom-modified-face '((((class color))
637 (:foreground "white" :background "blue"))
638 (t
639 (:italic t :bold)))
640 "Face used when the customize item has been modified.")
641
642(defface custom-set-face '((((class color))
643 (:foreground "blue" :background "white"))
644 (t
645 (:italic t)))
646 "Face used when the customize item has been set.")
647
648(defface custom-changed-face '((((class color))
649 (:foreground "white" :background "blue"))
650 (t
651 (:italic t)))
652 "Face used when the customize item has been changed.")
653
654(defface custom-saved-face '((t (:underline t)))
655 "Face used when the customize item has been saved.")
656
657(defcustom custom-magic-alist '((nil "#" underline "\
658uninitialized, you should not see this.")
659 (unknown "?" italic "\
660unknown, you should not see this.")
661 (hidden "-" default "\
662hidden, press the state button to show.")
663 (invalid "x" custom-invalid-face "\
664the value displayed for this item is invalid and cannot be set.")
665 (modified "*" custom-modified-face "\
666you have edited the item, and can now set it.")
667 (set "+" custom-set-face "\
668you have set this item, but not saved it.")
669 (changed ":" custom-changed-face "\
670this item has been changed outside customize.")
671 (saved "!" custom-saved-face "\
672this item has been saved.")
673 (rogue "@" custom-rogue-face "\
674this item is not prepared for customization.")
675 (factory " " nil "\
676this item is unchanged from its factory setting."))
677 "Alist of customize option states.
678Each entry is of the form (STATE MAGIC FACE DESCRIPTION), where
679
680STATE is one of the following symbols:
681
682`nil'
683 For internal use, should never occur.
684`unknown'
685 For internal use, should never occur.
686`hidden'
687 This item is not being displayed.
688`invalid'
689 This item is modified, but has an invalid form.
690`modified'
691 This item is modified, and has a valid form.
692`set'
693 This item has been set but not saved.
694`changed'
695 The current value of this item has been changed temporarily.
696`saved'
697 This item is marked for saving.
698`rogue'
699 This item has no customization information.
700`factory'
701 This item is unchanged from the factory default.
702
703MAGIC is a string used to present that state.
704
705FACE is a face used to present the state.
706
707DESCRIPTION is a string describing the state.
708
709The list should be sorted most significant first."
710 :type '(list (checklist :inline t
711 (group (const nil)
712 (string :tag "Magic")
713 face
714 (string :tag "Description"))
715 (group (const unknown)
716 (string :tag "Magic")
717 face
718 (string :tag "Description"))
719 (group (const hidden)
720 (string :tag "Magic")
721 face
722 (string :tag "Description"))
723 (group (const invalid)
724 (string :tag "Magic")
725 face
726 (string :tag "Description"))
727 (group (const modified)
728 (string :tag "Magic")
729 face
730 (string :tag "Description"))
731 (group (const set)
732 (string :tag "Magic")
733 face
734 (string :tag "Description"))
735 (group (const changed)
736 (string :tag "Magic")
737 face
738 (string :tag "Description"))
739 (group (const saved)
740 (string :tag "Magic")
741 face
742 (string :tag "Description"))
743 (group (const rogue)
744 (string :tag "Magic")
745 face
746 (string :tag "Description"))
747 (group (const factory)
748 (string :tag "Magic")
749 face
750 (string :tag "Description")))
751 (editable-list :inline t
752 (group symbol
753 (string :tag "Magic")
754 face
755 (string :tag "Description"))))
756 :group 'customize)
757
758(defcustom custom-magic-show 'long
759 "Show long description of the state of each customization option."
760 :type '(choice (const :tag "no" nil)
761 (const short)
762 (const long))
763 :group 'customize)
764
765(defcustom custom-magic-show-button t
766 "Show a magic button indicating the state of each customization option."
767 :type 'boolean
768 :group 'customize)
769
770(define-widget 'custom-magic 'default
771 "Show and manipulate state for a customization option."
772 :format "%v"
773 :action 'widget-choice-item-action
774 :value-get 'ignore
775 :value-create 'custom-magic-value-create
776 :value-delete 'widget-children-value-delete)
777
778(defun custom-magic-value-create (widget)
779 ;; Create compact status report for WIDGET.
780 (let* ((parent (widget-get widget :parent))
781 (state (widget-get parent :custom-state))
782 (entry (assq state custom-magic-alist))
783 (magic (nth 1 entry))
784 (face (nth 2 entry))
785 (text (nth 3 entry))
786 (lisp (eq (widget-get parent :custom-form) 'lisp))
787 children)
788 (when custom-magic-show
789 (push (widget-create-child-and-convert widget 'choice-item
790 :help-echo "\
791Change the state of this item."
792 :format "%[%t%]"
793 :tag "State")
794 children)
795 (insert ": ")
796 (if (eq custom-magic-show 'long)
797 (insert text)
798 (insert (symbol-name state)))
799 (when lisp
800 (insert " (lisp)"))
801 (insert "\n"))
802 (when custom-magic-show-button
803 (when custom-magic-show
804 (let ((indent (widget-get parent :indent)))
805 (when indent
806 (insert-char ? indent))))
807 (push (widget-create-child-and-convert widget 'choice-item
808 :button-face face
809 :help-echo "Change the state."
810 :format "%[%t%]"
811 :tag (if lisp
812 (concat "(" magic ")")
813 (concat "[" magic "]")))
814 children)
815 (insert " "))
816 (widget-put widget :children children)))
817
818(defun custom-magic-reset (widget)
819 "Redraw the :custom-magic property of WIDGET."
820 (let ((magic (widget-get widget :custom-magic)))
821 (widget-value-set magic (widget-value magic))))
822
823;;; The `custom-level' Widget.
824
825(define-widget 'custom-level 'item
826 "The custom level buttons."
827 :format "%[%t%]"
828 :help-echo "Expand or collapse this item."
829 :action 'custom-level-action)
830
831(defun custom-level-action (widget &optional event)
832 "Toggle visibility for parent to WIDGET."
833 (let* ((parent (widget-get widget :parent))
834 (state (widget-get parent :custom-state)))
835 (cond ((memq state '(invalid modified))
836 (error "There are unset changes"))
837 ((eq state 'hidden)
838 (widget-put parent :custom-state 'unknown))
839 (t
840 (widget-put parent :custom-state 'hidden)))
841 (custom-redraw parent)))
842
843;;; The `custom' Widget.
844
845(define-widget 'custom 'default
846 "Customize a user option."
847 :convert-widget 'custom-convert-widget
848 :format "%l%[%t%]: %v%m%h%a"
849 :format-handler 'custom-format-handler
850 :notify 'custom-notify
851 :custom-level 1
852 :custom-state 'hidden
853 :documentation-property 'widget-subclass-responsibility
854 :value-create 'widget-subclass-responsibility
855 :value-delete 'widget-children-value-delete
856 :value-get 'widget-item-value-get
857 :validate 'widget-editable-list-validate
858 :match (lambda (widget value) (symbolp value)))
859
860(defun custom-convert-widget (widget)
861 ;; Initialize :value and :tag from :args in WIDGET.
862 (let ((args (widget-get widget :args)))
863 (when args
864 (widget-put widget :value (widget-apply widget
865 :value-to-internal (car args)))
866 (widget-put widget :tag (custom-unlispify-tag-name (car args)))
867 (widget-put widget :args nil)))
868 widget)
869
870(defun custom-format-handler (widget escape)
871 ;; We recognize extra escape sequences.
872 (let* ((buttons (widget-get widget :buttons))
873 (state (widget-get widget :custom-state))
874 (level (widget-get widget :custom-level)))
875 (cond ((eq escape ?l)
876 (when level
877 (push (widget-create-child-and-convert
878 widget 'custom-level (make-string level ?*))
879 buttons)
880 (widget-insert " ")
881 (widget-put widget :buttons buttons)))
882 ((eq escape ?L)
883 (when (eq state 'hidden)
884 (widget-insert " ...")))
885 ((eq escape ?m)
886 (and (eq (preceding-char) ?\n)
887 (widget-get widget :indent)
888 (insert-char ? (widget-get widget :indent)))
889 (let ((magic (widget-create-child-and-convert
890 widget 'custom-magic nil)))
891 (widget-put widget :custom-magic magic)
892 (push magic buttons)
893 (widget-put widget :buttons buttons)))
894 ((eq escape ?a)
895 (let* ((symbol (widget-get widget :value))
896 (links (get symbol 'custom-links))
897 (many (> (length links) 2)))
898 (when links
899 (and (eq (preceding-char) ?\n)
900 (widget-get widget :indent)
901 (insert-char ? (widget-get widget :indent)))
902 (insert "See also ")
903 (while links
904 (push (widget-create-child-and-convert widget (car links))
905 buttons)
906 (setq links (cdr links))
907 (cond ((null links)
908 (insert ".\n"))
909 ((null (cdr links))
910 (if many
911 (insert ", and ")
912 (insert " and ")))
913 (t
914 (insert ", "))))
915 (widget-put widget :buttons buttons))))
916 (t
917 (widget-default-format-handler widget escape)))))
918
919(defun custom-notify (widget &rest args)
920 "Keep track of changes."
921 (unless (memq (widget-get widget :custom-state) '(nil unknown hidden))
922 (widget-put widget :custom-state 'modified))
923 (let ((buffer-undo-list t))
924 (custom-magic-reset widget))
925 (apply 'widget-default-notify widget args))
926
927(defun custom-redraw (widget)
928 "Redraw WIDGET with current settings."
929 (let ((pos (point))
930 (from (marker-position (widget-get widget :from)))
931 (to (marker-position (widget-get widget :to))))
932 (save-excursion
933 (widget-value-set widget (widget-value widget))
934 (custom-redraw-magic widget))
935 (when (and (>= pos from) (<= pos to))
936 (goto-char pos))))
937
938(defun custom-redraw-magic (widget)
939 "Redraw WIDGET state with current settings."
940 (while widget
941 (let ((magic (widget-get widget :custom-magic)))
942 (unless magic
943 (debug))
944 (widget-value-set magic (widget-value magic))
945 (when (setq widget (widget-get widget :group))
946 (custom-group-state-update widget))))
947 (widget-setup))
948
949(defun custom-show (widget value)
950 "Non-nil if WIDGET should be shown with VALUE by default."
951 (let ((show (widget-get widget :custom-show)))
952 (cond ((null show)
953 nil)
954 ((eq t show)
955 t)
956 (t
957 (funcall show widget value)))))
958
959(defun custom-load-symbol (symbol)
960 "Load all dependencies for SYMBOL."
961 (let ((loads (get symbol 'custom-loads))
962 load)
963 (while loads
964 (setq load (car loads)
965 loads (cdr loads))
966 (cond ((symbolp load)
967 (condition-case nil
968 (require load)
969 (error nil)))
970 ((assoc load load-history))
971 (t
972 (condition-case nil
973 (load-library load)
974 (error nil)))))))
975
976(defun custom-load-widget (widget)
977 "Load all dependencies for WIDGET."
978 (custom-load-symbol (widget-value widget)))
979
980;;; The `custom-variable' Widget.
981
982(defface custom-variable-sample-face '((t (:underline t)))
983 "Face used for unpushable variable tags."
984 :group 'customize)
985
986(defface custom-variable-button-face '((t (:underline t :bold t)))
987 "Face used for pushable variable tags."
988 :group 'customize)
989
990(define-widget 'custom-variable 'custom
991 "Customize variable."
992 :format "%l%v%m%h%a"
993 :help-echo "Set or reset this variable."
994 :documentation-property 'variable-documentation
995 :custom-state nil
996 :custom-menu 'custom-variable-menu-create
997 :custom-form 'edit
998 :value-create 'custom-variable-value-create
999 :action 'custom-variable-action
1000 :custom-set 'custom-variable-set
1001 :custom-save 'custom-variable-save
1002 :custom-reset-current 'custom-redraw
1003 :custom-reset-saved 'custom-variable-reset-saved
1004 :custom-reset-factory 'custom-variable-reset-factory)
1005
1006(defun custom-variable-value-create (widget)
1007 "Here is where you edit the variables value."
1008 (custom-load-widget widget)
1009 (let* ((buttons (widget-get widget :buttons))
1010 (children (widget-get widget :children))
1011 (form (widget-get widget :custom-form))
1012 (state (widget-get widget :custom-state))
1013 (symbol (widget-get widget :value))
1014 (options (get symbol 'custom-options))
1015 (child-type (or (get symbol 'custom-type) 'sexp))
1016 (tag (widget-get widget :tag))
1017 (type (let ((tmp (if (listp child-type)
1018 (copy-list child-type)
1019 (list child-type))))
1020 (when options
1021 (widget-put tmp :options options))
1022 tmp))
1023 (conv (widget-convert type))
1024 (value (if (default-boundp symbol)
1025 (default-value symbol)
1026 (widget-get conv :value))))
1027 ;; If the widget is new, the child determine whether it is hidden.
1028 (cond (state)
1029 ((custom-show type value)
1030 (setq state 'unknown))
1031 (t
1032 (setq state 'hidden)))
1033 ;; If we don't know the state, see if we need to edit it in lisp form.
1034 (when (eq state 'unknown)
1035 (unless (widget-apply conv :match value)
1036 ;; (widget-apply (widget-convert type) :match value)
1037 (setq form 'lisp)))
1038 ;; Now we can create the child widget.
1039 (cond ((eq state 'hidden)
1040 ;; Indicate hidden value.
1041 (push (widget-create-child-and-convert
1042 widget 'item
1043 :format "%{%t%}: ..."
1044 :sample-face 'custom-variable-sample-face
1045 :tag tag
1046 :parent widget)
1047 children))
1048 ((eq form 'lisp)
1049 ;; In lisp mode edit the saved value when possible.
1050 (let* ((value (cond ((get symbol 'saved-value)
1051 (car (get symbol 'saved-value)))
1052 ((get symbol 'factory-value)
1053 (car (get symbol 'factory-value)))
1054 ((default-boundp symbol)
1055 (custom-quote (default-value symbol)))
1056 (t
1057 (custom-quote (widget-get conv :value))))))
1058 (push (widget-create-child-and-convert
1059 widget 'sexp
1060 :button-face 'custom-variable-button-face
1061 :tag (symbol-name symbol)
1062 :parent widget
1063 :value value)
1064 children)))
1065 (t
1066 ;; Edit mode.
1067 (push (widget-create-child-and-convert
1068 widget type
1069 :tag tag
1070 :button-face 'custom-variable-button-face
1071 :sample-face 'custom-variable-sample-face
1072 :value value)
1073 children)))
1074 ;; Now update the state.
1075 (unless (eq (preceding-char) ?\n)
1076 (widget-insert "\n"))
1077 (if (eq state 'hidden)
1078 (widget-put widget :custom-state state)
1079 (custom-variable-state-set widget))
1080 (widget-put widget :custom-form form)
1081 (widget-put widget :buttons buttons)
1082 (widget-put widget :children children)))
1083
1084(defun custom-variable-state-set (widget)
1085 "Set the state of WIDGET."
1086 (let* ((symbol (widget-value widget))
1087 (value (if (default-boundp symbol)
1088 (default-value symbol)
1089 (widget-get widget :value)))
1090 tmp
1091 (state (cond ((setq tmp (get symbol 'customized-value))
1092 (if (condition-case nil
1093 (equal value (eval (car tmp)))
1094 (error nil))
1095 'set
1096 'changed))
1097 ((setq tmp (get symbol 'saved-value))
1098 (if (condition-case nil
1099 (equal value (eval (car tmp)))
1100 (error nil))
1101 'saved
1102 'changed))
1103 ((setq tmp (get symbol 'factory-value))
1104 (if (condition-case nil
1105 (equal value (eval (car tmp)))
1106 (error nil))
1107 'factory
1108 'changed))
1109 (t 'rogue))))
1110 (widget-put widget :custom-state state)))
1111
1112(defvar custom-variable-menu
1113 '(("Edit" . custom-variable-edit)
1114 ("Edit Lisp" . custom-variable-edit-lisp)
1115 ("Set" . custom-variable-set)
1116 ("Save" . custom-variable-save)
1117 ("Reset to Current" . custom-redraw)
1118 ("Reset to Saved" . custom-variable-reset-saved)
1119 ("Reset to Factory Settings" . custom-variable-reset-factory))
1120 "Alist of actions for the `custom-variable' widget.
1121The key is a string containing the name of the action, the value is a
1122lisp function taking the widget as an element which will be called
1123when the action is chosen.")
1124
1125(defun custom-variable-action (widget &optional event)
1126 "Show the menu for `custom-variable' WIDGET.
1127Optional EVENT is the location for the menu."
1128 (if (eq (widget-get widget :custom-state) 'hidden)
1129 (progn
1130 (widget-put widget :custom-state 'unknown)
1131 (custom-redraw widget))
1132 (let* ((completion-ignore-case t)
1133 (answer (widget-choose (custom-unlispify-tag-name
1134 (widget-get widget :value))
1135 custom-variable-menu
1136 event)))
1137 (if answer
1138 (funcall answer widget)))))
1139
1140(defun custom-variable-edit (widget)
1141 "Edit value of WIDGET."
1142 (widget-put widget :custom-state 'unknown)
1143 (widget-put widget :custom-form 'edit)
1144 (custom-redraw widget))
1145
1146(defun custom-variable-edit-lisp (widget)
1147 "Edit the lisp representation of the value of WIDGET."
1148 (widget-put widget :custom-state 'unknown)
1149 (widget-put widget :custom-form 'lisp)
1150 (custom-redraw widget))
1151
1152(defun custom-variable-set (widget)
1153 "Set the current value for the variable being edited by WIDGET."
1154 (let ((form (widget-get widget :custom-form))
1155 (state (widget-get widget :custom-state))
1156 (child (car (widget-get widget :children)))
1157 (symbol (widget-value widget))
1158 val)
1159 (cond ((eq state 'hidden)
1160 (error "Cannot set hidden variable."))
1161 ((setq val (widget-apply child :validate))
1162 (goto-char (widget-get val :from))
1163 (error "%s" (widget-get val :error)))
1164 ((eq form 'lisp)
1165 (set symbol (eval (setq val (widget-value child))))
1166 (put symbol 'customized-value (list val)))
1167 (t
1168 (set symbol (setq val (widget-value child)))
1169 (put symbol 'customized-value (list (custom-quote val)))))
1170 (custom-variable-state-set widget)
1171 (custom-redraw-magic widget)))
1172
1173(defun custom-variable-save (widget)
1174 "Set the default value for the variable being edited by WIDGET."
1175 (let ((form (widget-get widget :custom-form))
1176 (state (widget-get widget :custom-state))
1177 (child (car (widget-get widget :children)))
1178 (symbol (widget-value widget))
1179 val)
1180 (cond ((eq state 'hidden)
1181 (error "Cannot set hidden variable."))
1182 ((setq val (widget-apply child :validate))
1183 (goto-char (widget-get val :from))
1184 (error "%s" (widget-get val :error)))
1185 ((eq form 'lisp)
1186 (put symbol 'saved-value (list (widget-value child)))
1187 (set symbol (eval (widget-value child))))
1188 (t
1189 (put symbol
1190 'saved-value (list (custom-quote (widget-value
1191 child))))
1192 (set symbol (widget-value child))))
1193 (put symbol 'customized-value nil)
1194 (custom-save-all)
1195 (custom-variable-state-set widget)
1196 (custom-redraw-magic widget)))
1197
1198(defun custom-variable-reset-saved (widget)
1199 "Restore the saved value for the variable being edited by WIDGET."
1200 (let ((symbol (widget-value widget)))
1201 (if (get symbol 'saved-value)
1202 (condition-case nil
1203 (set symbol (eval (car (get symbol 'saved-value))))
1204 (error nil))
1205 (error "No saved value for %s" symbol))
1206 (put symbol 'customized-value nil)
1207 (widget-put widget :custom-state 'unknown)
1208 (custom-redraw widget)))
1209
1210(defun custom-variable-reset-factory (widget)
1211 "Restore the factory setting for the variable being edited by WIDGET."
1212 (let ((symbol (widget-value widget)))
1213 (if (get symbol 'factory-value)
1214 (set symbol (eval (car (get symbol 'factory-value))))
1215 (error "No factory default for %S" symbol))
1216 (put symbol 'customized-value nil)
1217 (when (get symbol 'saved-value)
1218 (put symbol 'saved-value nil)
1219 (custom-save-all))
1220 (widget-put widget :custom-state 'unknown)
1221 (custom-redraw widget)))
1222
1223;;; The `custom-face-edit' Widget.
1224
1225(define-widget 'custom-face-edit 'checklist
1226 "Edit face attributes."
1227 :format "%t: %v"
1228 :tag "Attributes"
1229 :extra-offset 12
1230 :button-args '(:help-echo "Control whether this attribute have any effect.")
1231 :args (mapcar (lambda (att)
1232 (list 'group
1233 :inline t
1234 :sibling-args (widget-get (nth 1 att) :sibling-args)
1235 (list 'const :format "" :value (nth 0 att))
1236 (nth 1 att)))
1237 custom-face-attributes))
1238
1239;;; The `custom-display' Widget.
1240
1241(define-widget 'custom-display 'menu-choice
1242 "Select a display type."
1243 :tag "Display"
1244 :value t
1245 :help-echo "Specify frames where the face attributes should be used."
1246 :args '((const :tag "all" t)
1247 (checklist
1248 :offset 0
1249 :extra-offset 9
1250 :args ((group :sibling-args (:help-echo "\
1251Only match the specified window systems.")
1252 (const :format "Type: "
1253 type)
1254 (checklist :inline t
1255 :offset 0
1256 (const :format "X "
1257 :sibling-args (:help-echo "\
1258The X11 Window System.")
1259 x)
1260 (const :format "PM "
1261 :sibling-args (:help-echo "\
1262OS/2 Presentation Manager.")
1263 pm)
1264 (const :format "Win32 "
1265 :sibling-args (:help-echo "\
1266Windows NT/95/97.")
1267 win32)
1268 (const :format "DOS "
1269 :sibling-args (:help-echo "\
1270Plain MS-DOS.")
1271 pc)
1272 (const :format "TTY%n"
1273 :sibling-args (:help-echo "\
1274Plain text terminals.")
1275 tty)))
1276 (group :sibling-args (:help-echo "\
1277Only match the frames with the specified color support.")
1278 (const :format "Class: "
1279 class)
1280 (checklist :inline t
1281 :offset 0
1282 (const :format "Color "
1283 :sibling-args (:help-echo "\
1284Match color frames.")
1285 color)
1286 (const :format "Grayscale "
1287 :sibling-args (:help-echo "\
1288Match grayscale frames.")
1289 grayscale)
1290 (const :format "Monochrome%n"
1291 :sibling-args (:help-echo "\
1292Match frames with no color support.")
1293 mono)))
1294 (group :sibling-args (:help-echo "\
1295Only match frames with the specified intensity.")
1296 (const :format "\
1297Background brightness: "
1298 background)
1299 (checklist :inline t
1300 :offset 0
1301 (const :format "Light "
1302 :sibling-args (:help-echo "\
1303Match frames with light backgrounds.")
1304 light)
1305 (const :format "Dark\n"
1306 :sibling-args (:help-echo "\
1307Match frames with dark backgrounds.")
1308 dark)))))))
1309
1310;;; The `custom-face' Widget.
1311
1312(defface custom-face-tag-face '((t (:underline t)))
1313 "Face used for face tags."
1314 :group 'customize)
1315
1316(define-widget 'custom-face 'custom
1317 "Customize face."
1318 :format "%l%{%t%}: %s%m%h%a%v"
1319 :format-handler 'custom-face-format-handler
1320 :sample-face 'custom-face-tag-face
1321 :help-echo "Set or reset this face."
1322 :documentation-property '(lambda (face)
1323 (face-doc-string face))
1324 :value-create 'custom-face-value-create
1325 :action 'custom-face-action
1326 :custom-form 'selected
1327 :custom-set 'custom-face-set
1328 :custom-save 'custom-face-save
1329 :custom-reset-current 'custom-redraw
1330 :custom-reset-saved 'custom-face-reset-saved
1331 :custom-reset-factory 'custom-face-reset-factory
1332 :custom-menu 'custom-face-menu-create)
1333
1334(defun custom-face-format-handler (widget escape)
1335 ;; We recognize extra escape sequences.
1336 (let (child
1337 (symbol (widget-get widget :value)))
1338 (cond ((eq escape ?s)
1339 (and (string-match "XEmacs" emacs-version)
1340 ;; XEmacs cannot display initialized faces.
1341 (not (custom-facep symbol))
1342 (copy-face 'custom-face-empty symbol))
1343 (setq child (widget-create-child-and-convert
1344 widget 'item
1345 :format "(%{%t%})\n"
1346 :sample-face symbol
1347 :tag "sample")))
1348 (t
1349 (custom-format-handler widget escape)))
1350 (when child
1351 (widget-put widget
1352 :buttons (cons child (widget-get widget :buttons))))))
1353
1354(define-widget 'custom-face-all 'editable-list
1355 "An editable list of display specifications and attributes."
1356 :entry-format "%i %d %v"
1357 :insert-button-args '(:help-echo "Insert new display specification here.")
1358 :append-button-args '(:help-echo "Append new display specification here.")
1359 :delete-button-args '(:help-echo "Delete this display specification.")
1360 :args '((group :format "%v" custom-display custom-face-edit)))
1361
1362(defconst custom-face-all (widget-convert 'custom-face-all)
1363 "Converted version of the `custom-face-all' widget.")
1364
1365(define-widget 'custom-display-unselected 'item
1366 "A display specification that doesn't match the selected display."
1367 :match 'custom-display-unselected-match)
1368
1369(defun custom-display-unselected-match (widget value)
1370 "Non-nil if VALUE is an unselected display specification."
1371 (and (listp value)
1372 (eq (length value) 2)
1373 (not (custom-display-match-frame value (selected-frame)))))
1374
1375(define-widget 'custom-face-selected 'group
1376 "Edit the attributes of the selected display in a face specification."
1377 :args '((repeat :format ""
1378 :inline t
1379 (group custom-display-unselected sexp))
1380 (group (sexp :format "") custom-face-edit)
1381 (repeat :format ""
1382 :inline t
1383 sexp)))
1384
1385(defconst custom-face-selected (widget-convert 'custom-face-selected)
1386 "Converted version of the `custom-face-selected' widget.")
1387
1388(defun custom-face-value-create (widget)
1389 ;; Create a list of the display specifications.
1390 (unless (eq (preceding-char) ?\n)
1391 (insert "\n"))
1392 (when (not (eq (widget-get widget :custom-state) 'hidden))
1393 (message "Creating face editor...")
1394 (custom-load-widget widget)
1395 (let* ((symbol (widget-value widget))
1396 (spec (or (get symbol 'saved-face)
1397 (get symbol 'factory-face)
1398 ;; Attempt to construct it.
1399 (list (list t (custom-face-attributes-get
1400 symbol (selected-frame))))))
1401 (form (widget-get widget :custom-form))
1402 (indent (widget-get widget :indent))
1403 (edit (widget-create-child-and-convert
1404 widget
1405 (cond ((and (eq form 'selected)
1406 (widget-apply custom-face-selected :match spec))
1407 (when indent (insert-char ?\ indent))
1408 'custom-face-selected)
1409 ((and (not (eq form 'lisp))
1410 (widget-apply custom-face-all :match spec))
1411 'custom-face-all)
1412 (t
1413 (when indent (insert-char ?\ indent))
1414 'sexp))
1415 :value spec)))
1416 (custom-face-state-set widget)
1417 (widget-put widget :children (list edit)))
1418 (message "Creating face editor...done")))
1419
1420(defvar custom-face-menu
1421 '(("Edit Selected" . custom-face-edit-selected)
1422 ("Edit All" . custom-face-edit-all)
1423 ("Edit Lisp" . custom-face-edit-lisp)
1424 ("Set" . custom-face-set)
1425 ("Save" . custom-face-save)
1426 ("Reset to Saved" . custom-face-reset-saved)
1427 ("Reset to Factory Setting" . custom-face-reset-factory))
1428 "Alist of actions for the `custom-face' widget.
1429The key is a string containing the name of the action, the value is a
1430lisp function taking the widget as an element which will be called
1431when the action is chosen.")
1432
1433(defun custom-face-edit-selected (widget)
1434 "Edit selected attributes of the value of WIDGET."
1435 (widget-put widget :custom-state 'unknown)
1436 (widget-put widget :custom-form 'selected)
1437 (custom-redraw widget))
1438
1439(defun custom-face-edit-all (widget)
1440 "Edit all attributes of the value of WIDGET."
1441 (widget-put widget :custom-state 'unknown)
1442 (widget-put widget :custom-form 'all)
1443 (custom-redraw widget))
1444
1445(defun custom-face-edit-lisp (widget)
1446 "Edit the lisp representation of the value of WIDGET."
1447 (widget-put widget :custom-state 'unknown)
1448 (widget-put widget :custom-form 'lisp)
1449 (custom-redraw widget))
1450
1451(defun custom-face-state-set (widget)
1452 "Set the state of WIDGET."
1453 (let ((symbol (widget-value widget)))
1454 (widget-put widget :custom-state (cond ((get symbol 'customized-face)
1455 'set)
1456 ((get symbol 'saved-face)
1457 'saved)
1458 ((get symbol 'factory-face)
1459 'factory)
1460 (t
1461 'rogue)))))
1462
1463(defun custom-face-action (widget &optional event)
1464 "Show the menu for `custom-face' WIDGET.
1465Optional EVENT is the location for the menu."
1466 (if (eq (widget-get widget :custom-state) 'hidden)
1467 (progn
1468 (widget-put widget :custom-state 'unknown)
1469 (custom-redraw widget))
1470 (let* ((completion-ignore-case t)
1471 (symbol (widget-get widget :value))
1472 (answer (widget-choose (custom-unlispify-tag-name symbol)
1473 custom-face-menu event)))
1474 (if answer
1475 (funcall answer widget)))))
1476
1477(defun custom-face-set (widget)
1478 "Make the face attributes in WIDGET take effect."
1479 (let* ((symbol (widget-value widget))
1480 (child (car (widget-get widget :children)))
1481 (value (widget-value child)))
1482 (put symbol 'customized-face value)
1483 (when (fboundp 'copy-face)
1484 (copy-face 'custom-face-empty symbol))
1485 (custom-face-display-set symbol value)
1486 (custom-face-state-set widget)
1487 (custom-redraw-magic widget)))
1488
1489(defun custom-face-save (widget)
1490 "Make the face attributes in WIDGET default."
1491 (let* ((symbol (widget-value widget))
1492 (child (car (widget-get widget :children)))
1493 (value (widget-value child)))
1494 (when (fboundp 'copy-face)
1495 (copy-face 'custom-face-empty symbol))
1496 (custom-face-display-set symbol value)
1497 (put symbol 'saved-face value)
1498 (put symbol 'customized-face nil)
1499 (custom-face-state-set widget)
1500 (custom-redraw-magic widget)))
1501
1502(defun custom-face-reset-saved (widget)
1503 "Restore WIDGET to the face's default attributes."
1504 (let* ((symbol (widget-value widget))
1505 (child (car (widget-get widget :children)))
1506 (value (get symbol 'saved-face)))
1507 (unless value
1508 (error "No saved value for this face"))
1509 (put symbol 'customized-face nil)
1510 (when (fboundp 'copy-face)
1511 (copy-face 'custom-face-empty symbol))
1512 (custom-face-display-set symbol value)
1513 (widget-value-set child value)
1514 (custom-face-state-set widget)
1515 (custom-redraw-magic widget)))
1516
1517(defun custom-face-reset-factory (widget)
1518 "Restore WIDGET to the face's factory settings."
1519 (let* ((symbol (widget-value widget))
1520 (child (car (widget-get widget :children)))
1521 (value (get symbol 'factory-face)))
1522 (unless value
1523 (error "No factory default for this face"))
1524 (put symbol 'customized-face nil)
1525 (when (get symbol 'saved-face)
1526 (put symbol 'saved-face nil)
1527 (custom-save-all))
1528 (when (fboundp 'copy-face)
1529 (copy-face 'custom-face-empty symbol))
1530 (custom-face-display-set symbol value)
1531 (widget-value-set child value)
1532 (custom-face-state-set widget)
1533 (custom-redraw-magic widget)))
1534
1535;;; The `face' Widget.
1536
1537(define-widget 'face 'default
1538 "Select and customize a face."
1539 :convert-widget 'widget-item-convert-widget
1540 :format "%[%t%]: %v"
1541 :tag "Face"
1542 :value 'default
1543 :value-create 'widget-face-value-create
1544 :value-delete 'widget-face-value-delete
1545 :value-get 'widget-item-value-get
1546 :validate 'widget-editable-list-validate
1547 :action 'widget-face-action
1548 :match '(lambda (widget value) (symbolp value)))
1549
1550(defun widget-face-value-create (widget)
1551 ;; Create a `custom-face' child.
1552 (let* ((symbol (widget-value widget))
1553 (child (widget-create-child-and-convert
1554 widget 'custom-face
1555 :format "%t %s%m%h%v"
1556 :custom-level nil
1557 :value symbol)))
1558 (custom-magic-reset child)
1559 (setq custom-options (cons child custom-options))
1560 (widget-put widget :children (list child))))
1561
1562(defun widget-face-value-delete (widget)
1563 ;; Remove the child from the options.
1564 (let ((child (car (widget-get widget :children))))
1565 (setq custom-options (delq child custom-options))
1566 (widget-children-value-delete widget)))
1567
1568(defvar face-history nil
1569 "History of entered face names.")
1570
1571(defun widget-face-action (widget &optional event)
1572 "Prompt for a face."
1573 (let ((answer (completing-read "Face: "
1574 (mapcar (lambda (face)
1575 (list (symbol-name face)))
1576 (face-list))
1577 nil nil nil
1578 'face-history)))
1579 (unless (zerop (length answer))
1580 (widget-value-set widget (intern answer))
1581 (widget-apply widget :notify widget event)
1582 (widget-setup))))
1583
1584;;; The `hook' Widget.
1585
1586(define-widget 'hook 'list
1587 "A emacs lisp hook"
1588 :convert-widget 'custom-hook-convert-widget
1589 :tag "Hook")
1590
1591(defun custom-hook-convert-widget (widget)
1592 ;; Handle `:custom-options'.
1593 (let* ((options (widget-get widget :options))
1594 (other `(editable-list :inline t
1595 :entry-format "%i %d%v"
1596 (function :format " %v")))
1597 (args (if options
1598 (list `(checklist :inline t
1599 ,@(mapcar (lambda (entry)
1600 `(function-item ,entry))
1601 options))
1602 other)
1603 (list other))))
1604 (widget-put widget :args args)
1605 widget))
1606
1607;;; The `custom-group' Widget.
1608
1609(defcustom custom-group-tag-faces '(custom-group-tag-face-1)
1610 ;; In XEmacs, this ought to play games with font size.
1611 "Face used for group tags.
1612The first member is used for level 1 groups, the second for level 2,
1613and so forth. The remaining group tags are shown with
1614`custom-group-tag-face'."
1615 :type '(repeat face)
1616 :group 'customize)
1617
1618(defface custom-group-tag-face-1 '((((class color)
1619 (background dark))
1620 (:foreground "pink" :underline t))
1621 (((class color)
1622 (background light))
1623 (:foreground "red" :underline t))
1624 (t (:underline t)))
1625 "Face used for group tags.")
1626
1627(defface custom-group-tag-face '((((class color)
1628 (background dark))
1629 (:foreground "light blue" :underline t))
1630 (((class color)
1631 (background light))
1632 (:foreground "blue" :underline t))
1633 (t (:underline t)))
1634 "Face used for low level group tags."
1635 :group 'customize)
1636
1637(define-widget 'custom-group 'custom
1638 "Customize group."
1639 :format "%l%{%t%}:%L\n%m%h%a%v"
1640 :sample-face-get 'custom-group-sample-face-get
1641 :documentation-property 'group-documentation
1642 :help-echo "Set or reset all members of this group."
1643 :value-create 'custom-group-value-create
1644 :action 'custom-group-action
1645 :custom-set 'custom-group-set
1646 :custom-save 'custom-group-save
1647 :custom-reset-current 'custom-group-reset-current
1648 :custom-reset-saved 'custom-group-reset-saved
1649 :custom-reset-factory 'custom-group-reset-factory
1650 :custom-menu 'custom-group-menu-create)
1651
1652(defun custom-group-sample-face-get (widget)
1653 ;; Use :sample-face.
1654 (or (nth (1- (widget-get widget :custom-level)) custom-group-tag-faces)
1655 'custom-group-tag-face))
1656
1657(defun custom-group-value-create (widget)
1658 (let ((state (widget-get widget :custom-state)))
1659 (unless (eq state 'hidden)
1660 (message "Creating group...")
1661 (custom-load-widget widget)
1662 (let* ((level (widget-get widget :custom-level))
1663 (symbol (widget-value widget))
1664 (members (get symbol 'custom-group))
1665 (prefixes (widget-get widget :custom-prefixes))
1666 (custom-prefix-list (custom-prefix-add symbol prefixes))
1667 (length (length members))
1668 (count 0)
1669 (children (mapcar (lambda (entry)
1670 (widget-insert "\n")
1671 (message "Creating group members... %2d%%"
1672 (/ (* 100.0 count) length))
1673 (setq count (1+ count))
1674 (prog1
1675 (widget-create-child-and-convert
1676 widget (nth 1 entry)
1677 :group widget
1678 :tag (custom-unlispify-tag-name
1679 (nth 0 entry))
1680 :custom-prefixes custom-prefix-list
1681 :custom-level (1+ level)
1682 :value (nth 0 entry))
1683 (unless (eq (preceding-char) ?\n)
1684 (widget-insert "\n"))))
1685 members)))
1686 (message "Creating group magic...")
1687 (mapcar 'custom-magic-reset children)
1688 (message "Creating group state...")
1689 (widget-put widget :children children)
1690 (custom-group-state-update widget)
1691 (message "Creating group... done")))))
1692
1693(defvar custom-group-menu
1694 '(("Set" . custom-group-set)
1695 ("Save" . custom-group-save)
1696 ("Reset to Current" . custom-group-reset-current)
1697 ("Reset to Saved" . custom-group-reset-saved)
1698 ("Reset to Factory" . custom-group-reset-factory))
1699 "Alist of actions for the `custom-group' widget.
1700The key is a string containing the name of the action, the value is a
1701lisp function taking the widget as an element which will be called
1702when the action is chosen.")
1703
1704(defun custom-group-action (widget &optional event)
1705 "Show the menu for `custom-group' WIDGET.
1706Optional EVENT is the location for the menu."
1707 (if (eq (widget-get widget :custom-state) 'hidden)
1708 (progn
1709 (widget-put widget :custom-state 'unknown)
1710 (custom-redraw widget))
1711 (let* ((completion-ignore-case t)
1712 (answer (widget-choose (custom-unlispify-tag-name
1713 (widget-get widget :value))
1714 custom-group-menu
1715 event)))
1716 (if answer
1717 (funcall answer widget)))))
1718
1719(defun custom-group-set (widget)
1720 "Set changes in all modified group members."
1721 (let ((children (widget-get widget :children)))
1722 (mapcar (lambda (child)
1723 (when (eq (widget-get child :custom-state) 'modified)
1724 (widget-apply child :custom-set)))
1725 children )))
1726
1727(defun custom-group-save (widget)
1728 "Save all modified group members."
1729 (let ((children (widget-get widget :children)))
1730 (mapcar (lambda (child)
1731 (when (memq (widget-get child :custom-state) '(modified set))
1732 (widget-apply child :custom-save)))
1733 children )))
1734
1735(defun custom-group-reset-current (widget)
1736 "Reset all modified group members."
1737 (let ((children (widget-get widget :children)))
1738 (mapcar (lambda (child)
1739 (when (eq (widget-get child :custom-state) 'modified)
1740 (widget-apply child :custom-reset-current)))
1741 children )))
1742
1743(defun custom-group-reset-saved (widget)
1744 "Reset all modified or set group members."
1745 (let ((children (widget-get widget :children)))
1746 (mapcar (lambda (child)
1747 (when (memq (widget-get child :custom-state) '(modified set))
1748 (widget-apply child :custom-reset-saved)))
1749 children )))
1750
1751(defun custom-group-reset-factory (widget)
1752 "Reset all modified, set, or saved group members."
1753 (let ((children (widget-get widget :children)))
1754 (mapcar (lambda (child)
1755 (when (memq (widget-get child :custom-state)
1756 '(modified set saved))
1757 (widget-apply child :custom-reset-factory)))
1758 children )))
1759
1760(defun custom-group-state-update (widget)
1761 "Update magic."
1762 (unless (eq (widget-get widget :custom-state) 'hidden)
1763 (let* ((children (widget-get widget :children))
1764 (states (mapcar (lambda (child)
1765 (widget-get child :custom-state))
1766 children))
1767 (magics custom-magic-alist)
1768 (found 'factory))
1769 (while magics
1770 (let ((magic (car (car magics))))
1771 (if (and (not (eq magic 'hidden))
1772 (memq magic states))
1773 (setq found magic
1774 magics nil)
1775 (setq magics (cdr magics)))))
1776 (widget-put widget :custom-state found)))
1777 (custom-magic-reset widget))
1778
1779;;; The `custom-save-all' Function.
1780
1781(defcustom custom-file "~/.emacs"
1782 "File used for storing customization information.
1783If you change this from the default \"~/.emacs\" you need to
1784explicitly load that file for the settings to take effect."
1785 :type 'file
1786 :group 'customize)
1787
1788(defun custom-save-delete (symbol)
1789 "Delete the call to SYMBOL form `custom-file'.
1790Leave point at the location of the call, or after the last expression."
1791 (set-buffer (find-file-noselect custom-file))
1792 (goto-char (point-min))
1793 (catch 'found
1794 (while t
1795 (let ((sexp (condition-case nil
1796 (read (current-buffer))
1797 (end-of-file (throw 'found nil)))))
1798 (when (and (listp sexp)
1799 (eq (car sexp) symbol))
1800 (delete-region (save-excursion
1801 (backward-sexp)
1802 (point))
1803 (point))
1804 (throw 'found nil))))))
1805
1806(defun custom-save-variables ()
1807 "Save all customized variables in `custom-file'."
1808 (save-excursion
1809 (custom-save-delete 'custom-set-variables)
1810 (let ((standard-output (current-buffer)))
1811 (unless (bolp)
1812 (princ "\n"))
1813 (princ "(custom-set-variables")
1814 (mapatoms (lambda (symbol)
1815 (let ((value (get symbol 'saved-value)))
1816 (when value
1817 (princ "\n '(")
1818 (princ symbol)
1819 (princ " ")
1820 (prin1 (car value))
1821 (if (or (get symbol 'factory-value)
1822 (and (not (boundp symbol))
1823 (not (get symbol 'force-value))))
1824 (princ ")")
1825 (princ " t)"))))))
1826 (princ ")")
1827 (unless (looking-at "\n")
1828 (princ "\n")))))
1829
1830(defun custom-save-faces ()
1831 "Save all customized faces in `custom-file'."
1832 (save-excursion
1833 (custom-save-delete 'custom-set-faces)
1834 (let ((standard-output (current-buffer)))
1835 (unless (bolp)
1836 (princ "\n"))
1837 (princ "(custom-set-faces")
1838 (mapatoms (lambda (symbol)
1839 (let ((value (get symbol 'saved-face)))
1840 (when value
1841 (princ "\n '(")
1842 (princ symbol)
1843 (princ " ")
1844 (prin1 value)
1845 (if (or (get symbol 'factory-face)
1846 (and (not (custom-facep symbol))
1847 (not (get symbol 'force-face))))
1848 (princ ")")
1849 (princ " t)"))))))
1850 (princ ")")
1851 (unless (looking-at "\n")
1852 (princ "\n")))))
1853
1854;;;###autoload
1855(defun custom-save-all ()
1856 "Save all customizations in `custom-file'."
1857 (custom-save-variables)
1858 (custom-save-faces)
1859 (save-excursion
1860 (set-buffer (find-file-noselect custom-file))
1861 (save-buffer)))
1862
1863;;; The Customize Menu.
1864
1865(defcustom custom-menu-nesting 2
1866 "Maximum nesting in custom menus."
1867 :type 'integer
1868 :group 'customize)
1869
1870(defun custom-face-menu-create (widget symbol)
1871 "Ignoring WIDGET, create a menu entry for customization face SYMBOL."
1872 (vector (custom-unlispify-menu-entry symbol)
1873 `(custom-buffer-create '((,symbol custom-face)))
1874 t))
1875
1876(defun custom-variable-menu-create (widget symbol)
1877 "Ignoring WIDGET, create a menu entry for customization variable SYMBOL."
1878 (let ((type (get symbol 'custom-type)))
1879 (unless (listp type)
1880 (setq type (list type)))
1881 (if (and type (widget-get type :custom-menu))
1882 (widget-apply type :custom-menu symbol)
1883 (vector (custom-unlispify-menu-entry symbol)
1884 `(custom-buffer-create '((,symbol custom-variable)))
1885 t))))
1886
1887(widget-put (get 'boolean 'widget-type)
1888 :custom-menu (lambda (widget symbol)
1889 (vector (custom-unlispify-menu-entry symbol)
1890 `(custom-buffer-create
1891 '((,symbol custom-variable)))
1892 ':style 'toggle
1893 ':selected symbol)))
1894
1895(if (string-match "XEmacs" emacs-version)
1896 ;; XEmacs can create menus dynamically.
1897 (defun custom-group-menu-create (widget symbol)
1898 "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
1899 `( ,(custom-unlispify-menu-entry symbol t)
1900 :filter (lambda (&rest junk)
1901 (cdr (custom-menu-create ',symbol)))))
1902 ;; But emacs can't.
1903 (defun custom-group-menu-create (widget symbol)
1904 "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
1905 ;; Limit the nesting.
1906 (let ((custom-menu-nesting (1- custom-menu-nesting)))
1907 (custom-menu-create symbol))))
1908
1909(defun custom-menu-create (symbol &optional name)
1910 "Create menu for customization group SYMBOL.
1911If optional NAME is given, use that as the name of the menu.
1912Otherwise make up a name from SYMBOL.
1913The menu is in a format applicable to `easy-menu-define'."
1914 (unless name
1915 (setq name (custom-unlispify-menu-entry symbol)))
1916 (let ((item (vector name
1917 `(custom-buffer-create '((,symbol custom-group)))
1918 t)))
1919 (if (and (>= custom-menu-nesting 0)
1920 (< (length (get symbol 'custom-group)) widget-menu-max-size))
1921 (let ((custom-prefix-list (custom-prefix-add symbol
1922 custom-prefix-list)))
1923 (custom-load-symbol symbol)
1924 `(,(custom-unlispify-menu-entry symbol t)
1925 ,item
1926 "--"
1927 ,@(mapcar (lambda (entry)
1928 (widget-apply (if (listp (nth 1 entry))
1929 (nth 1 entry)
1930 (list (nth 1 entry)))
1931 :custom-menu (nth 0 entry)))
1932 (get symbol 'custom-group))))
1933 item)))
1934
1935;;;###autoload
1936(defun custom-menu-update (event)
1937 "Update customize menu."
1938 (interactive "e")
1939 (add-hook 'custom-define-hook 'custom-menu-reset)
1940 (let* ((emacs (widget-apply '(custom-group) :custom-menu 'emacs))
1941 (menu `(,(car custom-help-menu)
1942 ,emacs
1943 ,@(cdr (cdr custom-help-menu)))))
1944 (let ((map (easy-menu-create-keymaps (car menu) (cdr menu))))
1945 (define-key global-map [menu-bar help-menu customize-menu]
1946 (cons (car menu) map)))))
1947
1948;;; Dependencies.
1949
1950;;;###autoload
1951(defun custom-make-dependencies ()
1952 "Batch function to extract custom dependencies from .el files.
1953Usage: emacs -batch *.el -f custom-make-dependencies > deps.el"
1954 (let ((buffers (buffer-list)))
1955 (while buffers
1956 (set-buffer (car buffers))
1957 (setq buffers (cdr buffers))
1958 (let ((file (buffer-file-name)))
1959 (when (and file (string-match "\\`\\(.*\\)\\.el\\'" file))
1960 (goto-char (point-min))
1961 (condition-case nil
1962 (let ((name (file-name-nondirectory (match-string 1 file))))
1963 (while t
1964 (let ((expr (read (current-buffer))))
1965 (when (and (listp expr)
1966 (memq (car expr) '(defcustom defface defgroup)))
1967 (eval expr)
1968 (put (nth 1 expr) 'custom-where name)))))
1969 (error nil))))))
1970 (mapatoms (lambda (symbol)
1971 (let ((members (get symbol 'custom-group))
1972 item where found)
1973 (when members
1974 (princ "(put '")
1975 (princ symbol)
1976 (princ " 'custom-loads '(")
1977 (while members
1978 (setq item (car (car members))
1979 members (cdr members)
1980 where (get item 'custom-where))
1981 (unless (or (null where)
1982 (member where found))
1983 (when found
1984 (princ " "))
1985 (prin1 where)
1986 (push where found)))
1987 (princ "))\n"))))))
1988
1989;;; The End.
1990
1991(provide 'cus-edit)
1992
1993;; cus-edit.el ends here
diff --git a/lisp/cus-face.el b/lisp/cus-face.el
new file mode 100644
index 00000000000..ae8e60b499f
--- /dev/null
+++ b/lisp/cus-face.el
@@ -0,0 +1,590 @@
1;;; cus-face.el -- XEmacs specific custom support.
2;;
3;; Copyright (C) 1996, 1997 Free Software Foundation, Inc.
4;;
5;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6;; Keywords: help, faces
7;; Version: 1.71
8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
9
10;;; Commentary:
11;;
12;; See `custom.el'.
13
14;;; Code:
15
16(require 'custom)
17
18(eval-and-compile (require 'cl))
19
20;;; Compatibility.
21
22(if (string-match "XEmacs" emacs-version)
23 (defun custom-face-background (face &optional frame)
24 ;; Specifiers suck!
25 "Return the background color name of face FACE, or nil if unspecified."
26 (color-instance-name (specifier-instance (face-background face) frame)))
27 (defalias 'custom-face-background 'face-background))
28
29(if (string-match "XEmacs" emacs-version)
30 (defun custom-face-foreground (face &optional frame)
31 ;; Specifiers suck!
32 "Return the background color name of face FACE, or nil if unspecified."
33 (color-instance-name (specifier-instance (face-foreground face) frame)))
34 (defalias 'custom-face-foreground 'face-foreground))
35
36(defalias 'custom-face-font-name (if (string-match "XEmacs" emacs-version)
37 'face-font-name
38 'face-font))
39
40(eval-and-compile
41 (unless (fboundp 'frame-property)
42 ;; XEmacs function missing in Emacs 19.34.
43 (defun frame-property (frame property &optional default)
44 "Return FRAME's value for property PROPERTY."
45 (or (cdr (assq property (frame-parameters frame)))
46 default)))
47
48 (unless (fboundp 'face-doc-string)
49 ;; XEmacs function missing in Emacs.
50 (defun face-doc-string (face)
51 "Get the documentation string for FACE."
52 (get face 'face-doc-string)))
53
54 (unless (fboundp 'set-face-doc-string)
55 ;; XEmacs function missing in Emacs.
56 (defun set-face-doc-string (face string)
57 "Set the documentation string for FACE to STRING."
58 (put face 'face-doc-string string)))
59
60 (when (and (not (fboundp 'set-face-stipple))
61 (fboundp 'set-face-background-pixmap))
62 ;; Emacs function missing in XEmacs 19.15.
63 (defun set-face-stipple (face pixmap &optional frame)
64 ;; Written by Kyle Jones.
65 "Change the stipple pixmap of face FACE to PIXMAP.
66PIXMAP should be a string, the name of a file of pixmap data.
67The directories listed in the `x-bitmap-file-path' variable are searched.
68
69Alternatively, PIXMAP may be a list of the form (WIDTH HEIGHT DATA)
70where WIDTH and HEIGHT are the size in pixels,
71and DATA is a string, containing the raw bits of the bitmap.
72
73If the optional FRAME argument is provided, change only
74in that frame; otherwise change each frame."
75 (while (not (find-face face))
76 (setq face (signal 'wrong-type-argument (list 'facep face))))
77 (while (cond ((stringp pixmap)
78 (unless (file-readable-p pixmap)
79 (setq pixmap (vector 'xbm ':file pixmap)))
80 nil)
81 ((and (consp pixmap) (= (length pixmap) 3))
82 (setq pixmap (vector 'xbm ':data pixmap))
83 nil)
84 (t t))
85 (setq pixmap (signal 'wrong-type-argument
86 (list 'stipple-pixmap-p pixmap))))
87 (while (and frame (not (framep frame)))
88 (setq frame (signal 'wrong-type-argument (list 'framep frame))))
89 (set-face-background-pixmap face pixmap frame))))
90
91(unless (fboundp 'x-color-values)
92 ;; Emacs function missing in XEmacs 19.14.
93 (defun x-color-values (color &optional frame)
94 "Return a description of the color named COLOR on frame FRAME.
95The value is a list of integer RGB values--(RED GREEN BLUE).
96These values appear to range from 0 to 65280 or 65535, depending
97on the system; white is (65280 65280 65280) or (65535 65535 65535).
98If FRAME is omitted or nil, use the selected frame."
99 (color-instance-rgb-components (make-color-instance color))))
100
101;; XEmacs and Emacs have different definitions of `facep'.
102;; The Emacs definition is the useful one, so emulate that.
103(cond ((not (fboundp 'facep))
104 (defun custom-facep (face)
105 "No faces"
106 nil))
107 ((string-match "XEmacs" emacs-version)
108 (defalias 'custom-facep 'find-face))
109 (t
110 (defalias 'custom-facep 'facep)))
111
112(unless (fboundp 'make-empty-face)
113 ;; This should be moved to `faces.el'.
114 (if (string-match "XEmacs" emacs-version)
115 ;; Give up for old XEmacs pre 19.15/20.1.
116 (defalias 'make-empty-face 'make-face)
117 ;; Define for Emacs pre 19.35.
118 (defun make-empty-face (name)
119 "Define a new FACE on all frames, ignoring X resources."
120 (interactive "SMake face: ")
121 (or (internal-find-face name)
122 (let ((face (make-vector 8 nil)))
123 (aset face 0 'face)
124 (aset face 1 name)
125 (let* ((frames (frame-list))
126 (inhibit-quit t)
127 (id (internal-next-face-id)))
128 (make-face-internal id)
129 (aset face 2 id)
130 (while frames
131 (set-frame-face-alist (car frames)
132 (cons (cons name (copy-sequence face))
133 (frame-face-alist (car frames))))
134 (setq frames (cdr frames)))
135 (setq global-face-data (cons (cons name face) global-face-data)))
136 ;; add to menu
137 (if (fboundp 'facemenu-add-new-face)
138 (facemenu-add-new-face name))
139 face))
140 name)))
141
142(defcustom initialize-face-resources t
143 "If non nil, allow X resources to initialize face properties.
144This only affects faces declared with `defface', and only NT or X11 frames."
145 :group 'customize
146 :type 'boolean)
147
148(cond ((fboundp 'initialize-face-resources)
149 ;; Already bound, do nothing.
150 )
151 ((fboundp 'make-face-x-resource-internal)
152 ;; Emacs or new XEmacs.
153 (defun initialize-face-resources (face &optional frame)
154 "Initialize face according to the X11 resources.
155This might overwrite existing face properties.
156Does nothing when the variable initialize-face-resources is nil."
157 (when initialize-face-resources
158 (make-face-x-resource-internal face frame t))))
159 (t
160 ;; Too hard to do right on XEmacs.
161 (defalias 'initialize-face-resources 'ignore)))
162
163;;(if (string-match "XEmacs" emacs-version)
164;; ;; Xemacs.
165;; (defun custom-invert-face (face &optional frame)
166;; "Swap the foreground and background colors of face FACE.
167;;If the colors are not specified in the face, use the default colors."
168;; (interactive (list (read-face-name "Reverse face: ")))
169;; (let ((fg (color-name (face-foreground face frame) frame))
170;; (bg (color-name (face-background face frame) frame)))
171;; (set-face-foreground face bg frame)
172;; (set-face-background face fg frame)))
173;; ;; Emacs.
174;; (defun custom-invert-face (face &optional frame)
175;; "Swap the foreground and background colors of face FACE.
176;;If the colors are not specified in the face, use the default colors."
177;; (interactive (list (read-face-name "Reverse face: ")))
178;; (let ((fg (or (face-foreground face frame)
179;; (face-foreground 'default frame)
180;; (frame-property (or frame (selected-frame))
181;; 'foreground-color)
182;; "black"))
183;; (bg (or (face-background face frame)
184;; (face-background 'default frame)
185;; (frame-property (or frame (selected-frame))
186;; 'background-color)
187;; "white")))
188;; (set-face-foreground face bg frame)
189;; (set-face-background face fg frame))))
190
191(defcustom custom-background-mode nil
192 "The brightness of the background.
193Set this to the symbol dark if your background color is dark, light if
194your background is light, or nil (default) if you want Emacs to
195examine the brightness for you."
196 :group 'customize
197 :type '(choice (choice-item dark)
198 (choice-item light)
199 (choice-item :tag "default" nil)))
200
201(defun custom-background-mode (frame)
202 "Kludge to detect background mode for FRAME."
203 (let* ((bg-resource
204 (condition-case ()
205 (x-get-resource ".backgroundMode" "BackgroundMode" 'string)
206 (error nil)))
207 color
208 (mode (cond (bg-resource
209 (intern (downcase bg-resource)))
210 ((and (setq color (condition-case ()
211 (or (frame-property
212 frame
213 'background-color)
214 (custom-face-background
215 'default))
216 (error nil)))
217 (or (string-match "XEmacs" emacs-version)
218 window-system)
219 (< (apply '+ (x-color-values color))
220 (/ (apply '+ (x-color-values "white"))
221 3)))
222 'dark)
223 (t 'light))))
224 (modify-frame-parameters frame (list (cons 'background-mode mode)))
225 mode))
226
227(eval-and-compile
228 (if (string-match "XEmacs" emacs-version)
229 ;; XEmacs.
230 (defun custom-extract-frame-properties (frame)
231 "Return a plist with the frame properties of FRAME used by custom."
232 (list 'type (device-type (frame-device frame))
233 'class (device-class (frame-device frame))
234 'background (or custom-background-mode
235 (frame-property frame
236 'background-mode)
237 (custom-background-mode frame))))
238 ;; Emacs.
239 (defun custom-extract-frame-properties (frame)
240 "Return a plist with the frame properties of FRAME used by custom."
241 (list 'type window-system
242 'class (frame-property frame 'display-type)
243 'background (or custom-background-mode
244 (frame-property frame 'background-mode)
245 (custom-background-mode frame))))))
246
247;;; Declaring a face.
248
249;;;###autoload
250(defun custom-declare-face (face spec doc &rest args)
251 "Like `defface', but FACE is evaluated as a normal argument."
252 (when (fboundp 'load-gc)
253 ;; This should be allowed, somehow.
254 (error "Attempt to declare a face during dump"))
255 (unless (get face 'factory-face)
256 (put face 'factory-face spec)
257 (when (fboundp 'facep)
258 (unless (custom-facep face)
259 ;; If the user has already created the face, respect that.
260 (let ((value (or (get face 'saved-face) spec))
261 (frames (custom-relevant-frames))
262 frame)
263 ;; Create global face.
264 (make-empty-face face)
265 (custom-face-display-set face value)
266 ;; Create frame local faces
267 (while frames
268 (setq frame (car frames)
269 frames (cdr frames))
270 (custom-face-display-set face value frame))
271 (initialize-face-resources face))))
272 (when (and doc (null (face-doc-string face)))
273 (set-face-doc-string face doc))
274 (custom-handle-all-keywords face args 'custom-face)
275 (run-hooks 'custom-define-hook))
276 face)
277
278;;; Font Attributes.
279
280(defconst custom-face-attributes
281 '((:bold (toggle :format "Bold: %[%v%]\n"
282 :help-echo "Control whether a bold font should be used.")
283 custom-set-face-bold
284 custom-face-bold)
285 (:italic (toggle :format "Italic: %[%v%]\n"
286 :help-echo "\
287Control whether an italic font should be used.")
288 custom-set-face-italic
289 custom-face-italic)
290 (:underline (toggle :format "Underline: %[%v%]\n"
291 :help-echo "\
292Control whether the text should be underlined.")
293 set-face-underline-p
294 face-underline-p)
295 (:foreground (color :tag "Foreground"
296 :value "black"
297 :help-echo "Set foreground color.")
298 set-face-foreground
299 custom-face-foreground)
300 (:background (color :tag "Background"
301 :value "white"
302 :help-echo "Set background color.")
303 set-face-background
304 custom-face-background)
305 ;; (:invert (const :format "Invert Face\n"
306 ;; :sibling-args (:help-echo "
307 ;;Reverse the foreground and background color.
308 ;;If you haven't specified them for the face, the default colors will be used.")
309 ;; t)
310 ;; (lambda (face value &optional frame)
311 ;; ;; We don't use VALUE.
312 ;; (custom-invert-face face frame)))
313 (:stipple (editable-field :format "Stipple: %v"
314 :help-echo "Name of background bitmap file.")
315 set-face-stipple custom-face-stipple))
316 "Alist of face attributes.
317
318The elements are of the form (KEY TYPE SET GET) where KEY is a symbol
319identifying the attribute, TYPE is a widget type for editing the
320attibute, SET is a function for setting the attribute value, and GET is a function for getiing the attribute value.
321
322The SET function should take three arguments, the face to modify, the
323value of the attribute, and optionally the frame where the face should
324be changed.
325
326The GET function should take two arguments, the face to examine, and
327optonally the frame where the face should be examined.")
328
329(defun custom-face-attributes-set (face frame &rest atts)
330 "For FACE on FRAME set the attributes [KEYWORD VALUE]....
331Each keyword should be listed in `custom-face-attributes'.
332
333If FRAME is nil, set the default face."
334 (while atts
335 (let* ((name (nth 0 atts))
336 (value (nth 1 atts))
337 (fun (nth 2 (assq name custom-face-attributes))))
338 (setq atts (cdr (cdr atts)))
339 (condition-case nil
340 (funcall fun face value frame)
341 (error nil)))))
342
343(defun custom-face-attributes-get (face frame)
344 "For FACE on FRAME get the attributes [KEYWORD VALUE]....
345Each keyword should be listed in `custom-face-attributes'.
346
347If FRAME is nil, use the default face."
348 (condition-case nil
349 ;; Attempt to get `font.el' from w3.
350 (require 'font)
351 (error nil))
352 (let ((atts custom-face-attributes)
353 att result get)
354 (while atts
355 (setq att (car atts)
356 atts (cdr atts)
357 get (nth 3 att))
358 (when get
359 (let ((answer (funcall get face frame)))
360 (unless (equal answer (funcall get 'default frame))
361 (when (widget-apply (nth 1 att) :match answer)
362 (setq result (cons (nth 0 att) (cons answer result))))))))
363 result))
364
365(defun custom-set-face-bold (face value &optional frame)
366 "Set the bold property of FACE to VALUE."
367 (if value
368 (make-face-bold face frame)
369 (make-face-unbold face frame)))
370
371(defun custom-face-bold (face &rest args)
372 "Return non-nil if the font of FACE is bold."
373 (let* ((font (apply 'custom-face-font-name face args))
374 (fontobj (font-create-object font)))
375 (font-bold-p fontobj)))
376
377(defun custom-set-face-italic (face value &optional frame)
378 "Set the italic property of FACE to VALUE."
379 (if value
380 (make-face-italic face frame)
381 (make-face-unitalic face frame)))
382
383(defun custom-face-italic (face &rest args)
384 "Return non-nil if the font of FACE is italic."
385 (let* ((font (apply 'custom-face-font-name face args))
386 (fontobj (font-create-object font)))
387 (font-italic-p fontobj)))
388
389(defun custom-face-stipple (face &rest args)
390 "Return the name of the stipple file used for FACE."
391 (if (string-match "XEmacs" emacs-version)
392 (let ((image (apply 'specifier-instance
393 (face-background-pixmap face) args)))
394 (when image
395 (image-instance-file-name image)))
396 (apply 'face-stipple face args)))
397
398(when (string-match "XEmacs" emacs-version)
399 ;; Support for special XEmacs font attributes.
400 (autoload 'font-create-object "font" nil)
401
402 (defun custom-set-face-font-size (face size &rest args)
403 "Set the font of FACE to SIZE"
404 (let* ((font (apply 'custom-face-font-name face args))
405 (fontobj (font-create-object font)))
406 (set-font-size fontobj size)
407 (apply 'font-set-face-font face fontobj args)))
408
409 (defun custom-face-font-size (face &rest args)
410 "Return the size of the font of FACE as a string."
411 (let* ((font (apply 'custom-face-font-name face args))
412 (fontobj (font-create-object font)))
413 (format "%d" (font-size fontobj))))
414
415 (defun custom-set-face-font-family (face family &rest args)
416 "Set the font of FACE to FAMILY."
417 (let* ((font (apply 'custom-face-font-name face args))
418 (fontobj (font-create-object font)))
419 (set-font-family fontobj family)
420 (apply 'font-set-face-font face fontobj args)))
421
422 (defun custom-face-font-family (face &rest args)
423 "Return the name of the font family of FACE."
424 (let* ((font (apply 'custom-face-font-name face args))
425 (fontobj (font-create-object font)))
426 (font-family fontobj)))
427
428 (nconc custom-face-attributes
429 '((:family (editable-field :format "Font Family: %v"
430 :help-echo "\
431Name of font family to use (e.g. times).")
432 custom-set-face-font-family
433 custom-face-font-family)
434 (:size (editable-field :format "Size: %v"
435 :help-echo "\
436Text size (e.g. 9pt or 2mm).")
437 custom-set-face-font-size
438 custom-face-font-size))))
439
440;;; Frames.
441
442(defun custom-face-display-set (face spec &optional frame)
443 "Set FACE to the attributes to the first matching entry in SPEC.
444Iff optional FRAME is non-nil, set it for that frame only.
445See `defface' for information about SPEC."
446 (when (fboundp 'make-face)
447 (while spec
448 (let* ((entry (car spec))
449 (display (nth 0 entry))
450 (atts (nth 1 entry)))
451 (setq spec (cdr spec))
452 (when (custom-display-match-frame display frame)
453 ;; Avoid creating frame local duplicates of the global face.
454 (unless (and frame (eq display (get face 'custom-face-display)))
455 (apply 'custom-face-attributes-set face frame atts))
456 (unless frame
457 (put face 'custom-face-display display))
458 (setq spec nil))))))
459
460(defvar custom-default-frame-properties nil
461 "The frame properties used for the global faces.
462Frames who doesn't match these propertiess should have frame local faces.
463The value should be nil, if uninitialized, or a plist otherwise.
464See `defface' for a list of valid keys and values for the plist.")
465
466(defun custom-get-frame-properties (&optional frame)
467 "Return a plist with the frame properties of FRAME used by custom.
468If FRAME is nil, return the default frame properties."
469 (cond (frame
470 ;; Try to get from cache.
471 (let ((cache (frame-property frame 'custom-properties)))
472 (unless cache
473 ;; Oh well, get it then.
474 (setq cache (custom-extract-frame-properties frame))
475 ;; and cache it...
476 (modify-frame-parameters frame
477 (list (cons 'custom-properties cache))))
478 cache))
479 (custom-default-frame-properties)
480 (t
481 (setq custom-default-frame-properties
482 (custom-extract-frame-properties (selected-frame))))))
483
484(defun custom-display-match-frame (display frame)
485 "Non-nil iff DISPLAY matches FRAME.
486If FRAME is nil, the current FRAME is used."
487 ;; This is a kludge to get started, we really should use specifiers!
488 (if (eq display t)
489 t
490 (let* ((props (custom-get-frame-properties frame))
491 (type (plist-get props 'type))
492 (class (plist-get props 'class))
493 (background (plist-get props 'background))
494 (match t)
495 (entries display)
496 entry req options)
497 (while (and entries match)
498 (setq entry (car entries)
499 entries (cdr entries)
500 req (car entry)
501 options (cdr entry)
502 match (cond ((eq req 'type)
503 (memq type options))
504 ((eq req 'class)
505 (memq class options))
506 ((eq req 'background)
507 (memq background options))
508 (t
509 (error "Unknown req `%S' with options `%S'"
510 req options)))))
511 match)))
512
513(defun custom-relevant-frames ()
514 "List of frames whose custom properties differ from the default."
515 (let ((relevant nil)
516 (default (custom-get-frame-properties))
517 (frames (frame-list))
518 frame)
519 (while frames
520 (setq frame (car frames)
521 frames (cdr frames))
522 (unless (equal default (custom-get-frame-properties frame))
523 (push frame relevant)))
524 relevant))
525
526(defun custom-initialize-faces (&optional frame)
527 "Initialize all custom faces for FRAME.
528If FRAME is nil or omitted, initialize them for all frames."
529 (mapcar (lambda (symbol)
530 (let ((spec (or (get symbol 'saved-face)
531 (get symbol 'factory-face))))
532 (when spec
533 (custom-face-display-set symbol spec frame)
534 (initialize-face-resources symbol frame))))
535 (face-list)))
536
537(defun custom-initialize-frame (&optional frame)
538 "Initialize local faces for FRAME if necessary.
539If FRAME is missing or nil, the first member of (frame-list) is used."
540 (unless frame
541 (setq frame (car (frame-list))))
542 (unless (equal (custom-get-frame-properties)
543 (custom-get-frame-properties frame))
544 (custom-initialize-faces frame)))
545
546;; Enable. This should go away when bundled with Emacs.
547(unless (string-match "XEmacs" emacs-version)
548 (add-hook 'after-make-frame-hook 'custom-initialize-frame))
549
550;;; Initializing.
551
552(and (fboundp 'make-face)
553 (make-face 'custom-face-empty))
554
555;;;###autoload
556(defun custom-set-faces (&rest args)
557 "Initialize faces according to user preferences.
558The arguments should be a list where each entry has the form:
559
560 (FACE SPEC [NOW])
561
562SPEC will be stored as the saved value for FACE. If NOW is present
563and non-nil, FACE will also be created according to SPEC.
564
565See `defface' for the format of SPEC."
566 (while args
567 (let ((entry (car args)))
568 (if (listp entry)
569 (let ((face (nth 0 entry))
570 (spec (nth 1 entry))
571 (now (nth 2 entry)))
572 (put face 'saved-face spec)
573 (when now
574 (put face 'force-face t))
575 (when (or now (custom-facep face))
576 (when (fboundp 'copy-face)
577 (copy-face 'custom-face-empty face))
578 (custom-face-display-set face spec))
579 (setq args (cdr args)))
580 ;; Old format, a plist of FACE SPEC pairs.
581 (let ((face (nth 0 args))
582 (spec (nth 1 args)))
583 (put face 'saved-face spec))
584 (setq args (cdr (cdr args)))))))
585
586;;; The End.
587
588(provide 'cus-face)
589
590;; cus-face.el ends here
diff --git a/lisp/custom.el b/lisp/custom.el
index e747264583c..6d247ebb379 100644
--- a/lisp/custom.el
+++ b/lisp/custom.el
@@ -1,2472 +1,332 @@
1;;; custom.el --- User friendly customization support. 1;;; custom.el -- Tools for declaring and initializing options.
2
3;; Copyright (C) 1995, 1996 Free Software Foundation, Inc.
4
5;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
6;; Keywords: help
7;; Version: 0.5
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
25
26;;; Commentary:
27
28;; WARNING: This package is still under construction and not all of
29;; the features below are implemented.
30;; 2;;
31;; This package provides a framework for adding user friendly 3;; Copyright (C) 1996, 1997 Free Software Foundation, Inc.
32;; customization support to Emacs. Having to do customization by
33;; editing a text file in some arcane syntax is user hostile in the
34;; extreme, and to most users emacs lisp definitely count as arcane.
35;; 4;;
36;; The intent is that authors of emacs lisp packages declare the 5;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
37;; variables intended for user customization with `custom-declare'. 6;; Keywords: help, faces
38;; Custom can then automatically generate a customization buffer with 7;; Version: 1.71
39;; `custom-buffer-create' where the user can edit the package 8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
40;; variables in a simple and intuitive way, as well as a menu with 9
41;; `custom-menu-create' where he can set the more commonly used 10;;; Commentary:
42;; variables interactively.
43;; 11;;
44;; It is also possible to use custom for modifying the properties of 12;; If you want to use this code, please visit the URL above.
45;; other objects than the package itself, by specifying extra optional
46;; arguments to `custom-buffer-create'.
47;; 13;;
48;; Custom is inspired by OPEN LOOK property windows. 14;; This file only contain the code needed to declare and initialize
15;; user options. The code to customize options is autoloaded from
16;; `cus-edit.el'.
49 17
50;;; Todo: 18;; The code implementing face declarations is in `cus-face.el'
51;;
52;; - Toggle documentation in three states `none', `one-line', `full'.
53;; - Function to generate an XEmacs menu from a CUSTOM.
54;; - Write TeXinfo documentation.
55;; - Make it possible to hide sections by clicking at the level.
56;; - Declare AUC TeX variables.
57;; - Declare (ding) Gnus variables.
58;; - Declare Emacs variables.
59;; - Implement remaining types.
60;; - XEmacs port.
61;; - Allow `URL', `info', and internal hypertext buttons.
62;; - Support meta-variables and goal directed customization.
63;; - Make it easy to declare custom types independently.
64;; - Make it possible to declare default value and type for a single
65;; variable, storing the data in a symbol property.
66;; - Syntactic sugar for CUSTOM declarations.
67;; - Use W3 for variable documentation.
68 19
69;;; Code: 20;;; Code:
70 21
71(eval-when-compile 22(require 'widget)
72 (require 'cl)) 23
73 24(define-widget-keywords :prefix :tag :load :link :options :type :group)
74;;; Compatibility: 25
75 26;; These autoloads should be deleted when the file is added to Emacs
76(defun custom-xmas-add-text-properties (start end props &optional object) 27
77 (add-text-properties start end props object) 28(unless (fboundp 'load-gc)
78 (put-text-property start end 'start-open t object) 29 ;; From cus-edit.el
79 (put-text-property start end 'end-open t object)) 30 (autoload 'customize "cus-edit" nil t)
80 31 (autoload 'customize-variable "cus-edit" nil t)
81(defun custom-xmas-put-text-property (start end prop value &optional object) 32 (autoload 'customize-face "cus-edit" nil t)
82 (put-text-property start end prop value object) 33 (autoload 'customize-apropos "cus-edit" nil t)
83 (put-text-property start end 'start-open t object) 34 (autoload 'customize-customized "cus-edit" nil t)
84 (put-text-property start end 'end-open t object)) 35 (autoload 'custom-buffer-create "cus-edit")
85 36 (autoload 'custom-menu-update "cus-edit")
86(defun custom-xmas-extent-start-open () 37 (autoload 'custom-make-dependencies "cus-edit")
87 (map-extents (lambda (extent arg) 38 ;; From cus-face.el
88 (set-extent-property extent 'start-open t)) 39 (autoload 'custom-declare-face "cus-face")
89 nil (point) (min (1+ (point)) (point-max)))) 40 (autoload 'custom-set-faces "cus-face"))
90 41
91(if (string-match "XEmacs\\|Lucid" emacs-version) 42;;; The `defcustom' Macro.
92 (progn 43
93 (fset 'custom-add-text-properties 'custom-xmas-add-text-properties) 44(defun custom-declare-variable (symbol value doc &rest args)
94 (fset 'custom-put-text-property 'custom-xmas-put-text-property) 45 "Like `defcustom', but SYMBOL and VALUE are evaluated as normal arguments."
95 (fset 'custom-extent-start-open 'custom-xmas-extent-start-open) 46 (unless (and (default-boundp symbol)
96 (fset 'custom-set-text-properties 47 (not (get symbol 'saved-value)))
97 (if (fboundp 'set-text-properties) 48 (set-default symbol (if (get symbol 'saved-value)
98 'set-text-properties)) 49 (eval (car (get symbol 'saved-value)))
99 (fset 'custom-buffer-substring-no-properties 50 (eval value))))
100 (if (fboundp 'buffer-substring-no-properties) 51 (put symbol 'factory-value (list value))
101 'buffer-substring-no-properties 52 (when doc
102 'custom-xmas-buffer-substring-no-properties))) 53 (put symbol 'variable-documentation doc))
103 (fset 'custom-add-text-properties 'add-text-properties) 54 (while args
104 (fset 'custom-put-text-property 'put-text-property) 55 (let ((arg (car args)))
105 (fset 'custom-extent-start-open 'ignore) 56 (setq args (cdr args))
106 (fset 'custom-set-text-properties 'set-text-properties) 57 (unless (symbolp arg)
107 (fset 'custom-buffer-substring-no-properties 58 (error "Junk in args %S" args))
108 'buffer-substring-no-properties)) 59 (let ((keyword arg)
109 60 (value (car args)))
110(defun custom-xmas-buffer-substring-no-properties (beg end) 61 (unless args
111 "Return the text from BEG to END, without text properties, as a string." 62 (error "Keyword %s is missing an argument" keyword))
112 (let ((string (buffer-substring beg end))) 63 (setq args (cdr args))
113 (custom-set-text-properties 0 (length string) nil string) 64 (cond ((eq keyword :type)
114 string)) 65 (put symbol 'custom-type value))
115 66 ((eq keyword :options)
116(or (fboundp 'add-to-list) 67 (if (get symbol 'custom-options)
117 ;; Introduced in Emacs 19.29. 68 ;; Slow safe code to avoid duplicates.
118 (defun add-to-list (list-var element) 69 (mapcar (lambda (option)
119 "Add to the value of LIST-VAR the element ELEMENT if it isn't there yet. 70 (custom-add-option symbol option))
120If you want to use `add-to-list' on a variable that is not defined 71 value)
121until a certain package is loaded, you should put the call to `add-to-list' 72 ;; Fast code for the common case.
122into a hook function that will be run only after loading the package. 73 (put symbol 'custom-options (copy-list value))))
123`eval-after-load' provides one way to do this. In some cases
124other hooks, such as major mode hooks, can do the job."
125 (or (member element (symbol-value list-var))
126 (set list-var (cons element (symbol-value list-var))))))
127
128(or (fboundp 'plist-get)
129 ;; Introduced in Emacs 19.29.
130 (defun plist-get (plist prop)
131 "Extract a value from a property list.
132PLIST is a property list, which is a list of the form
133\(PROP1 VALUE1 PROP2 VALUE2...). This function returns the value
134corresponding to the given PROP, or nil if PROP is not
135one of the properties on the list."
136 (let (result)
137 (while plist
138 (if (eq (car plist) prop)
139 (setq result (car (cdr plist))
140 plist nil)
141 (set plist (cdr (cdr plist)))))
142 result)))
143
144(or (fboundp 'plist-put)
145 ;; Introduced in Emacs 19.29.
146 (defun plist-put (plist prop val)
147 "Change value in PLIST of PROP to VAL.
148PLIST is a property list, which is a list of the form
149\(PROP1 VALUE1 PROP2 VALUE2 ...). PROP is a symbol and VAL is any object.
150If PROP is already a property on the list, its value is set to VAL,
151otherwise the new PROP VAL pair is added. The new plist is returned;
152use `(setq x (plist-put x prop val))' to be sure to use the new value.
153The PLIST is modified by side effects."
154 (if (null plist)
155 (list prop val)
156 (let ((current plist))
157 (while current
158 (cond ((eq (car current) prop)
159 (setcar (cdr current) val)
160 (setq current nil))
161 ((null (cdr (cdr current)))
162 (setcdr (cdr current) (list prop val))
163 (setq current nil))
164 (t
165 (setq current (cdr (cdr current)))))))
166 plist)))
167
168(or (fboundp 'match-string)
169 ;; Introduced in Emacs 19.29.
170 (defun match-string (num &optional string)
171 "Return string of text matched by last search.
172NUM specifies which parenthesized expression in the last regexp.
173 Value is nil if NUMth pair didn't match, or there were less than NUM pairs.
174Zero means the entire text matched by the whole regexp or whole string.
175STRING should be given if the last search was by `string-match' on STRING."
176 (if (match-beginning num)
177 (if string
178 (substring string (match-beginning num) (match-end num))
179 (buffer-substring (match-beginning num) (match-end num))))))
180
181(or (fboundp 'facep)
182 ;; Introduced in Emacs 19.29.
183 (defun facep (x)
184 "Return t if X is a face name or an internal face vector."
185 (and (or (and (fboundp 'internal-facep) (internal-facep x))
186 (and
187 (symbolp x)
188 (assq x (and (boundp 'global-face-data) global-face-data))))
189 t)))
190
191;; XEmacs and Emacs 19.29 facep does different things.
192(if (fboundp 'find-face)
193 (fset 'custom-facep 'find-face)
194 (fset 'custom-facep 'facep))
195
196(if (custom-facep 'underline)
197 ()
198 ;; No underline face in XEmacs 19.12.
199 (and (fboundp 'make-face)
200 (funcall (intern "make-face") 'underline))
201 ;; Must avoid calling set-face-underline-p directly, because it
202 ;; is a defsubst in emacs19, and will make the .elc files non
203 ;; portable!
204 (or (and (fboundp 'face-differs-from-default-p)
205 (face-differs-from-default-p 'underline))
206 (and (fboundp 'set-face-underline-p)
207 (funcall 'set-face-underline-p 'underline t))))
208
209(defun custom-xmas-set-text-properties (start end props &optional buffer)
210 (if (null buffer)
211 (if props
212 (while props
213 (custom-put-text-property
214 start end (car props) (nth 1 props) buffer)
215 (setq props (nthcdr 2 props)))
216 (remove-text-properties start end ()))))
217
218(or (fboundp 'event-point)
219 ;; Missing in Emacs 19.29.
220 (defun event-point (event)
221 "Return the character position of the given mouse-motion, button-press,
222or button-release event. If the event did not occur over a window, or did
223not occur over text, then this returns nil. Otherwise, it returns an index
224into the buffer visible in the event's window."
225 (posn-point (event-start event))))
226
227(eval-when-compile
228 (defvar x-colors nil)
229 (defvar custom-button-face nil)
230 (defvar custom-field-uninitialized-face nil)
231 (defvar custom-field-invalid-face nil)
232 (defvar custom-field-modified-face nil)
233 (defvar custom-field-face nil)
234 (defvar custom-mouse-face nil)
235 (defvar custom-field-active-face nil))
236
237;; We can't easily check for a working intangible.
238(defconst intangible (if (and (boundp 'emacs-minor-version)
239 (or (> emacs-major-version 19)
240 (and (> emacs-major-version 18)
241 (> emacs-minor-version 28))))
242 (setq intangible 'intangible)
243 (setq intangible 'intangible-if-it-had-been-working))
244 "The symbol making text intangible.")
245
246(defconst rear-nonsticky (if (string-match "XEmacs" emacs-version)
247 'end-open
248 'rear-nonsticky)
249 "The symbol making text properties non-sticky in the rear end.")
250
251(defconst front-sticky (if (string-match "XEmacs" emacs-version)
252 'front-closed
253 'front-sticky)
254 "The symbol making text properties sticky in the front.")
255
256(defconst mouse-face (if (string-match "XEmacs" emacs-version)
257 'highlight
258 'mouse-face)
259 "Symbol used for highlighting text under mouse.")
260
261;; Put it in the Help menu, if possible.
262(if (string-match "XEmacs" emacs-version)
263 (if (featurep 'menubar)
264 ;; XEmacs (disabled because it doesn't work)
265 (and current-menubar
266 (add-menu-item '("Help") "Customize..." 'customize t)))
267 ;; Emacs 19.28 and earlier
268 (global-set-key [ menu-bar help customize ]
269 '("Customize..." . customize))
270 ;; Emacs 19.29 and later
271 (global-set-key [ menu-bar help-menu customize ]
272 '("Customize..." . customize)))
273
274;; XEmacs popup-menu stolen from w3.el.
275(defun custom-x-really-popup-menu (pos title menudesc)
276 "My hacked up function to do a blocking popup menu..."
277 (let ((echo-keystrokes 0)
278 event menu)
279 (while menudesc
280 (setq menu (cons (vector (car (car menudesc))
281 (list (car (car menudesc))) t) menu)
282 menudesc (cdr menudesc)))
283 (setq menu (cons title menu))
284 (popup-menu menu)
285 (catch 'popup-done
286 (while t
287 (setq event (next-command-event event))
288 (cond ((and (misc-user-event-p event) (stringp (car-safe (event-object event))))
289 (throw 'popup-done (event-object event)))
290 ((and (misc-user-event-p event)
291 (or (eq (event-object event) 'abort)
292 (eq (event-object event) 'menu-no-selection-hook)))
293 nil)
294 ((not (popup-menu-up-p))
295 (throw 'popup-done nil))
296 ((button-release-event-p event);; don't beep twice
297 nil)
298 (t 74 (t
299 (beep) 75 (custom-handle-keyword symbol keyword value
300 (message "please make a choice from the menu."))))))) 76 'custom-variable))))))
301 77 (run-hooks 'custom-define-hook)
302;;; Categories: 78 symbol)
303;;
304;; XEmacs use inheritable extents for the same purpose as Emacs uses
305;; the category text property.
306
307(if (string-match "XEmacs" emacs-version)
308 (progn
309 ;; XEmacs categories.
310 (defun custom-category-create (name)
311 (set name (make-extent nil nil))
312 "Create a text property category named NAME.")
313
314 (defun custom-category-put (name property value)
315 "In CATEGORY set PROPERTY to VALUE."
316 (set-extent-property (symbol-value name) property value))
317
318 (defun custom-category-get (name property)
319 "In CATEGORY get PROPERTY."
320 (extent-property (symbol-value name) property))
321
322 (defun custom-category-set (from to category)
323 "Make text between FROM and TWO have category CATEGORY."
324 (let ((extent (make-extent from to)))
325 (set-extent-parent extent (symbol-value category)))))
326
327 ;; Emacs categories.
328 (defun custom-category-create (name)
329 "Create a text property category named NAME."
330 (set name name))
331
332 (defun custom-category-put (name property value)
333 "In CATEGORY set PROPERTY to VALUE."
334 (put name property value))
335
336 (defun custom-category-get (name property)
337 "In CATEGORY get PROPERTY."
338 (get name property))
339
340 (defun custom-category-set (from to category)
341 "Make text between FROM and TWO have category CATEGORY."
342 (custom-put-text-property from to 'category category)))
343
344;;; External Data:
345;;
346;; The following functions and variables defines the interface for
347;; connecting a CUSTOM with an external entity, by default an emacs
348;; lisp variable.
349
350(defvar custom-external 'default-value
351 "Function returning the external value of NAME.")
352
353(defvar custom-external-set 'set-default
354 "Function setting the external value of NAME to VALUE.")
355
356(defun custom-external (name)
357 "Get the external value associated with NAME."
358 (funcall custom-external name))
359
360(defun custom-external-set (name value)
361 "Set the external value associated with NAME to VALUE."
362 (funcall custom-external-set name value))
363
364(defvar custom-name-fields nil
365 "Alist of custom names and their associated editing field.")
366(make-variable-buffer-local 'custom-name-fields)
367
368(defun custom-name-enter (name field)
369 "Associate NAME with FIELD."
370 (if (null name)
371 ()
372 (custom-assert 'field)
373 (setq custom-name-fields (cons (cons name field) custom-name-fields))))
374
375(defun custom-name-field (name)
376 "The editing field associated with NAME."
377 (cdr (assq name custom-name-fields)))
378
379(defun custom-name-value (name)
380 "The value currently displayed for NAME in the customization buffer."
381 (let* ((field (custom-name-field name))
382 (custom (custom-field-custom field)))
383 (custom-field-parse field)
384 (funcall (custom-property custom 'export) custom
385 (car (custom-field-extract custom field)))))
386
387(defvar custom-save 'custom-save
388 "Function that will save current customization buffer.")
389
390;;; Custom Functions:
391;;
392;; The following functions are part of the public interface to the
393;; CUSTOM datastructure. Each CUSTOM describes a group of variables,
394;; a single variable, or a component of a structured variable. The
395;; CUSTOM instances are part of two hierarchies, the first is the
396;; `part-of' hierarchy in which each CUSTOM is a component of another
397;; CUSTOM, except for the top level CUSTOM which is contained in
398;; `custom-data'. The second hierarchy is a `is-a' type hierarchy
399;; where each CUSTOM is a leaf in the hierarchy defined by the `type'
400;; property and `custom-type-properties'.
401
402(defvar custom-file "~/.custom.el"
403 "Name of file with customization information.")
404
405(defconst custom-data
406 '((tag . "Emacs")
407 (doc . "The extensible self-documenting text editor.")
408 (type . group)
409 (data "\n"
410 ((header . nil)
411 (compact . t)
412 (type . group)
413 (doc . "\
414Press [Save] to save any changes permanently after you are done editing.
415You can load customization information from other files by editing the
416`File' field and pressing the [Load] button. When you press [Save] the
417customization information of all files you have loaded, plus any
418changes you might have made manually, will be stored in the file
419specified by the `File' field.")
420 (data ((tag . "Load")
421 (type . button)
422 (query . custom-load))
423 ((tag . "Save")
424 (type . button)
425 (query . custom-save))
426 ((name . custom-file)
427 (default . "~/.custom.el")
428 (doc . "Name of file with customization information.\n")
429 (tag . "File")
430 (type . file))))))
431 "The global customization information.
432A custom association list.")
433
434(defun custom-declare (path custom)
435 "Declare variables for customization.
436PATH is a list of tags leading to the place in the customization
437hierarchy the new entry should be added. CUSTOM is the entry to add."
438 (custom-initialize custom)
439 (let ((current (custom-travel-path custom-data path)))
440 (or (member custom (custom-data current))
441 (nconc (custom-data current) (list custom)))))
442
443(put 'custom-declare 'lisp-indent-hook 1)
444
445(defconst custom-type-properties
446 '((repeat (type . default)
447 ;; See `custom-match'.
448 (import . custom-repeat-import)
449 (eval . custom-repeat-eval)
450 (quote . custom-repeat-quote)
451 (accept . custom-repeat-accept)
452 (extract . custom-repeat-extract)
453 (validate . custom-repeat-validate)
454 (insert . custom-repeat-insert)
455 (match . custom-repeat-match)
456 (query . custom-repeat-query)
457 (prefix . "")
458 (del-tag . "[DEL]")
459 (add-tag . "[INS]"))
460 (pair (type . group)
461 ;; A cons-cell.
462 (accept . custom-pair-accept)
463 (eval . custom-pair-eval)
464 (import . custom-pair-import)
465 (quote . custom-pair-quote)
466 (valid . (lambda (c d) (consp d)))
467 (extract . custom-pair-extract))
468 (list (type . group)
469 ;; A lisp list.
470 (quote . custom-list-quote)
471 (valid . (lambda (c d)
472 (listp d)))
473 (extract . custom-list-extract))
474 (group (type . default)
475 ;; See `custom-match'.
476 (face-tag . nil)
477 (eval . custom-group-eval)
478 (import . custom-group-import)
479 (initialize . custom-group-initialize)
480 (apply . custom-group-apply)
481 (reset . custom-group-reset)
482 (factory-reset . custom-group-factory-reset)
483 (extract . nil)
484 (validate . custom-group-validate)
485 (query . custom-toggle-hide)
486 (accept . custom-group-accept)
487 (insert . custom-group-insert)
488 (find . custom-group-find))
489 (toggle (type . choice)
490 ;; Booleans.
491 (data ((type . const)
492 (tag . "On ")
493 (default . t))
494 ((type . const)
495 (tag . "Off")
496 (default . nil))))
497 (triggle (type . choice)
498 ;; On/Off/Default.
499 (data ((type . const)
500 (tag . "On ")
501 (default . t))
502 ((type . const)
503 (tag . "Off")
504 (default . nil))
505 ((type . const)
506 (tag . "Def")
507 (default . custom:asis))))
508 (choice (type . default)
509 ;; See `custom-match'.
510 (query . custom-choice-query)
511 (accept . custom-choice-accept)
512 (extract . custom-choice-extract)
513 (validate . custom-choice-validate)
514 (insert . custom-choice-insert)
515 (none (tag . "Unknown")
516 (default . __uninitialized__)
517 (type . const)))
518 (const (type . default)
519 ;; A `const' only matches a single lisp value.
520 (extract . (lambda (c f) (list (custom-default c))))
521 (validate . (lambda (c f) nil))
522 (valid . custom-const-valid)
523 (update . custom-const-update)
524 (insert . custom-const-insert))
525 (face-doc (type . doc)
526 ;; A variable containing a face.
527 (doc . "\
528You can customize the look of Emacs by deciding which faces should be
529used when. If you push one of the face buttons below, you will be
530given a choice between a number of standard faces. The name of the
531selected face is shown right after the face button, and it is
532displayed its own face so you can see how it looks. If you know of
533another standard face not listed and want to use it, you can select
534`Other' and write the name in the editing field.
535
536If none of the standard faces suits you, you can select `Customize' to
537create your own face. This will make six fields appear under the face
538button. The `Fg' and `Bg' fields are the foreground and background
539colors for the face, respectively. You should type the name of the
540color in the field. You can use any X11 color name. A list of X11
541color names may be available in the file `/usr/lib/X11/rgb.txt' on
542your system. The special color name `default' means that the face
543will not change the color of the text. The `Stipple' field is weird,
544so just ignore it. The three remaining fields are toggles, which will
545make the text `bold', `italic', or `underline' respectively. For some
546fonts `bold' or `italic' will not make any visible change."))
547 (face (type . choice)
548 (eval . custom-face-eval)
549 (import . custom-face-import)
550 (data ((tag . "None")
551 (default . nil)
552 (type . const))
553 ((tag . "Default")
554 (default . default)
555 (face . custom-const-face)
556 (type . const))
557 ((tag . "Bold")
558 (default . bold)
559 (face . custom-const-face)
560 (type . const))
561 ((tag . "Bold-italic")
562 (default . bold-italic)
563 (face . custom-const-face)
564 (type . const))
565 ((tag . "Italic")
566 (default . italic)
567 (face . custom-const-face)
568 (type . const))
569 ((tag . "Underline")
570 (default . underline)
571 (face . custom-const-face)
572 (type . const))
573 ((tag . "Highlight")
574 (default . highlight)
575 (face . custom-const-face)
576 (type . const))
577 ((tag . "Modeline")
578 (default . modeline)
579 (face . custom-const-face)
580 (type . const))
581 ((tag . "Region")
582 (default . region)
583 (face . custom-const-face)
584 (type . const))
585 ((tag . "Secondary Selection")
586 (default . secondary-selection)
587 (face . custom-const-face)
588 (type . const))
589 ((tag . "Customized")
590 (compact . t)
591 (face-tag . custom-face-hack)
592 (eval . custom-face-eval)
593 (data ((hidden . t)
594 (tag . "")
595 (doc . "\
596Select the properties you want this face to have.")
597 (default . custom-face-lookup)
598 (type . const))
599 "\n"
600 ((tag . "Fg")
601 (hidden . t)
602 (default . "default")
603 (width . 20)
604 (type . string))
605 ((tag . "Bg")
606 (default . "default")
607 (width . 20)
608 (type . string))
609 ((tag . "Stipple")
610 (default . "default")
611 (width . 20)
612 (type . string))
613 "\n"
614 ((tag . "Bold")
615 (default . custom:asis)
616 (type . triggle))
617 " "
618 ((tag . "Italic")
619 (default . custom:asis)
620 (type . triggle))
621 " "
622 ((tag . "Underline")
623 (hidden . t)
624 (default . custom:asis)
625 (type . triggle)))
626 (default . (custom-face-lookup "default" "default" "default"
627 nil nil nil))
628 (type . list))
629 ((prompt . "Other")
630 (face . custom-field-value)
631 (default . __uninitialized__)
632 (type . symbol))))
633 (file (type . string)
634 ;; A string containing a file or directory name.
635 (directory . nil)
636 (default-file . nil)
637 (query . custom-file-query))
638 (sexp (type . default)
639 ;; Any lisp expression.
640 (width . 40)
641 (default . (__uninitialized__ . "Uninitialized"))
642 (read . custom-sexp-read)
643 (write . custom-sexp-write))
644 (symbol (type . sexp)
645 ;; A lisp symbol.
646 (width . 40)
647 (valid . (lambda (c d) (symbolp d))))
648 (integer (type . sexp)
649 ;; A lisp integer.
650 (width . 10)
651 (valid . (lambda (c d) (integerp d))))
652 (string (type . default)
653 ;; A lisp string.
654 (width . 40)
655 (valid . (lambda (c d) (stringp d)))
656 (read . custom-string-read)
657 (write . custom-string-write))
658 (button (type . default)
659 ;; Push me.
660 (accept . ignore)
661 (extract . nil)
662 (validate . ignore)
663 (insert . custom-button-insert))
664 (doc (type . default)
665 ;; A documentation only entry with no value.
666 (header . nil)
667 (reset . ignore)
668 (extract . nil)
669 (validate . ignore)
670 (insert . custom-documentation-insert))
671 (default (width . 20)
672 (valid . (lambda (c v) t))
673 (insert . custom-default-insert)
674 (update . custom-default-update)
675 (query . custom-default-query)
676 (tag . nil)
677 (prompt . nil)
678 (doc . nil)
679 (header . t)
680 (padding . ? )
681 (quote . custom-default-quote)
682 (eval . (lambda (c v) nil))
683 (export . custom-default-export)
684 (import . (lambda (c v) (list v)))
685 (synchronize . ignore)
686 (initialize . custom-default-initialize)
687 (extract . custom-default-extract)
688 (validate . custom-default-validate)
689 (apply . custom-default-apply)
690 (reset . custom-default-reset)
691 (factory-reset . custom-default-factory-reset)
692 (accept . custom-default-accept)
693 (match . custom-default-match)
694 (name . nil)
695 (compact . nil)
696 (hidden . nil)
697 (face . custom-default-face)
698 (data . nil)
699 (calculate . nil)
700 (default . __uninitialized__)))
701 "Alist of default properties for type symbols.
702The format is `((SYMBOL (PROPERTY . VALUE)... )... )'.")
703
704(defconst custom-local-type-properties nil
705 "Local type properties.
706Entries in this list take precedence over `custom-type-properties'.")
707
708(make-variable-buffer-local 'custom-local-type-properties)
709
710(defconst custom-nil '__uninitialized__
711 "Special value representing an uninitialized field.")
712
713(defconst custom-invalid '__invalid__
714 "Special value representing an invalid field.")
715
716(defconst custom:asis 'custom:asis)
717;; Bad, ugly, and horrible kludge.
718
719(defun custom-property (custom property)
720 "Extract from CUSTOM property PROPERTY."
721 (let ((entry (assq property custom)))
722 (while (null entry)
723 ;; Look in superclass.
724 (let ((type (custom-type custom)))
725 (setq custom (cdr (or (assq type custom-local-type-properties)
726 (assq type custom-type-properties)))
727 entry (assq property custom))
728 (custom-assert 'custom)))
729 (cdr entry)))
730
731(defun custom-super (custom property)
732 "Extract from CUSTOM property PROPERTY. Start with CUSTOM's superclass."
733 (let ((entry nil))
734 (while (null entry)
735 ;; Look in superclass.
736 (let ((type (custom-type custom)))
737 (setq custom (cdr (or (assq type custom-local-type-properties)
738 (assq type custom-type-properties)))
739 entry (assq property custom))
740 (custom-assert 'custom)))
741 (cdr entry)))
742
743(defun custom-property-set (custom property value)
744 "Set CUSTOM PROPERTY to VALUE by side effect.
745CUSTOM must have at least one property already."
746 (let ((entry (assq property custom)))
747 (if entry
748 (setcdr entry value)
749 (setcdr custom (cons (cons property value) (cdr custom))))))
750
751(defun custom-type (custom)
752 "Extract `type' from CUSTOM."
753 (cdr (assq 'type custom)))
754
755(defun custom-name (custom)
756 "Extract `name' from CUSTOM."
757 (custom-property custom 'name))
758
759(defun custom-tag (custom)
760 "Extract `tag' from CUSTOM."
761 (custom-property custom 'tag))
762
763(defun custom-face-tag (custom)
764 "Extract `face-tag' from CUSTOM."
765 (custom-property custom 'face-tag))
766
767(defun custom-prompt (custom)
768 "Extract `prompt' from CUSTOM.
769If none exist, default to `tag' or, failing that, `type'."
770 (or (custom-property custom 'prompt)
771 (custom-property custom 'tag)
772 (capitalize (symbol-name (custom-type custom)))))
773
774(defun custom-default (custom)
775 "Extract `default' from CUSTOM."
776 (let ((value (custom-property custom 'calculate)))
777 (if value
778 (eval value)
779 (custom-property custom 'default))))
780
781(defun custom-data (custom)
782 "Extract the `data' from CUSTOM."
783 (custom-property custom 'data))
784
785(defun custom-documentation (custom)
786 "Extract `doc' from CUSTOM."
787 (custom-property custom 'doc))
788
789(defun custom-width (custom)
790 "Extract `width' from CUSTOM."
791 (custom-property custom 'width))
792
793(defun custom-compact (custom)
794 "Extract `compact' from CUSTOM."
795 (custom-property custom 'compact))
796
797(defun custom-padding (custom)
798 "Extract `padding' from CUSTOM."
799 (custom-property custom 'padding))
800
801(defun custom-valid (custom value)
802 "Non-nil if CUSTOM may validly be set to VALUE."
803 (and (not (and (listp value) (eq custom-invalid (car value))))
804 (funcall (custom-property custom 'valid) custom value)))
805
806(defun custom-import (custom value)
807 "Import CUSTOM VALUE from external variable.
808
809This function change VALUE into a form that makes it easier to edit
810internally. What the internal form is exactly depends on CUSTOM.
811The internal form is returned."
812 (if (eq custom-nil value)
813 (list custom-nil)
814 (funcall (custom-property custom 'import) custom value)))
815
816(defun custom-eval (custom value)
817 "Return non-nil if CUSTOM's VALUE needs to be evaluated."
818 (funcall (custom-property custom 'eval) custom value))
819
820(defun custom-quote (custom value)
821 "Quote CUSTOM's VALUE if necessary."
822 (funcall (custom-property custom 'quote) custom value))
823
824(defun custom-write (custom value)
825 "Convert CUSTOM VALUE to a string."
826 (cond ((eq value custom-nil)
827 "")
828 ((and (listp value) (eq (car value) custom-invalid))
829 (cdr value))
830 (t
831 (funcall (custom-property custom 'write) custom value))))
832
833(defun custom-read (custom string)
834 "Convert CUSTOM field content STRING into lisp."
835 (condition-case nil
836 (funcall (custom-property custom 'read) custom string)
837 (error (cons custom-invalid string))))
838
839(defun custom-match (custom values)
840 "Match CUSTOM with a list of VALUES.
841
842Return a cons-cell where the car is the sublist of VALUES matching CUSTOM,
843and the cdr is the remaining VALUES.
844
845A CUSTOM is actually a regular expression over the alphabet of lisp
846types. Most CUSTOM types are just doing a literal match, e.g. the
847`symbol' type matches any lisp symbol. The exceptions are:
848
849group: which corresponds to a `(' and `)' group in a regular expression.
850choice: which corresponds to a group of `|' in a regular expression.
851repeat: which corresponds to a `*' in a regular expression.
852optional: which corresponds to a `?', and isn't implemented yet."
853 (if (memq values (list custom-nil nil))
854 ;; Nothing matches the uninitialized or empty list.
855 (cons custom-nil nil)
856 (funcall (custom-property custom 'match) custom values)))
857
858(defun custom-initialize (custom)
859 "Initialize `doc' and `default' attributes of CUSTOM."
860 (funcall (custom-property custom 'initialize) custom))
861
862(defun custom-find (custom tag)
863 "Find child in CUSTOM with `tag' TAG."
864 (funcall (custom-property custom 'find) custom tag))
865
866(defun custom-travel-path (custom path)
867 "Find decedent of CUSTOM by looking through PATH."
868 (if (null path)
869 custom
870 (custom-travel-path (custom-find custom (car path)) (cdr path))))
871
872(defun custom-field-extract (custom field)
873 "Extract CUSTOM's value in FIELD."
874 (if (stringp custom)
875 nil
876 (funcall (custom-property (custom-field-custom field) 'extract)
877 custom field)))
878
879(defun custom-field-validate (custom field)
880 "Validate CUSTOM's value in FIELD.
881Return nil if valid, otherwise return a cons-cell where the car is the
882position of the error, and the cdr is a text describing the error."
883 (if (stringp custom)
884 nil
885 (funcall (custom-property custom 'validate) custom field)))
886
887;;; Field Functions:
888;;
889;; This section defines the public functions for manipulating the
890;; FIELD datatype. The FIELD instance hold information about a
891;; specific editing field in the customization buffer.
892;;
893;; Each FIELD can be seen as an instantiation of a CUSTOM.
894
895(defvar custom-field-last nil)
896;; Last field containing point.
897(make-variable-buffer-local 'custom-field-last)
898
899(defvar custom-modified-list nil)
900;; List of modified fields.
901(make-variable-buffer-local 'custom-modified-list)
902
903(defun custom-field-create (custom value)
904 "Create a field structure of type CUSTOM containing VALUE.
905
906A field structure is an array [ CUSTOM VALUE ORIGINAL START END ], where
907CUSTOM defines the type of the field,
908VALUE is the current value of the field,
909ORIGINAL is the original value when created, and
910START and END are markers to the start and end of the field."
911 (vector custom value custom-nil nil nil))
912
913(defun custom-field-custom (field)
914 "Return the `custom' attribute of FIELD."
915 (aref field 0))
916
917(defun custom-field-value (field)
918 "Return the `value' attribute of FIELD."
919 (aref field 1))
920
921(defun custom-field-original (field)
922 "Return the `original' attribute of FIELD."
923 (aref field 2))
924
925(defun custom-field-start (field)
926 "Return the `start' attribute of FIELD."
927 (aref field 3))
928
929(defun custom-field-end (field)
930 "Return the `end' attribute of FIELD."
931 (aref field 4))
932
933(defun custom-field-value-set (field value)
934 "Set the `value' attribute of FIELD to VALUE."
935 (aset field 1 value))
936
937(defun custom-field-original-set (field original)
938 "Set the `original' attribute of FIELD to ORIGINAL."
939 (aset field 2 original))
940
941(defun custom-field-move (field start end)
942 "Set the `start'and `end' attributes of FIELD to START and END."
943 (set-marker (or (aref field 3) (aset field 3 (make-marker))) start)
944 (set-marker (or (aref field 4) (aset field 4 (make-marker))) end))
945
946(defun custom-field-query (field)
947 "Query user for content of current field."
948 (funcall (custom-property (custom-field-custom field) 'query) field))
949 79
950(defun custom-field-accept (field value &optional original) 80(defmacro defcustom (symbol value doc &rest args)
951 "Store a new value into field FIELD, taking it from VALUE. 81 "Declare SYMBOL as a customizable variable that defaults to VALUE.
952If optional ORIGINAL is non-nil, consider VALUE for the original value." 82DOC is the variable documentation.
953 (let ((inhibit-point-motion-hooks t))
954 (funcall (custom-property (custom-field-custom field) 'accept)
955 field value original)))
956 83
957(defun custom-field-face (field) 84Neither SYMBOL nor VALUE needs to be quoted.
958 "The face used for highlighting FIELD." 85If SYMBOL is not already bound, initialize it to VALUE.
959 (let ((custom (custom-field-custom field))) 86The remaining arguments should have the form
960 (if (stringp custom)
961 nil
962 (let ((face (funcall (custom-property custom 'face) field)))
963 (if (custom-facep face) face nil)))))
964 87
965(defun custom-field-update (field) 88 [KEYWORD VALUE]...
966 "Update the screen appearance of FIELD to correspond with the field's value."
967 (let ((custom (custom-field-custom field)))
968 (if (stringp custom)
969 nil
970 (funcall (custom-property custom 'update) field))))
971 89
972;;; Types: 90The following KEYWORD's are defined:
973;;
974;; The following functions defines type specific actions.
975
976(defun custom-repeat-eval (custom value)
977 "Non-nil if CUSTOM's VALUE needs to be evaluated."
978 (if (eq value custom-nil)
979 nil
980 (let ((child (custom-data custom))
981 (found nil))
982 (mapcar (lambda (v) (if (custom-eval child v) (setq found t)))
983 value))))
984
985(defun custom-repeat-quote (custom value)
986 "A list of CUSTOM's VALUEs quoted."
987 (let ((child (custom-data custom)))
988 (apply 'append (mapcar (lambda (v) (custom-quote child v))
989 value))))
990
991
992(defun custom-repeat-import (custom value)
993 "Modify CUSTOM's VALUE to match internal expectations."
994 (let ((child (custom-data custom)))
995 (apply 'append (mapcar (lambda (v) (custom-import child v))
996 value))))
997
998(defun custom-repeat-accept (field value &optional original)
999 "Store a new value into field FIELD, taking it from VALUE."
1000 (let ((values (copy-sequence (custom-field-value field)))
1001 (all (custom-field-value field))
1002 (start (custom-field-start field))
1003 current new)
1004 (if original
1005 (custom-field-original-set field value))
1006 (while (consp value)
1007 (setq new (car value)
1008 value (cdr value))
1009 (if values
1010 ;; Change existing field.
1011 (setq current (car values)
1012 values (cdr values))
1013 ;; Insert new field if series has grown.
1014 (goto-char start)
1015 (setq current (custom-repeat-insert-entry field))
1016 (setq all (custom-insert-before all nil current))
1017 (custom-field-value-set field all))
1018 (custom-field-accept current new original))
1019 (while (consp values)
1020 ;; Delete old field if series has scrunk.
1021 (setq current (car values)
1022 values (cdr values))
1023 (let ((pos (custom-field-start current))
1024 data)
1025 (while (not data)
1026 (setq pos (previous-single-property-change pos 'custom-data))
1027 (custom-assert 'pos)
1028 (setq data (get-text-property pos 'custom-data))
1029 (or (and (arrayp data)
1030 (> (length data) 1)
1031 (eq current (aref data 1)))
1032 (setq data nil)))
1033 (custom-repeat-delete data)))))
1034
1035(defun custom-repeat-insert (custom level)
1036 "Insert field for CUSTOM at nesting LEVEL in customization buffer."
1037 (let* ((field (custom-field-create custom nil))
1038 (add-tag (custom-property custom 'add-tag))
1039 (start (make-marker))
1040 (data (vector field nil start nil)))
1041 (custom-text-insert "\n")
1042 (let ((pos (point)))
1043 (custom-text-insert (custom-property custom 'prefix))
1044 (custom-tag-insert add-tag 'custom-repeat-add data)
1045 (set-marker start pos))
1046 (custom-field-move field start (point))
1047 (custom-documentation-insert custom)
1048 field))
1049
1050(defun custom-repeat-insert-entry (repeat)
1051 "Insert entry at point in the REPEAT field."
1052 (let* ((inhibit-point-motion-hooks t)
1053 (inhibit-read-only t)
1054 (before-change-functions nil)
1055 (after-change-functions nil)
1056 (custom (custom-field-custom repeat))
1057 (add-tag (custom-property custom 'add-tag))
1058 (del-tag (custom-property custom 'del-tag))
1059 (start (make-marker))
1060 (end (make-marker))
1061 (data (vector repeat nil start end))
1062 field)
1063 (custom-extent-start-open)
1064 (insert-before-markers "\n")
1065 (backward-char 1)
1066 (set-marker start (point))
1067 (custom-text-insert " ")
1068 (aset data 1 (setq field (custom-insert (custom-data custom) nil)))
1069 (custom-text-insert " ")
1070 (set-marker end (point))
1071 (goto-char start)
1072 (custom-text-insert (custom-property custom 'prefix))
1073 (custom-tag-insert add-tag 'custom-repeat-add data)
1074 (custom-text-insert " ")
1075 (custom-tag-insert del-tag 'custom-repeat-delete data)
1076 (forward-char 1)
1077 field))
1078
1079(defun custom-repeat-add (data)
1080 "Add list entry."
1081 (let ((parent (aref data 0))
1082 (field (aref data 1))
1083 (at (aref data 2))
1084 new)
1085 (goto-char at)
1086 (setq new (custom-repeat-insert-entry parent))
1087 (custom-field-value-set parent
1088 (custom-insert-before (custom-field-value parent)
1089 field new))))
1090
1091(defun custom-repeat-delete (data)
1092 "Delete list entry."
1093 (let ((inhibit-point-motion-hooks t)
1094 (inhibit-read-only t)
1095 (before-change-functions nil)
1096 (after-change-functions nil)
1097 (parent (aref data 0))
1098 (field (aref data 1)))
1099 (delete-region (aref data 2) (1+ (aref data 3)))
1100 (custom-field-untouch (aref data 1))
1101 (custom-field-value-set parent
1102 (delq field (custom-field-value parent)))))
1103
1104(defun custom-repeat-match (custom values)
1105 "Match CUSTOM with VALUES."
1106 (let* ((child (custom-data custom))
1107 (match (custom-match child values))
1108 matches)
1109 (while (not (eq (car match) custom-nil))
1110 (setq matches (cons (car match) matches)
1111 values (cdr match)
1112 match (custom-match child values)))
1113 (cons (nreverse matches) values)))
1114
1115(defun custom-repeat-extract (custom field)
1116 "Extract list of children's values."
1117 (let ((values (custom-field-value field))
1118 (data (custom-data custom))
1119 result)
1120 (if (eq values custom-nil)
1121 ()
1122 (while values
1123 (setq result (append result (custom-field-extract data (car values)))
1124 values (cdr values))))
1125 result))
1126
1127(defun custom-repeat-validate (custom field)
1128 "Validate children."
1129 (let ((values (custom-field-value field))
1130 (data (custom-data custom))
1131 result)
1132 (if (eq values custom-nil)
1133 (setq result (cons (custom-field-start field) "Uninitialized list")))
1134 (while (and values (not result))
1135 (setq result (custom-field-validate data (car values))
1136 values (cdr values)))
1137 result))
1138
1139(defun custom-pair-accept (field value &optional original)
1140 "Store a new value into field FIELD, taking it from VALUE."
1141 (custom-group-accept field (list (car value) (cdr value)) original))
1142
1143(defun custom-pair-eval (custom value)
1144 "Non-nil if CUSTOM's VALUE needs to be evaluated."
1145 (custom-group-eval custom (list (car value) (cdr value))))
1146
1147(defun custom-pair-import (custom value)
1148 "Modify CUSTOM's VALUE to match internal expectations."
1149 (let ((result (car (custom-group-import custom
1150 (list (car value) (cdr value))))))
1151 (custom-assert '(eq (length result) 2))
1152 (list (cons (nth 0 result) (nth 1 result)))))
1153
1154(defun custom-pair-quote (custom value)
1155 "Quote CUSTOM's VALUE if necessary."
1156 (if (custom-eval custom value)
1157 (let ((v (car (custom-group-quote custom
1158 (list (car value) (cdr value))))))
1159 (list (list 'cons (nth 0 v) (nth 1 v))))
1160 (custom-default-quote custom value)))
1161
1162(defun custom-pair-extract (custom field)
1163 "Extract cons of children's values."
1164 (let ((values (custom-field-value field))
1165 (data (custom-data custom))
1166 result)
1167 (custom-assert '(eq (length values) (length data)))
1168 (while values
1169 (setq result (append result
1170 (custom-field-extract (car data) (car values)))
1171 data (cdr data)
1172 values (cdr values)))
1173 (custom-assert '(null data))
1174 (list (cons (nth 0 result) (nth 1 result)))))
1175
1176(defun custom-list-quote (custom value)
1177 "Quote CUSTOM's VALUE if necessary."
1178 (if (custom-eval custom value)
1179 (let ((v (car (custom-group-quote custom value))))
1180 (list (cons 'list v)))
1181 (custom-default-quote custom value)))
1182
1183(defun custom-list-extract (custom field)
1184 "Extract list of children's values."
1185 (let ((values (custom-field-value field))
1186 (data (custom-data custom))
1187 result)
1188 (custom-assert '(eq (length values) (length data)))
1189 (while values
1190 (setq result (append result
1191 (custom-field-extract (car data) (car values)))
1192 data (cdr data)
1193 values (cdr values)))
1194 (custom-assert '(null data))
1195 (list result)))
1196
1197(defun custom-group-validate (custom field)
1198 "Validate children."
1199 (let ((values (custom-field-value field))
1200 (data (custom-data custom))
1201 result)
1202 (if (eq values custom-nil)
1203 (setq result (cons (custom-field-start field) "Uninitialized list"))
1204 (custom-assert '(eq (length values) (length data))))
1205 (while (and values (not result))
1206 (setq result (custom-field-validate (car data) (car values))
1207 data (cdr data)
1208 values (cdr values)))
1209 result))
1210
1211(defun custom-group-eval (custom value)
1212 "Non-nil if CUSTOM's VALUE needs to be evaluated."
1213 (let ((found nil))
1214 (mapcar (lambda (c)
1215 (or (stringp c)
1216 (let ((match (custom-match c value)))
1217 (if (custom-eval c (car match))
1218 (setq found t))
1219 (setq value (cdr match)))))
1220 (custom-data custom))
1221 found))
1222
1223(defun custom-group-quote (custom value)
1224 "A list of CUSTOM's VALUE members, quoted."
1225 (list (apply 'append
1226 (mapcar (lambda (c)
1227 (if (stringp c)
1228 ()
1229 (let ((match (custom-match c value)))
1230 (prog1 (custom-quote c (car match))
1231 (setq value (cdr match))))))
1232 (custom-data custom)))))
1233
1234(defun custom-group-import (custom value)
1235 "Modify CUSTOM's VALUE to match internal expectations."
1236 (list (apply 'append
1237 (mapcar (lambda (c)
1238 (if (stringp c)
1239 ()
1240 (let ((match (custom-match c value)))
1241 (prog1 (custom-import c (car match))
1242 (setq value (cdr match))))))
1243 (custom-data custom)))))
1244
1245(defun custom-group-initialize (custom)
1246 "Initialize `doc' and `default' entries in CUSTOM."
1247 (if (custom-name custom)
1248 (custom-default-initialize custom)
1249 (mapcar 'custom-initialize (custom-data custom))))
1250
1251(defun custom-group-apply (field)
1252 "Reset `value' in FIELD to `original'."
1253 (let ((custom (custom-field-custom field))
1254 (values (custom-field-value field)))
1255 (if (custom-name custom)
1256 (custom-default-apply field)
1257 (mapcar 'custom-field-apply values))))
1258
1259(defun custom-group-reset (field)
1260 "Reset `value' in FIELD to `original'."
1261 (let ((custom (custom-field-custom field))
1262 (values (custom-field-value field)))
1263 (if (custom-name custom)
1264 (custom-default-reset field)
1265 (mapcar 'custom-field-reset values))))
1266
1267(defun custom-group-factory-reset (field)
1268 "Reset `value' in FIELD to `default'."
1269 (let ((custom (custom-field-custom field))
1270 (values (custom-field-value field)))
1271 (if (custom-name custom)
1272 (custom-default-factory-reset field)
1273 (mapcar 'custom-field-factory-reset values))))
1274
1275(defun custom-group-find (custom tag)
1276 "Find child in CUSTOM with `tag' TAG."
1277 (let ((data (custom-data custom))
1278 (result nil))
1279 (while (not result)
1280 (custom-assert 'data)
1281 (if (equal (custom-tag (car data)) tag)
1282 (setq result (car data))
1283 (setq data (cdr data))))))
1284 91
1285(defun custom-group-accept (field value &optional original) 92:type VALUE should be a widget type.
1286 "Store a new value into field FIELD, taking it from VALUE." 93:options VALUE should be a list of valid members of the widget type.
1287 (let* ((values (custom-field-value field)) 94:group VALUE should be a customization group.
1288 (custom (custom-field-custom field)) 95 Add SYMBOL to that group.
1289 (from (custom-field-start field))
1290 (face-tag (custom-face-tag custom))
1291 current)
1292 (if face-tag
1293 (custom-put-text-property from (+ from (length (custom-tag custom)))
1294 'face (funcall face-tag field value)))
1295 (if original
1296 (custom-field-original-set field value))
1297 (while values
1298 (setq current (car values)
1299 values (cdr values))
1300 (if current
1301 (let* ((custom (custom-field-custom current))
1302 (match (custom-match custom value)))
1303 (setq value (cdr match))
1304 (custom-field-accept current (car match) original))))))
1305 96
1306(defun custom-group-insert (custom level) 97Read the section about customization in the emacs lisp manual for more
1307 "Insert field for CUSTOM at nesting LEVEL in customization buffer." 98information."
1308 (let* ((field (custom-field-create custom nil)) 99 `(eval-and-compile
1309 fields hidden 100 (custom-declare-variable (quote ,symbol) (quote ,value) ,doc ,@args)))
1310 (from (point))
1311 (compact (custom-compact custom))
1312 (tag (custom-tag custom))
1313 (face-tag (custom-face-tag custom)))
1314 (cond (face-tag (custom-text-insert tag))
1315 (tag (custom-tag-insert tag field)))
1316 (or compact (custom-documentation-insert custom))
1317 (or compact (custom-text-insert "\n"))
1318 (let ((data (custom-data custom)))
1319 (while data
1320 (setq fields (cons (custom-insert (car data) (if level (1+ level)))
1321 fields))
1322 (setq hidden (or (stringp (car data))
1323 (custom-property (car data) 'hidden)))
1324 (setq data (cdr data))
1325 (if data (custom-text-insert (cond (hidden "")
1326 (compact " ")
1327 (t "\n"))))))
1328 (if compact (custom-documentation-insert custom))
1329 (custom-field-value-set field (nreverse fields))
1330 (custom-field-move field from (point))
1331 field))
1332 101
1333(defun custom-choice-insert (custom level) 102;;; The `defface' Macro.
1334 "Insert field for CUSTOM at nesting LEVEL in customization buffer."
1335 (let* ((field (custom-field-create custom nil))
1336 (from (point)))
1337 (custom-text-insert "lars er en nisse")
1338 (custom-field-move field from (point))
1339 (custom-documentation-insert custom)
1340 (custom-field-reset field)
1341 field))
1342 103
1343(defun custom-choice-accept (field value &optional original) 104(defmacro defface (face spec doc &rest args)
1344 "Store a new value into field FIELD, taking it from VALUE." 105 "Declare FACE as a customizable face that defaults to SPEC.
1345 (let ((custom (custom-field-custom field)) 106FACE does not need to be quoted.
1346 (start (custom-field-start field))
1347 (end (custom-field-end field))
1348 (inhibit-read-only t)
1349 (before-change-functions nil)
1350 (after-change-functions nil)
1351 from)
1352 (cond (original
1353 (setq custom-modified-list (delq field custom-modified-list))
1354 (custom-field-original-set field value))
1355 ((equal value (custom-field-original field))
1356 (setq custom-modified-list (delq field custom-modified-list)))
1357 (t
1358 (add-to-list 'custom-modified-list field)))
1359 (custom-field-untouch (custom-field-value field))
1360 (delete-region start end)
1361 (goto-char start)
1362 (setq from (point))
1363 (insert-before-markers " ")
1364 (backward-char 1)
1365 (custom-category-set (point) (1+ (point)) 'custom-hidden-properties)
1366 (custom-tag-insert (custom-tag custom) field)
1367 (custom-text-insert ": ")
1368 (let ((data (custom-data custom))
1369 found begin)
1370 (while (and data (not found))
1371 (if (not (custom-valid (car data) value))
1372 (setq data (cdr data))
1373 (setq found (custom-insert (car data) nil))
1374 (setq data nil)))
1375 (if found
1376 ()
1377 (setq begin (point)
1378 found (custom-insert (custom-property custom 'none) nil))
1379 (custom-add-text-properties
1380 begin (point)
1381 (list rear-nonsticky t
1382 'face custom-field-uninitialized-face)))
1383 (or original
1384 (custom-field-original-set found (custom-field-original field)))
1385 (custom-field-accept found value original)
1386 (custom-field-value-set field found)
1387 (custom-field-move field from end))))
1388 107
1389(defun custom-choice-extract (custom field) 108Third argument DOC is the face documentation.
1390 "Extract child's value."
1391 (let ((value (custom-field-value field)))
1392 (custom-field-extract (custom-field-custom value) value)))
1393 109
1394(defun custom-choice-validate (custom field) 110If FACE has been set with `custom-set-face', set the face attributes
1395 "Validate child's value." 111as specified by that function, otherwise set the face attributes
1396 (let ((value (custom-field-value field)) 112according to SPEC.
1397 (custom (custom-field-custom field)))
1398 (if (or (eq value custom-nil)
1399 (eq (custom-field-custom value) (custom-property custom 'none)))
1400 (cons (custom-field-start field) "Make a choice")
1401 (custom-field-validate (custom-field-custom value) value))))
1402 113
1403(defun custom-choice-query (field) 114The remaining arguments should have the form
1404 "Choose a child."
1405 (let* ((custom (custom-field-custom field))
1406 (old (custom-field-custom (custom-field-value field)))
1407 (default (custom-prompt old))
1408 (tag (custom-prompt custom))
1409 (data (custom-data custom))
1410 current alist)
1411 (if (eq (length data) 2)
1412 (custom-field-accept field (custom-default (if (eq (nth 0 data) old)
1413 (nth 1 data)
1414 (nth 0 data))))
1415 (while data
1416 (setq current (car data)
1417 data (cdr data))
1418 (setq alist (cons (cons (custom-prompt current) current) alist)))
1419 (let ((answer (cond ((and (fboundp 'button-press-event-p)
1420 (fboundp 'popup-menu)
1421 (button-press-event-p last-input-event))
1422 (cdr (assoc (car (custom-x-really-popup-menu
1423 last-input-event tag
1424 (reverse alist)))
1425 alist)))
1426 ((listp last-input-event)
1427 (x-popup-menu last-input-event
1428 (list tag (cons "" (reverse alist)))))
1429 (t
1430 (let ((choice (completing-read (concat tag
1431 " (default "
1432 default
1433 "): ")
1434 alist nil t)))
1435 (if (or (null choice) (string-equal choice ""))
1436 (setq choice default))
1437 (cdr (assoc choice alist)))))))
1438 (if answer
1439 (custom-field-accept field (custom-default answer)))))))
1440 115
1441(defun custom-file-query (field) 116 [KEYWORD VALUE]...
1442 "Prompt for a file name"
1443 (let* ((value (custom-field-value field))
1444 (custom (custom-field-custom field))
1445 (valid (custom-valid custom value))
1446 (directory (custom-property custom 'directory))
1447 (default (and (not valid)
1448 (custom-property custom 'default-file)))
1449 (tag (custom-tag custom))
1450 (prompt (if default
1451 (concat tag " (" default "): ")
1452 (concat tag ": "))))
1453 (custom-field-accept field
1454 (if (custom-valid custom value)
1455 (read-file-name prompt
1456 (if (file-name-absolute-p value)
1457 ""
1458 directory)
1459 default nil value)
1460 (read-file-name prompt directory default)))))
1461 117
1462(defun custom-face-eval (custom value) 118The following KEYWORD's are defined:
1463 "Return non-nil if CUSTOM's VALUE needs to be evaluated."
1464 (not (symbolp value)))
1465 119
1466(defun custom-face-import (custom value) 120:group VALUE should be a customization group.
1467 "Modify CUSTOM's VALUE to match internal expectations." 121 Add FACE to that group.
1468 (let ((name (or (and (facep value) (symbol-name (face-name value)))
1469 (symbol-name value))))
1470 (list (if (string-match "\
1471custom-face-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)"
1472 name)
1473 (list 'custom-face-lookup
1474 (match-string 1 name)
1475 (match-string 2 name)
1476 (match-string 3 name)
1477 (intern (match-string 4 name))
1478 (intern (match-string 5 name))
1479 (intern (match-string 6 name)))
1480 value))))
1481 122
1482(defun custom-face-lookup (&optional fg bg stipple bold italic underline) 123SPEC should be an alist of the form ((DISPLAY ATTS)...).
1483 "Lookup or create a face with specified attributes."
1484 (let ((name (intern (format "custom-face-%s-%s-%s-%S-%S-%S"
1485 (or fg "default")
1486 (or bg "default")
1487 (or stipple "default")
1488 bold italic underline))))
1489 (if (and (custom-facep name)
1490 (fboundp 'make-face))
1491 ()
1492 (copy-face 'default name)
1493 (when (and fg
1494 (not (string-equal fg "default")))
1495 (condition-case ()
1496 (set-face-foreground name fg)
1497 (error nil)))
1498 (when (and bg
1499 (not (string-equal bg "default")))
1500 (condition-case ()
1501 (set-face-background name bg)
1502 (error nil)))
1503 (when (and stipple
1504 (not (string-equal stipple "default"))
1505 (not (eq stipple 'custom:asis))
1506 (fboundp 'set-face-stipple))
1507 (set-face-stipple name stipple))
1508 (when (and bold
1509 (not (eq bold 'custom:asis)))
1510 (condition-case ()
1511 (make-face-bold name)
1512 (error nil)))
1513 (when (and italic
1514 (not (eq italic 'custom:asis)))
1515 (condition-case ()
1516 (make-face-italic name)
1517 (error nil)))
1518 (when (and underline
1519 (not (eq underline 'custom:asis)))
1520 (condition-case ()
1521 (set-face-underline-p name t)
1522 (error nil))))
1523 name))
1524 124
1525(defun custom-face-hack (field value) 125ATTS is a list of face attributes and their values. The possible
1526 "Face that should be used for highlighting FIELD containing VALUE." 126attributes are defined in the variable `custom-face-attributes'.
1527 (let* ((custom (custom-field-custom field)) 127Alternatively, ATTS can be a face in which case the attributes of that
1528 (form (funcall (custom-property custom 'export) custom value)) 128face is used.
1529 (face (apply (car form) (cdr form))))
1530 (if (custom-facep face) face nil)))
1531 129
1532(defun custom-const-insert (custom level) 130The ATTS of the first entry in SPEC where the DISPLAY matches the
1533 "Insert field for CUSTOM at nesting LEVEL in customization buffer." 131frame should take effect in that frame. DISPLAY can either be the
1534 (let* ((field (custom-field-create custom custom-nil)) 132symbol t, which will match all frames, or an alist of the form
1535 (face (custom-field-face field)) 133\((REQ ITEM...)...)
1536 (from (point)))
1537 (custom-text-insert (custom-tag custom))
1538 (custom-add-text-properties from (point)
1539 (list 'face face
1540 rear-nonsticky t))
1541 (custom-documentation-insert custom)
1542 (custom-field-move field from (point))
1543 field))
1544 134
1545(defun custom-const-update (field) 135For the DISPLAY to match a FRAME, the REQ property of the frame must
1546 "Update face of FIELD." 136match one of the ITEM. The following REQ are defined:
1547 (let ((from (custom-field-start field))
1548 (custom (custom-field-custom field)))
1549 (custom-put-text-property from (+ from (length (custom-tag custom)))
1550 'face (custom-field-face field))))
1551 137
1552(defun custom-const-valid (custom value) 138`type' (the value of `window-system')
1553 "Non-nil if CUSTOM can validly have the value VALUE." 139 Should be one of `x' or `tty'.
1554 (equal (custom-default custom) value))
1555 140
1556(defun custom-const-face (field) 141`class' (the frame's color support)
1557 "Face used for a FIELD." 142 Should be one of `color', `grayscale', or `mono'.
1558 (custom-default (custom-field-custom field)))
1559 143
1560(defun custom-sexp-read (custom string) 144`background' (what color is used for the background text)
1561 "Read from CUSTOM an STRING." 145 Should be one of `light' or `dark'.
1562 (save-match-data
1563 (save-excursion
1564 (set-buffer (get-buffer-create " *Custom Scratch*"))
1565 (erase-buffer)
1566 (insert string)
1567 (goto-char (point-min))
1568 (prog1 (read (current-buffer))
1569 (or (looking-at
1570 (concat (regexp-quote (char-to-string
1571 (custom-padding custom)))
1572 "*\\'"))
1573 (error "Junk at end of expression"))))))
1574 146
1575(autoload 'pp-to-string "pp") 147Read the section about customization in the emacs lisp manual for more
148information."
149 `(custom-declare-face (quote ,face) ,spec ,doc ,@args))
1576 150
1577(defun custom-sexp-write (custom sexp) 151;;; The `defgroup' Macro.
1578 "Write CUSTOM SEXP as string."
1579 (let ((string (prin1-to-string sexp)))
1580 (if (<= (length string) (custom-width custom))
1581 string
1582 (setq string (pp-to-string sexp))
1583 (string-match "[ \t\n]*\\'" string)
1584 (concat "\n" (substring string 0 (match-beginning 0))))))
1585 152
1586(defun custom-string-read (custom string) 153(defun custom-declare-group (symbol members doc &rest args)
1587 "Read string by ignoring trailing padding characters." 154 "Like `defgroup', but SYMBOL is evaluated as a normal argument."
1588 (let ((last (length string)) 155 (put symbol 'custom-group (nconc members (get symbol 'custom-group)))
1589 (padding (custom-padding custom))) 156 (when doc
1590 (while (and (> last 0) 157 (put symbol 'group-documentation doc))
1591 (eq (aref string (1- last)) padding)) 158 (while args
1592 (setq last (1- last))) 159 (let ((arg (car args)))
1593 (substring string 0 last))) 160 (setq args (cdr args))
1594 161 (unless (symbolp arg)
1595(defun custom-string-write (custom string) 162 (error "Junk in args %S" args))
1596 "Write raw string." 163 (let ((keyword arg)
1597 string) 164 (value (car args)))
1598 165 (unless args
1599(defun custom-button-insert (custom level) 166 (error "Keyword %s is missing an argument" keyword))
1600 "Insert field for CUSTOM at nesting LEVEL in customization buffer." 167 (setq args (cdr args))
1601 (custom-tag-insert (concat "[" (custom-tag custom) "]") 168 (cond ((eq keyword :prefix)
1602 (custom-property custom 'query)) 169 (put symbol 'custom-prefix value))
1603 (custom-documentation-insert custom) 170 (t
1604 nil) 171 (custom-handle-keyword symbol keyword value
1605 172 'custom-group))))))
1606(defun custom-default-export (custom value) 173 (run-hooks 'custom-define-hook)
1607 ;; Convert CUSTOM's VALUE to external representation. 174 symbol)
1608 ;; See `custom-import'. 175
1609 (if (custom-eval custom value) 176(defmacro defgroup (symbol members doc &rest args)
1610 (eval (car (custom-quote custom value))) 177 "Declare SYMBOL as a customization group containing MEMBERS.
1611 value)) 178SYMBOL does not need to be quoted.
1612 179
1613(defun custom-default-quote (custom value) 180Third arg DOC is the group documentation.
1614 "Quote CUSTOM's VALUE if necessary." 181
1615 (list (if (and (not (custom-eval custom value)) 182MEMBERS should be an alist of the form ((NAME WIDGET)...) where
1616 (or (and (symbolp value) 183NAME is a symbol and WIDGET is a widget is a widget for editing that
1617 value 184symbol. Useful widgets are `custom-variable' for editing variables,
1618 (not (eq t value))) 185`custom-face' for edit faces, and `custom-group' for editing groups.
1619 (and (listp value) 186
1620 value 187The remaining arguments should have the form
1621 (not (memq (car value) '(quote function lambda)))))) 188
1622 (list 'quote value) 189 [KEYWORD VALUE]...
1623 value))) 190
1624 191The following KEYWORD's are defined:
1625(defun custom-default-initialize (custom) 192
1626 "Initialize `doc' and `default' entries in CUSTOM." 193:group VALUE should be a customization group.
1627 (let ((name (custom-name custom))) 194 Add SYMBOL to that group.
1628 (if (null name) 195
1629 () 196Read the section about customization in the emacs lisp manual for more
1630 (let ((default (custom-default custom)) 197information."
1631 (doc (custom-documentation custom)) 198 `(custom-declare-group (quote ,symbol) ,members ,doc ,@args))
1632 (vdoc (documentation-property name 'variable-documentation t))) 199
1633 (if doc 200(defun custom-add-to-group (group option widget)
1634 (or vdoc (put name 'variable-documentation doc)) 201 "To existing GROUP add a new OPTION of type WIDGET.
1635 (if vdoc (custom-property-set custom 'doc vdoc))) 202If there already is an entry for that option, overwrite it."
1636 (if (eq default custom-nil) 203 (let* ((members (get group 'custom-group))
1637 (if (boundp name) 204 (old (assq option members)))
1638 (custom-property-set custom 'default (symbol-value name))) 205 (if old
1639 (or (boundp name) 206 (setcar (cdr old) widget)
1640 (set name default))))))) 207 (put group 'custom-group (nconc members (list (list option widget)))))))
1641 208
1642(defun custom-default-insert (custom level) 209;;; Properties.
1643 "Insert field for CUSTOM at nesting LEVEL in customization buffer." 210
1644 (let ((field (custom-field-create custom custom-nil)) 211(defun custom-handle-all-keywords (symbol args type)
1645 (tag (custom-tag custom))) 212 "For customization option SYMBOL, handle keyword arguments ARGS.
1646 (if (null tag) 213Third argument TYPE is the custom option type."
1647 () 214 (while args
1648 (custom-tag-insert tag field) 215 (let ((arg (car args)))
1649 (custom-text-insert ": ")) 216 (setq args (cdr args))
1650 (custom-field-insert field) 217 (unless (symbolp arg)
1651 (custom-documentation-insert custom) 218 (error "Junk in args %S" args))
1652 field)) 219 (let ((keyword arg)
1653 220 (value (car args)))
1654(defun custom-default-accept (field value &optional original) 221 (unless args
1655 "Store a new value into field FIELD, taking it from VALUE." 222 (error "Keyword %s is missing an argument" keyword))
1656 (if original 223 (setq args (cdr args))
1657 (custom-field-original-set field value)) 224 (custom-handle-keyword symbol keyword value type)))))
1658 (custom-field-value-set field value) 225
1659 (custom-field-update field)) 226(defun custom-handle-keyword (symbol keyword value type)
1660 227 "For customization option SYMBOL, handle KEYWORD with VALUE.
1661(defun custom-default-apply (field) 228Fourth argument TYPE is the custom option type."
1662 "Apply any changes in FIELD since the last apply." 229 (cond ((eq keyword :group)
1663 (let* ((custom (custom-field-custom field)) 230 (custom-add-to-group value symbol type))
1664 (name (custom-name custom))) 231 ((eq keyword :link)
1665 (if (null name) 232 (custom-add-link symbol value))
1666 (error "This field cannot be applied alone")) 233 ((eq keyword :load)
1667 (custom-external-set name (custom-name-value name)) 234 (custom-add-load symbol value))
1668 (custom-field-reset field))) 235 ((eq keyword :tag)
1669 236 (put symbol 'custom-tag value))
1670(defun custom-default-reset (field)
1671 "Reset content of editing FIELD to `original'."
1672 (custom-field-accept field (custom-field-original field) t))
1673
1674(defun custom-default-factory-reset (field)
1675 "Reset content of editing FIELD to `default'."
1676 (let* ((custom (custom-field-custom field))
1677 (default (car (custom-import custom (custom-default custom)))))
1678 (or (eq default custom-nil)
1679 (custom-field-accept field default nil))))
1680
1681(defun custom-default-query (field)
1682 "Prompt for a FIELD"
1683 (let* ((custom (custom-field-custom field))
1684 (value (custom-field-value field))
1685 (initial (custom-write custom value))
1686 (prompt (concat (custom-prompt custom) ": ")))
1687 (custom-field-accept field
1688 (custom-read custom
1689 (if (custom-valid custom value)
1690 (read-string prompt (cons initial 1))
1691 (read-string prompt))))))
1692
1693(defun custom-default-match (custom values)
1694 "Match CUSTOM with VALUES."
1695 values)
1696
1697(defun custom-default-extract (custom field)
1698 "Extract CUSTOM's content in FIELD."
1699 (list (custom-field-value field)))
1700
1701(defun custom-default-validate (custom field)
1702 "Validate FIELD."
1703 (let ((value (custom-field-value field))
1704 (start (custom-field-start field)))
1705 (cond ((eq value custom-nil)
1706 (cons start "Uninitialized field"))
1707 ((and (consp value) (eq (car value) custom-invalid))
1708 (cons start "Unparsable field content"))
1709 ((custom-valid custom value)
1710 nil)
1711 (t
1712 (cons start "Wrong type of field content")))))
1713
1714(defun custom-default-face (field)
1715 "Face used for a FIELD."
1716 (let ((value (custom-field-value field)))
1717 (cond ((eq value custom-nil)
1718 custom-field-uninitialized-face)
1719 ((not (custom-valid (custom-field-custom field) value))
1720 custom-field-invalid-face)
1721 ((not (equal (custom-field-original field) value))
1722 custom-field-modified-face)
1723 (t
1724 custom-field-face))))
1725
1726(defun custom-default-update (field)
1727 "Update the content of FIELD."
1728 (let ((inhibit-point-motion-hooks t)
1729 (before-change-functions nil)
1730 (after-change-functions nil)
1731 (start (custom-field-start field))
1732 (end (custom-field-end field))
1733 (pos (point)))
1734 ;; Keep track of how many modified fields we have.
1735 (cond ((equal (custom-field-value field) (custom-field-original field))
1736 (setq custom-modified-list (delq field custom-modified-list)))
1737 ((memq field custom-modified-list))
1738 (t
1739 (setq custom-modified-list (cons field custom-modified-list))))
1740 ;; Update the field.
1741 (goto-char end)
1742 (insert-before-markers " ")
1743 (delete-region start (1- end))
1744 (goto-char start)
1745 (custom-field-insert field)
1746 (goto-char end)
1747 (delete-char 1)
1748 (goto-char pos)
1749 (and (<= start pos)
1750 (<= pos end)
1751 (custom-field-enter field))))
1752
1753;;; Create Buffer:
1754;;
1755;; Public functions to create a customization buffer and to insert
1756;; various forms of text, fields, and buttons in it.
1757
1758(defun customize ()
1759 "Customize GNU Emacs.
1760Create a *Customize* buffer with editable customization information
1761about GNU Emacs."
1762 (interactive)
1763 (custom-buffer-create "*Customize*")
1764 (custom-reset-all))
1765
1766(defun custom-buffer-create (name &optional custom types set get save)
1767 "Create a customization buffer named NAME.
1768If the optional argument CUSTOM is non-nil, use that as the custom declaration.
1769If the optional argument TYPES is non-nil, use that as the local types.
1770If the optional argument SET is non-nil, use that to set external data.
1771If the optional argument GET is non-nil, use that to get external data.
1772If the optional argument SAVE is non-nil, use that for saving changes."
1773 (switch-to-buffer name)
1774 (buffer-disable-undo (current-buffer))
1775 (custom-mode)
1776 (setq custom-local-type-properties types)
1777 (if (null custom)
1778 ()
1779 (make-local-variable 'custom-data)
1780 (setq custom-data custom))
1781 (if (null set)
1782 ()
1783 (make-local-variable 'custom-external-set)
1784 (setq custom-external-set set))
1785 (if (null get)
1786 ()
1787 (make-local-variable 'custom-external)
1788 (setq custom-external get))
1789 (if (null save)
1790 ()
1791 (make-local-variable 'custom-save)
1792 (setq custom-save save))
1793 (let ((inhibit-point-motion-hooks t)
1794 (before-change-functions nil)
1795 (after-change-functions nil))
1796 (erase-buffer)
1797 (insert "\n")
1798 (goto-char (point-min))
1799 (custom-text-insert "This is a customization buffer.\n")
1800 (custom-help-insert "\n")
1801 (custom-help-button 'custom-forward-field)
1802 (custom-help-button 'custom-backward-field)
1803 (custom-help-button 'custom-enter-value)
1804 (custom-help-button 'custom-field-factory-reset)
1805 (custom-help-button 'custom-field-reset)
1806 (custom-help-button 'custom-field-apply)
1807 (custom-help-button 'custom-save-and-exit)
1808 (custom-help-button 'custom-toggle-documentation)
1809 (custom-help-insert "\nClick mouse-2 on any button to activate it.\n")
1810 (custom-text-insert "\n")
1811 (custom-insert custom-data 0)
1812 (goto-char (point-min))))
1813
1814(defun custom-insert (custom level)
1815 "Insert custom declaration CUSTOM in current buffer at level LEVEL."
1816 (if (stringp custom)
1817 (progn
1818 (custom-text-insert custom)
1819 nil)
1820 (and level (null (custom-property custom 'header))
1821 (setq level nil))
1822 (and level
1823 (> level 0)
1824 (custom-text-insert (concat "\n" (make-string level ?*) " ")))
1825 (let ((field (funcall (custom-property custom 'insert) custom level)))
1826 (custom-name-enter (custom-name custom) field)
1827 field)))
1828
1829(defun custom-text-insert (text)
1830 "Insert TEXT in current buffer."
1831 (insert text))
1832
1833(defun custom-tag-insert (tag field &optional data)
1834 "Insert TAG for FIELD in current buffer."
1835 (let ((from (point)))
1836 (insert tag)
1837 (custom-category-set from (point) 'custom-button-properties)
1838 (custom-put-text-property from (point) 'custom-tag field)
1839 (if data
1840 (custom-add-text-properties from (point) (list 'custom-data data)))))
1841
1842(defun custom-documentation-insert (custom &rest ignore)
1843 "Insert documentation from CUSTOM in current buffer."
1844 (let ((doc (custom-documentation custom)))
1845 (if (null doc)
1846 ()
1847 (custom-help-insert "\n" doc))))
1848
1849(defun custom-help-insert (&rest args)
1850 "Insert ARGS as documentation text."
1851 (let ((from (point)))
1852 (apply 'insert args)
1853 (custom-category-set from (point) 'custom-documentation-properties)))
1854
1855(defun custom-help-button (command)
1856 "Describe how to execute COMMAND."
1857 (let ((from (point)))
1858 (insert "`" (key-description (where-is-internal command nil t)) "'")
1859 (custom-set-text-properties from (point)
1860 (list 'face custom-button-face
1861 mouse-face custom-mouse-face
1862 'custom-jump t ;Make TAB jump over it.
1863 'custom-tag command
1864 'start-open t
1865 'end-open t))
1866 (custom-category-set from (point) 'custom-documentation-properties))
1867 (custom-help-insert ": " (custom-first-line (documentation command)) "\n"))
1868
1869;;; Mode:
1870;;
1871;; The Customization major mode and interactive commands.
1872
1873(defvar custom-mode-map nil
1874 "Keymap for Custom Mode.")
1875(if custom-mode-map
1876 nil
1877 (setq custom-mode-map (make-sparse-keymap))
1878 (define-key custom-mode-map (if (string-match "XEmacs" emacs-version) [button2] [mouse-2]) 'custom-push-button)
1879 (define-key custom-mode-map "\t" 'custom-forward-field)
1880 (define-key custom-mode-map "\M-\t" 'custom-backward-field)
1881 (define-key custom-mode-map "\r" 'custom-enter-value)
1882 (define-key custom-mode-map "\C-k" 'custom-kill-line)
1883 (define-key custom-mode-map "\C-c\C-r" 'custom-field-reset)
1884 (define-key custom-mode-map "\C-c\M-\C-r" 'custom-reset-all)
1885 (define-key custom-mode-map "\C-c\C-z" 'custom-field-factory-reset)
1886 (define-key custom-mode-map "\C-c\M-\C-z" 'custom-factory-reset-all)
1887 (define-key custom-mode-map "\C-c\C-a" 'custom-field-apply)
1888 (define-key custom-mode-map "\C-c\M-\C-a" 'custom-apply-all)
1889 (define-key custom-mode-map "\C-c\C-c" 'custom-save-and-exit)
1890 (define-key custom-mode-map "\C-c\C-d" 'custom-toggle-documentation))
1891
1892;; C-c keymap ideas: C-a field-beginning, C-e field-end, C-f
1893;; forward-field, C-b backward-field, C-n next-field, C-p
1894;; previous-field, ? describe-field.
1895
1896(defun custom-mode ()
1897 "Major mode for doing customizations.
1898
1899\\{custom-mode-map}"
1900 (kill-all-local-variables)
1901 (setq major-mode 'custom-mode
1902 mode-name "Custom")
1903 (use-local-map custom-mode-map)
1904 (make-local-variable 'before-change-functions)
1905 (setq before-change-functions '(custom-before-change))
1906 (make-local-variable 'after-change-functions)
1907 (setq after-change-functions '(custom-after-change))
1908 (if (not (fboundp 'make-local-hook))
1909 ;; Emacs 19.28 and earlier.
1910 (add-hook 'post-command-hook
1911 (lambda ()
1912 (if (eq major-mode 'custom-mode)
1913 (custom-post-command))))
1914 ;; Emacs 19.29.
1915 (make-local-hook 'post-command-hook)
1916 (add-hook 'post-command-hook 'custom-post-command nil t)))
1917
1918(defun custom-forward-field (arg)
1919 "Move point to the next field or button.
1920With optional ARG, move across that many fields."
1921 (interactive "p")
1922 (while (> arg 0)
1923 (let ((next (if (get-text-property (point) 'custom-tag)
1924 (next-single-property-change (point) 'custom-tag)
1925 (point))))
1926 (setq next (or (next-single-property-change next 'custom-tag)
1927 (next-single-property-change (point-min) 'custom-tag)))
1928 (if next
1929 (goto-char next)
1930 (error "No customization fields in this buffer.")))
1931 (or (get-text-property (point) 'custom-jump)
1932 (setq arg (1- arg))))
1933 (while (< arg 0)
1934 (let ((previous (if (get-text-property (1- (point)) 'custom-tag)
1935 (previous-single-property-change (point) 'custom-tag)
1936 (point))))
1937 (setq previous
1938 (or (previous-single-property-change previous 'custom-tag)
1939 (previous-single-property-change (point-max) 'custom-tag)))
1940 (if previous
1941 (goto-char previous)
1942 (error "No customization fields in this buffer.")))
1943 (or (get-text-property (1- (point)) 'custom-jump)
1944 (setq arg (1+ arg)))))
1945
1946(defun custom-backward-field (arg)
1947 "Move point to the previous field or button.
1948With optional ARG, move across that many fields."
1949 (interactive "p")
1950 (custom-forward-field (- arg)))
1951
1952(defun custom-toggle-documentation (&optional arg)
1953 "Toggle display of documentation text.
1954If the optional argument is non-nil, show text iff the argument is positive."
1955 (interactive "P")
1956 (let ((hide (or (and (null arg)
1957 (null (custom-category-get
1958 'custom-documentation-properties 'invisible)))
1959 (<= (prefix-numeric-value arg) 0))))
1960 (custom-category-put 'custom-documentation-properties 'invisible hide)
1961 (custom-category-put 'custom-documentation-properties intangible hide))
1962 (redraw-display))
1963
1964(defun custom-enter-value (field data)
1965 "Enter value for current customization field or push button."
1966 (interactive (list (get-text-property (point) 'custom-tag)
1967 (get-text-property (point) 'custom-data)))
1968 (cond (data
1969 (funcall field data))
1970 ((eq field 'custom-enter-value)
1971 (error "Don't be silly"))
1972 ((and (symbolp field) (fboundp field))
1973 (call-interactively field))
1974 (field
1975 (custom-field-query field))
1976 (t
1977 (message "Nothing to enter here"))))
1978
1979(defun custom-kill-line ()
1980 "Kill to end of field or end of line, whichever is first."
1981 (interactive)
1982 (let ((field (get-text-property (point) 'custom-field))
1983 (newline (save-excursion (search-forward "\n")))
1984 (next (next-single-property-change (point) 'custom-field)))
1985 (if (and field (> newline next))
1986 (kill-region (point) next)
1987 (call-interactively 'kill-line))))
1988
1989(defun custom-push-button (event)
1990 "Activate button below mouse pointer."
1991 (interactive "@e")
1992 (let* ((pos (event-point event))
1993 (field (get-text-property pos 'custom-field))
1994 (tag (get-text-property pos 'custom-tag))
1995 (data (get-text-property pos 'custom-data)))
1996 (cond (data
1997 (funcall tag data))
1998 ((and (symbolp tag) (fboundp tag))
1999 (call-interactively tag))
2000 (field
2001 (call-interactively (lookup-key global-map (this-command-keys))))
2002 (tag
2003 (custom-enter-value tag data))
2004 (t
2005 (error "Nothing to click on here.")))))
2006
2007(defun custom-reset-all ()
2008 "Undo any changes since the last apply in all fields."
2009 (interactive (and custom-modified-list
2010 (not (y-or-n-p "Discard all changes? "))
2011 (error "Reset aborted")))
2012 (let ((all custom-name-fields)
2013 current field)
2014 (while all
2015 (setq current (car all)
2016 field (cdr current)
2017 all (cdr all))
2018 (custom-field-reset field))))
2019
2020(defun custom-field-reset (field)
2021 "Undo any changes in FIELD since the last apply."
2022 (interactive (list (or (get-text-property (point) 'custom-field)
2023 (get-text-property (point) 'custom-tag))))
2024 (if (arrayp field)
2025 (let* ((custom (custom-field-custom field))
2026 (name (custom-name custom)))
2027 (save-excursion
2028 (if name
2029 (custom-field-original-set
2030 field (car (custom-import custom (custom-external name)))))
2031 (if (not (custom-valid custom (custom-field-original field)))
2032 (error "This field cannot be reset alone")
2033 (funcall (custom-property custom 'reset) field)
2034 (funcall (custom-property custom 'synchronize) field))))))
2035
2036(defun custom-factory-reset-all ()
2037 "Reset all field to their default values."
2038 (interactive (and custom-modified-list
2039 (not (y-or-n-p "Discard all changes? "))
2040 (error "Reset aborted")))
2041 (let ((all custom-name-fields)
2042 field)
2043 (while all
2044 (setq field (cdr (car all))
2045 all (cdr all))
2046 (custom-field-factory-reset field))))
2047
2048(defun custom-field-factory-reset (field)
2049 "Reset FIELD to its default value."
2050 (interactive (list (or (get-text-property (point) 'custom-field)
2051 (get-text-property (point) 'custom-tag))))
2052 (if (arrayp field)
2053 (save-excursion
2054 (funcall (custom-property (custom-field-custom field) 'factory-reset)
2055 field))))
2056
2057(defun custom-apply-all ()
2058 "Apply any changes since the last reset in all fields."
2059 (interactive (if custom-modified-list
2060 nil
2061 (error "No changes to apply.")))
2062 (custom-field-parse custom-field-last)
2063 (let ((all custom-name-fields)
2064 field)
2065 (while all
2066 (setq field (cdr (car all))
2067 all (cdr all))
2068 (let ((error (custom-field-validate (custom-field-custom field) field)))
2069 (if (null error)
2070 ()
2071 (goto-char (car error))
2072 (error (cdr error))))))
2073 (let ((all custom-name-fields)
2074 field)
2075 (while all
2076 (setq field (cdr (car all))
2077 all (cdr all))
2078 (custom-field-apply field))))
2079
2080(defun custom-field-apply (field)
2081 "Apply any changes in FIELD since the last apply."
2082 (interactive (list (or (get-text-property (point) 'custom-field)
2083 (get-text-property (point) 'custom-tag))))
2084 (custom-field-parse custom-field-last)
2085 (if (arrayp field)
2086 (let* ((custom (custom-field-custom field))
2087 (error (custom-field-validate custom field)))
2088 (if error
2089 (error (cdr error)))
2090 (funcall (custom-property custom 'apply) field))))
2091
2092(defun custom-toggle-hide (&rest ignore)
2093 "Hide or show entry."
2094 (interactive)
2095 (error "This button is not yet implemented"))
2096
2097(defun custom-save-and-exit ()
2098 "Save and exit customization buffer."
2099 (interactive "@")
2100 (save-excursion
2101 (funcall custom-save))
2102 (kill-buffer (current-buffer)))
2103
2104(defun custom-save ()
2105 "Save customization information."
2106 (interactive)
2107 (custom-apply-all)
2108 (let ((new custom-name-fields))
2109 (set-buffer (find-file-noselect custom-file))
2110 (goto-char (point-min))
2111 (save-excursion
2112 (let ((old (condition-case nil
2113 (read (current-buffer))
2114 (end-of-file (append '(setq custom-dummy
2115 'custom-dummy) ())))))
2116 (or (eq (car old) 'setq)
2117 (error "Invalid customization file: %s" custom-file))
2118 (while new
2119 (let* ((field (cdr (car new)))
2120 (custom (custom-field-custom field))
2121 (value (custom-field-original field))
2122 (default (car (custom-import custom (custom-default custom))))
2123 (name (car (car new))))
2124 (setq new (cdr new))
2125 (custom-assert '(eq name (custom-name custom)))
2126 (if (equal default value)
2127 (setcdr old (custom-plist-delq name (cdr old)))
2128 (setcdr old (plist-put (cdr old) name
2129 (car (custom-quote custom value)))))))
2130 (erase-buffer)
2131 (insert ";; " custom-file "\
2132 --- Automatically generated customization information.
2133;;
2134;; Feel free to edit by hand, but the entire content should consist of
2135;; a single setq. Any other lisp expressions will confuse the
2136;; automatic configuration engine.
2137
2138\(setq ")
2139 (setq old (cdr old))
2140 (while old
2141 (prin1 (car old) (current-buffer))
2142 (setq old (cdr old))
2143 (insert " ")
2144 (pp (car old) (current-buffer))
2145 (setq old (cdr old))
2146 (if old (insert "\n ")))
2147 (insert ")\n")
2148 (save-buffer)
2149 (kill-buffer (current-buffer))))))
2150
2151(defun custom-load ()
2152 "Save customization information."
2153 (interactive (and custom-modified-list
2154 (not (equal (list (custom-name-field 'custom-file))
2155 custom-modified-list))
2156 (not (y-or-n-p "Discard all changes? "))
2157 (error "Load aborted")))
2158 (load-file (custom-name-value 'custom-file))
2159 (custom-reset-all))
2160
2161;;; Field Editing:
2162;;
2163;; Various internal functions for implementing the direct editing of
2164;; fields in the customization buffer.
2165
2166(defun custom-field-untouch (field)
2167 ;; Remove FIELD and its children from `custom-modified-list'.
2168 (setq custom-modified-list (delq field custom-modified-list))
2169 (if (arrayp field)
2170 (let ((value (custom-field-value field)))
2171 (cond ((null (custom-data (custom-field-custom field))))
2172 ((arrayp value)
2173 (custom-field-untouch value))
2174 ((listp value)
2175 (mapcar 'custom-field-untouch value))))))
2176
2177
2178(defun custom-field-insert (field)
2179 ;; Insert editing FIELD in current buffer.
2180 (let ((from (point))
2181 (custom (custom-field-custom field))
2182 (value (custom-field-value field)))
2183 (insert (custom-write custom value))
2184 (insert-char (custom-padding custom)
2185 (- (custom-width custom) (- (point) from)))
2186 (custom-field-move field from (point))
2187 (custom-set-text-properties
2188 from (point)
2189 (list 'custom-field field
2190 'custom-tag field
2191 'face (custom-field-face field)
2192 'start-open t
2193 'end-open t))))
2194
2195(defun custom-field-read (field)
2196 ;; Read the screen content of FIELD.
2197 (custom-read (custom-field-custom field)
2198 (custom-buffer-substring-no-properties (custom-field-start field)
2199 (custom-field-end field))))
2200
2201;; Fields are shown in a special `active' face when point is inside
2202;; it. You activate the field by moving point inside (entering) it
2203;; and deactivate the field by moving point outside (leaving) it.
2204
2205(defun custom-field-leave (field)
2206 ;; Deactivate FIELD.
2207 (let ((before-change-functions nil)
2208 (after-change-functions nil))
2209 (custom-put-text-property (custom-field-start field) (custom-field-end field)
2210 'face (custom-field-face field))))
2211
2212(defun custom-field-enter (field)
2213 ;; Activate FIELD.
2214 (let* ((start (custom-field-start field))
2215 (end (custom-field-end field))
2216 (custom (custom-field-custom field))
2217 (padding (custom-padding custom))
2218 (before-change-functions nil)
2219 (after-change-functions nil))
2220 (or (eq this-command 'self-insert-command)
2221 (let ((pos end))
2222 (while (and (< start pos)
2223 (eq (char-after (1- pos)) padding))
2224 (setq pos (1- pos)))
2225 (if (< pos (point))
2226 (goto-char pos))))
2227 (custom-put-text-property start end 'face custom-field-active-face)))
2228
2229(defun custom-field-resize (field)
2230 ;; Resize FIELD after change.
2231 (let* ((custom (custom-field-custom field))
2232 (begin (custom-field-start field))
2233 (end (custom-field-end field))
2234 (pos (point))
2235 (padding (custom-padding custom))
2236 (width (custom-width custom))
2237 (size (- end begin)))
2238 (cond ((< size width)
2239 (goto-char end)
2240 (if (fboundp 'insert-before-markers-and-inherit)
2241 ;; Emacs 19.
2242 (insert-before-markers-and-inherit
2243 (make-string (- width size) padding))
2244 ;; XEmacs: BUG: Doesn't work!
2245 (insert-before-markers (make-string (- width size) padding)))
2246 (goto-char pos))
2247 ((> size width)
2248 (let ((start (if (and (< (+ begin width) pos) (<= pos end))
2249 pos
2250 (+ begin width))))
2251 (goto-char end)
2252 (while (and (< start (point)) (= (preceding-char) padding))
2253 (backward-delete-char 1))
2254 (goto-char pos))))))
2255
2256(defvar custom-field-changed nil)
2257;; List of fields changed on the screen but whose VALUE attribute has
2258;; not yet been updated to reflect the new screen content.
2259(make-variable-buffer-local 'custom-field-changed)
2260
2261(defun custom-field-parse (field)
2262 ;; Parse FIELD content iff changed.
2263 (if (memq field custom-field-changed)
2264 (progn
2265 (setq custom-field-changed (delq field custom-field-changed))
2266 (custom-field-value-set field (custom-field-read field))
2267 (custom-field-update field))))
2268
2269(defun custom-post-command ()
2270 ;; Keep track of their active field.
2271 (custom-assert '(eq major-mode 'custom-mode))
2272 (let ((field (custom-field-property (point))))
2273 (if (eq field custom-field-last)
2274 (if (memq field custom-field-changed)
2275 (custom-field-resize field))
2276 (custom-field-parse custom-field-last)
2277 (if custom-field-last
2278 (custom-field-leave custom-field-last))
2279 (if field
2280 (custom-field-enter field))
2281 (setq custom-field-last field))
2282 (set-buffer-modified-p (or custom-modified-list
2283 custom-field-changed))))
2284
2285(defvar custom-field-was nil)
2286;; The custom data before the change.
2287(make-variable-buffer-local 'custom-field-was)
2288
2289(defun custom-before-change (begin end)
2290 ;; Check that we the modification is allowed.
2291 (if (not (eq major-mode 'custom-mode))
2292 (message "Aargh! Why is custom-before-change called here?")
2293 (let ((from (custom-field-property begin))
2294 (to (custom-field-property end)))
2295 (cond ((or (null from) (null to))
2296 (error "You can only modify the fields"))
2297 ((not (eq from to))
2298 (error "Changes must be limited to a single field."))
2299 (t
2300 (setq custom-field-was from))))))
2301
2302(defun custom-after-change (begin end length)
2303 ;; Keep track of field content.
2304 (if (not (eq major-mode 'custom-mode))
2305 (message "Aargh! Why is custom-after-change called here?")
2306 (let ((field custom-field-was))
2307 (custom-assert '(prog1 field (setq custom-field-was nil)))
2308 ;; Prevent mixing fields properties.
2309 (custom-put-text-property begin end 'custom-field field)
2310 ;; Update the field after modification.
2311 (if (eq (custom-field-property begin) field)
2312 (let ((field-end (custom-field-end field)))
2313 (if (> end field-end)
2314 (set-marker field-end end))
2315 (add-to-list 'custom-field-changed field))
2316 ;; We deleted the entire field, reinsert it.
2317 (custom-assert '(eq begin end))
2318 (save-excursion
2319 (goto-char begin)
2320 (custom-field-value-set field
2321 (custom-read (custom-field-custom field) ""))
2322 (custom-field-insert field))))))
2323
2324(defun custom-field-property (pos)
2325 ;; The `custom-field' text property valid for POS.
2326 (or (get-text-property pos 'custom-field)
2327 (and (not (eq pos (point-min)))
2328 (get-text-property (1- pos) 'custom-field))))
2329
2330;;; Generic Utilities:
2331;;
2332;; Some utility functions that are not really specific to custom.
2333
2334(defun custom-assert (expr)
2335 "Assert that EXPR evaluates to non-nil at this point"
2336 (or (eval expr)
2337 (error "Assertion failed: %S" expr)))
2338
2339(defun custom-first-line (string)
2340 "Return the part of STRING before the first newline."
2341 (let ((pos 0)
2342 (len (length string)))
2343 (while (and (< pos len) (not (eq (aref string pos) ?\n)))
2344 (setq pos (1+ pos)))
2345 (if (eq pos len)
2346 string
2347 (substring string 0 pos))))
2348
2349(defun custom-insert-before (list old new)
2350 "In LIST insert before OLD a NEW element."
2351 (cond ((null list)
2352 (list new))
2353 ((null old)
2354 (nconc list (list new)))
2355 ((eq old (car list))
2356 (cons new list))
2357 (t 237 (t
2358 (let ((list list)) 238 (error "Unknown keyword %s" symbol))))
2359 (while (not (eq old (car (cdr list)))) 239
2360 (setq list (cdr list)) 240(defun custom-add-option (symbol option)
2361 (custom-assert '(cdr list))) 241 "To the variable SYMBOL add OPTION.
2362 (setcdr list (cons new (cdr list)))) 242
2363 list))) 243If SYMBOL is a hook variable, OPTION should be a hook member.
2364 244For other types variables, the effect is undefined."
2365(defun custom-strip-padding (string padding) 245 (let ((options (get symbol 'custom-options)))
2366 "Remove padding from STRING." 246 (unless (member option options)
2367 (let ((regexp (concat (regexp-quote (char-to-string padding)) "+"))) 247 (put symbol 'custom-options (cons option options)))))
2368 (while (string-match regexp string) 248
2369 (setq string (concat (substring string 0 (match-beginning 0)) 249(defun custom-add-link (symbol widget)
2370 (substring string (match-end 0)))))) 250 "To the custom option SYMBOL add the link WIDGET."
2371 string) 251 (let ((links (get symbol 'custom-links)))
2372 252 (unless (member widget links)
2373(defun custom-plist-memq (prop plist) 253 (put symbol 'custom-links (cons widget links)))))
2374 "Return non-nil if PROP is a property of PLIST. Comparison done with EQ." 254
2375 (let (result) 255(defun custom-add-load (symbol load)
2376 (while plist 256 "To the custom option SYMBOL add the dependency LOAD.
2377 (if (eq (car plist) prop) 257LOAD should be either a library file name, or a feature name."
2378 (setq result plist 258 (let ((loads (get symbol 'custom-loads)))
2379 plist nil) 259 (unless (member load loads)
2380 (setq plist (cdr (cdr plist))))) 260 (put symbol 'custom-loads (cons load loads)))))
2381 result)) 261
2382 262;;; Initializing.
2383(defun custom-plist-delq (prop plist) 263
2384 "Delete property PROP from property list PLIST." 264(defun custom-set-variables (&rest args)
2385 (while (eq (car plist) prop) 265 "Initialize variables according to user preferences.
2386 (setq plist (cdr (cdr plist)))) 266
2387 (let ((list plist) 267The arguments should be a list where each entry has the form:
2388 (next (cdr (cdr plist)))) 268
2389 (while next 269 (SYMBOL VALUE [NOW])
2390 (if (eq (car next) prop) 270
2391 (progn 271The unevaluated VALUE is stored as the saved value for SYMBOL.
2392 (setq next (cdr (cdr next))) 272If NOW is present and non-nil, VALUE is also evaluated and bound as
2393 (setcdr (cdr list) next)) 273the default value for the SYMBOL."
2394 (setq list next 274 (while args
2395 next (cdr (cdr next)))))) 275 (let ((entry (car args)))
2396 plist) 276 (if (listp entry)
277 (let ((symbol (nth 0 entry))
278 (value (nth 1 entry))
279 (now (nth 2 entry)))
280 (put symbol 'saved-value (list value))
281 (when now
282 (put symbol 'force-value t)
283 (set-default symbol (eval value)))
284 (setq args (cdr args)))
285 ;; Old format, a plist of SYMBOL VALUE pairs.
286 (let ((symbol (nth 0 args))
287 (value (nth 1 args)))
288 (put symbol 'saved-value (list value)))
289 (setq args (cdr (cdr args)))))))
290
291;;; Meta Customization
292
293(defcustom custom-define-hook nil
294 "Hook called after defining each customize option."
295 :group 'customize
296 :type 'hook)
297
298;;; Menu support
299
300(defconst custom-help-menu
301 `("Customize"
302 ,(if (string-match "XEmacs" emacs-version)
303 '("Emacs" :filter (lambda (&rest junk)
304 (cdr (custom-menu-create 'emacs))))
305 ["Update menu..." custom-menu-update t])
306 ["Group..." customize t]
307 ["Variable..." customize-variable t]
308 ["Face..." customize-face t]
309 ["Saved..." customize-customized t]
310 ["Apropos..." customize-apropos t])
311 "Customize menu")
312
313(defun custom-menu-reset ()
314 "Reset customize menu."
315 (remove-hook 'custom-define-hook 'custom-menu-reset)
316 (if (string-match "XEmacs" emacs-version)
317 (when (fboundp 'add-submenu)
318 (add-submenu '("Options") custom-help-menu))
319 (define-key global-map [menu-bar help-menu customize-menu]
320 (cons (car custom-help-menu)
321 (easy-menu-create-keymaps (car custom-help-menu)
322 (cdr custom-help-menu))))))
2397 323
2398;;; Meta Customization: 324(if (string-match "XEmacs" emacs-version)
2399 325 (autoload 'custom-menu-create "cus-edit")
2400(custom-declare '() 326 (custom-menu-reset))
2401 '((tag . "Meta Customization")
2402 (doc . "Customization of the customization support.")
2403 (type . group)
2404 (data ((type . face-doc))
2405 ((tag . "Button Face")
2406 (default . bold)
2407 (doc . "Face used for tags in customization buffers.")
2408 (name . custom-button-face)
2409 (synchronize . (lambda (f)
2410 (custom-category-put 'custom-button-properties
2411 'face custom-button-face)))
2412 (type . face))
2413 ((tag . "Mouse Face")
2414 (default . highlight)
2415 (doc . "\
2416Face used when mouse is above a button in customization buffers.")
2417 (name . custom-mouse-face)
2418 (synchronize . (lambda (f)
2419 (custom-category-put 'custom-button-properties
2420 mouse-face
2421 custom-mouse-face)))
2422 (type . face))
2423 ((tag . "Field Face")
2424 (default . italic)
2425 (doc . "Face used for customization fields.")
2426 (name . custom-field-face)
2427 (type . face))
2428 ((tag . "Uninitialized Face")
2429 (default . modeline)
2430 (doc . "Face used for uninitialized customization fields.")
2431 (name . custom-field-uninitialized-face)
2432 (type . face))
2433 ((tag . "Invalid Face")
2434 (default . highlight)
2435 (doc . "\
2436Face used for customization fields containing invalid data.")
2437 (name . custom-field-invalid-face)
2438 (type . face))
2439 ((tag . "Modified Face")
2440 (default . bold-italic)
2441 (doc . "Face used for modified customization fields.")
2442 (name . custom-field-modified-face)
2443 (type . face))
2444 ((tag . "Active Face")
2445 (default . underline)
2446 (doc . "\
2447Face used for customization fields while they are being edited.")
2448 (name . custom-field-active-face)
2449 (type . face)))))
2450
2451;; custom.el uses two categories.
2452
2453(custom-category-create 'custom-documentation-properties)
2454(custom-category-put 'custom-documentation-properties rear-nonsticky t)
2455
2456(custom-category-create 'custom-button-properties)
2457(custom-category-put 'custom-button-properties 'face custom-button-face)
2458(custom-category-put 'custom-button-properties mouse-face custom-mouse-face)
2459(custom-category-put 'custom-button-properties rear-nonsticky t)
2460
2461(custom-category-create 'custom-hidden-properties)
2462(custom-category-put 'custom-hidden-properties 'invisible
2463 (not (string-match "XEmacs" emacs-version)))
2464(custom-category-put 'custom-hidden-properties intangible t)
2465 327
2466(and init-file-user ; Don't load any init file if -q was used. 328;;; The End.
2467 (file-readable-p custom-file)
2468 (load-file custom-file))
2469 329
2470(provide 'custom) 330(provide 'custom)
2471 331
2472;;; custom.el ends here 332;; custom.el ends here
diff --git a/lisp/wid-browse.el b/lisp/wid-browse.el
new file mode 100644
index 00000000000..d90836c05c4
--- /dev/null
+++ b/lisp/wid-browse.el
@@ -0,0 +1,232 @@
1;;; wid-browse.el --- Functions for browsing widgets.
2;;
3;; Copyright (C) 1997 Free Software Foundation, Inc.
4;;
5;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6;; Keywords: extensions
7;; Version: 1.71
8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
9
10;;; Commentary:
11;;
12;; Widget browser. See `widget.el'.
13
14;;; Code:
15
16(require 'easymenu)
17(require 'custom)
18(require 'wid-edit)
19(require 'cl)
20
21(defgroup widget-browse nil
22 "Customization support for browsing widgets."
23 :group 'widgets)
24
25;;; The Mode.
26
27(defvar widget-browse-mode-map nil
28 "Keymap for `widget-browse-mode'.")
29
30(unless widget-browse-mode-map
31 (setq widget-browse-mode-map (make-sparse-keymap))
32 (set-keymap-parent widget-browse-mode-map widget-keymap))
33
34(easy-menu-define widget-browse-mode-menu
35 widget-browse-mode-map
36 "Menu used in widget browser buffers."
37 '("Widget"
38 ["Browse" widget-browse t]
39 ["Browse At" widget-browse-at t]))
40
41(defcustom widget-browse-mode-hook nil
42 "Hook called when entering widget-browse-mode."
43 :type 'hook
44 :group 'widget-browse)
45
46(defun widget-browse-mode ()
47 "Major mode for widget browser buffers.
48
49The following commands are available:
50
51\\[widget-forward] Move to next button or editable field.
52\\[widget-backward] Move to previous button or editable field.
53\\[widget-button-click] Activate button under the mouse pointer.
54\\[widget-button-press] Activate button under point.
55
56Entry to this mode calls the value of `widget-browse-mode-hook'
57if that value is non-nil."
58 (kill-all-local-variables)
59 (setq major-mode 'widget-browse-mode
60 mode-name "Widget")
61 (use-local-map widget-browse-mode-map)
62 (easy-menu-add widget-browse-mode-menu)
63 (run-hooks 'widget-browse-mode-hook))
64
65;;; Commands.
66
67;;;###autoload
68(defun widget-browse-at (pos)
69 "Browse the widget under point."
70 (interactive "d")
71 (let* ((field (get-text-property pos 'field))
72 (button (get-text-property pos 'button))
73 (doc (get-text-property pos 'widget-doc))
74 (text (cond (field "This is an editable text area.")
75 (button "This is an active area.")
76 (doc "This is documentation text.")
77 (t "This is unidentified text.")))
78 (widget (or field button doc)))
79 (when widget
80 (widget-browse widget))
81 (message text)))
82
83(defvar widget-browse-history nil)
84
85(defun widget-browse (widget)
86 "Create a widget browser for WIDGET."
87 (interactive (list (completing-read "Widget: "
88 obarray
89 (lambda (symbol)
90 (get symbol 'widget-type))
91 t nil 'widget-browse-history)))
92 (if (stringp widget)
93 (setq widget (intern widget)))
94 (unless (if (symbolp widget)
95 (get widget 'widget-type)
96 (and (consp widget)
97 (get (widget-type widget) 'widget-type)))
98 (error "Not a widget."))
99 ;; Create the buffer.
100 (if (symbolp widget)
101 (let ((buffer (format "*Browse %s Widget*" widget)))
102 (kill-buffer (get-buffer-create buffer))
103 (switch-to-buffer (get-buffer-create buffer)))
104 (kill-buffer (get-buffer-create "*Browse Widget*"))
105 (switch-to-buffer (get-buffer-create "*Browse Widget*")))
106 (widget-browse-mode)
107
108 ;; Quick way to get out.
109 (widget-create 'push-button
110 :action (lambda (widget &optional event)
111 (bury-buffer))
112 "Quit")
113 (widget-insert "\n")
114
115 ;; Top text indicating whether it is a class or object browser.
116 (if (listp widget)
117 (widget-insert "Widget object browser.\n\nClass: ")
118 (widget-insert "Widget class browser.\n\n")
119 (widget-create 'widget-browse
120 :format "%[%v%]\n%d"
121 :doc (get widget 'widget-documentation)
122 widget)
123 (unless (eq (preceding-char) ?\n)
124 (widget-insert "\n"))
125 (widget-insert "\nSuper: ")
126 (setq widget (get widget 'widget-type)))
127
128 ;; Now show the attributes.
129 (let ((name (car widget))
130 (items (cdr widget))
131 key value printer)
132 (widget-create 'widget-browse
133 :format "%[%v%]"
134 name)
135 (widget-insert "\n")
136 (while items
137 (setq key (nth 0 items)
138 value (nth 1 items)
139 printer (or (get key 'widget-keyword-printer)
140 'widget-browse-sexp)
141 items (cdr (cdr items)))
142 (widget-insert "\n" (symbol-name key) "\n\t")
143 (funcall printer widget key value)
144 (widget-insert "\n")))
145 (widget-setup)
146 (goto-char (point-min)))
147
148;;; The `widget-browse' Widget.
149
150(define-widget 'widget-browse 'push-button
151 "Button for creating a widget browser.
152The :value of the widget shuld be the widget to be browsed."
153 :format "%[[%v]%]"
154 :value-create 'widget-browse-value-create
155 :action 'widget-browse-action)
156
157(defun widget-browse-action (widget &optional event)
158 ;; Create widget browser for WIDGET's :value.
159 (widget-browse (widget-get widget :value)))
160
161(defun widget-browse-value-create (widget)
162 ;; Insert type name.
163 (let ((value (widget-get widget :value)))
164 (cond ((symbolp value)
165 (insert (symbol-name value)))
166 ((consp value)
167 (insert (symbol-name (widget-type value))))
168 (t
169 (insert "strange")))))
170
171;;; Keyword Printer Functions.
172
173(defun widget-browse-widget (widget key value)
174 "Insert description of WIDGET's KEY VALUE.
175VALUE is assumed to be a widget."
176 (widget-create 'widget-browse value))
177
178(defun widget-browse-widgets (widget key value)
179 "Insert description of WIDGET's KEY VALUE.
180VALUE is assumed to be a list of widgets."
181 (while value
182 (widget-create 'widget-browse
183 (car value))
184 (setq value (cdr value))
185 (when value
186 (widget-insert " "))))
187
188(defun widget-browse-sexp (widget key value)
189 "Insert description of WIDGET's KEY VALUE.
190Nothing is assumed about value."
191 (let ((pp (condition-case signal
192 (pp-to-string value)
193 (error (prin1-to-string signal)))))
194 (when (string-match "\n\\'" pp)
195 (setq pp (substring pp 0 (1- (length pp)))))
196 (if (cond ((string-match "\n" pp)
197 nil)
198 ((> (length pp) (- (window-width) (current-column)))
199 nil)
200 (t t))
201 (widget-insert pp)
202 (widget-create 'push-button
203 :tag "show"
204 :action (lambda (widget &optional event)
205 (with-output-to-temp-buffer
206 "*Pp Eval Output*"
207 (princ (widget-get widget :value))))
208 pp))))
209
210(defun widget-browse-sexps (widget key value)
211 "Insert description of WIDGET's KEY VALUE.
212VALUE is assumed to be a list of widgets."
213 (let ((target (current-column)))
214 (while value
215 (widget-browse-sexp widget key (car value))
216 (setq value (cdr value))
217 (when value
218 (widget-insert "\n" (make-string target ?\ ))))))
219
220;;; Keyword Printers.
221
222(put :parent 'widget-keyword-printer 'widget-browse-widget)
223(put :children 'widget-keyword-printer 'widget-browse-widgets)
224(put :buttons 'widget-keyword-printer 'widget-browse-widgets)
225(put :button 'widget-keyword-printer 'widget-browse-widget)
226(put :args 'widget-keyword-printer 'widget-browse-sexps)
227
228;;; The End:
229
230(provide 'wid-browse)
231
232;; wid-browse.el ends here
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
new file mode 100644
index 00000000000..283981d42f4
--- /dev/null
+++ b/lisp/wid-edit.el
@@ -0,0 +1,2542 @@
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.71
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
18(eval-and-compile
19 (require 'cl))
20
21;;; Compatibility.
22
23(eval-and-compile
24 (autoload 'pp-to-string "pp")
25 (autoload 'Info-goto-node "info")
26
27 (when (string-match "XEmacs" emacs-version)
28 (condition-case nil
29 (require 'overlay)
30 (error (load-library "x-overlay"))))
31
32 (if (string-match "XEmacs" emacs-version)
33 ;; XEmacs spell `intangible' as `atomic'.
34 (defun widget-make-intangible (from to side)
35 "Make text between FROM and TO atomic with regard to movement.
36Third argument should be `start-open' if it should be sticky to the rear,
37and `end-open' if it should sticky to the front."
38 (require 'atomic-extents)
39 (let ((ext (make-extent from to)))
40 ;; XEmacs doesn't understant different kinds of read-only, so
41 ;; we have to use extents instead.
42 (put-text-property from to 'read-only nil)
43 (set-extent-property ext 'read-only t)
44 (set-extent-property ext 'start-open nil)
45 (set-extent-property ext 'end-open nil)
46 (set-extent-property ext side t)
47 (set-extent-property ext 'atomic t)))
48 (defun widget-make-intangible (from to size)
49 "Make text between FROM and TO intangible."
50 (put-text-property from to 'intangible 'front)))
51
52;; The following should go away when bundled with Emacs.
53 (condition-case ()
54 (require 'custom)
55 (error nil))
56
57 (unless (and (featurep 'custom) (fboundp 'custom-declare-variable))
58 ;; We have the old custom-library, hack around it!
59 (defmacro defgroup (&rest args) nil)
60 (defmacro defcustom (var value doc &rest args)
61 `(defvar ,var ,value ,doc))
62 (defmacro defface (&rest args) nil)
63 (define-widget-keywords :prefix :tag :load :link :options :type :group)
64 (when (fboundp 'copy-face)
65 (copy-face 'default 'widget-documentation-face)
66 (copy-face 'bold 'widget-button-face)
67 (copy-face 'italic 'widget-field-face)))
68
69 (unless (fboundp 'event-point)
70 ;; XEmacs function missing in Emacs.
71 (defun event-point (event)
72 "Return the character position of the given mouse-motion, button-press,
73or button-release event. If the event did not occur over a window, or did
74not occur over text, then this returns nil. Otherwise, it returns an index
75into the buffer visible in the event's window."
76 (posn-point (event-start event))))
77
78 (unless (fboundp 'error-message-string)
79 ;; Emacs function missing in XEmacs.
80 (defun error-message-string (obj)
81 "Convert an error value to an error message."
82 (let ((buf (get-buffer-create " *error-message*")))
83 (erase-buffer buf)
84 (display-error obj buf)
85 (buffer-string buf)))))
86
87;;; Customization.
88
89(defgroup widgets nil
90 "Customization support for the Widget Library."
91 :link '(custom-manual "(widget)Top")
92 :link '(url-link :tag "Development Page"
93 "http://www.dina.kvl.dk/~abraham/custom/")
94 :prefix "widget-"
95 :group 'extensions
96 :group 'faces
97 :group 'hypermedia)
98
99(defface widget-documentation-face '((((class color)
100 (background dark))
101 (:foreground "lime green"))
102 (((class color)
103 (background light))
104 (:foreground "dark green"))
105 (t nil))
106 "Face used for documentation text."
107 :group 'widgets)
108
109(defface widget-button-face '((t (:bold t)))
110 "Face used for widget buttons."
111 :group 'widgets)
112
113(defcustom widget-mouse-face 'highlight
114 "Face used for widget buttons when the mouse is above them."
115 :type 'face
116 :group 'widgets)
117
118(defface widget-field-face '((((class grayscale color)
119 (background light))
120 (:background "light gray"))
121 (((class grayscale color)
122 (background dark))
123 (:background "dark gray"))
124 (t
125 (:italic t)))
126 "Face used for editable fields."
127 :group 'widgets)
128
129(defcustom widget-menu-max-size 40
130 "Largest number of items allowed in a popup-menu.
131Larger menus are read through the minibuffer."
132 :group 'widgets
133 :type 'integer)
134
135;;; Utility functions.
136;;
137;; These are not really widget specific.
138
139(defsubst widget-plist-member (plist prop)
140 ;; Return non-nil if PLIST has the property PROP.
141 ;; PLIST is a property list, which is a list of the form
142 ;; (PROP1 VALUE1 PROP2 VALUE2 ...). PROP is a symbol.
143 ;; Unlike `plist-get', this allows you to distinguish between a missing
144 ;; property and a property with the value nil.
145 ;; The value is actually the tail of PLIST whose car is PROP.
146 (while (and plist (not (eq (car plist) prop)))
147 (setq plist (cdr (cdr plist))))
148 plist)
149
150(defun widget-princ-to-string (object)
151 ;; Return string representation of OBJECT, any Lisp object.
152 ;; No quoting characters are used; no delimiters are printed around
153 ;; the contents of strings.
154 (save-excursion
155 (set-buffer (get-buffer-create " *widget-tmp*"))
156 (erase-buffer)
157 (let ((standard-output (current-buffer)))
158 (princ object))
159 (buffer-string)))
160
161(defun widget-clear-undo ()
162 "Clear all undo information."
163 (buffer-disable-undo (current-buffer))
164 (buffer-enable-undo))
165
166(defun widget-choose (title items &optional event)
167 "Choose an item from a list.
168
169First argument TITLE is the name of the list.
170Second argument ITEMS is an alist (NAME . VALUE).
171Optional third argument EVENT is an input event.
172
173The user is asked to choose between each NAME from the items alist,
174and the VALUE of the chosen element will be returned. If EVENT is a
175mouse event, and the number of elements in items is less than
176`widget-menu-max-size', a popup menu will be used, otherwise the
177minibuffer."
178 (cond ((and (< (length items) widget-menu-max-size)
179 event (fboundp 'x-popup-menu) window-system)
180 ;; We are in Emacs-19, pressed by the mouse
181 (x-popup-menu event
182 (list title (cons "" items))))
183 ((and (< (length items) widget-menu-max-size)
184 event (fboundp 'popup-menu) window-system)
185 ;; We are in XEmacs, pressed by the mouse
186 (let ((val (get-popup-menu-response
187 (cons title
188 (mapcar
189 (function
190 (lambda (x)
191 (vector (car x) (list (car x)) t)))
192 items)))))
193 (setq val (and val
194 (listp (event-object val))
195 (stringp (car-safe (event-object val)))
196 (car (event-object val))))
197 (cdr (assoc val items))))
198 (t
199 (let ((val (completing-read (concat title ": ") items nil t)))
200 (if (stringp val)
201 (let ((try (try-completion val items)))
202 (when (stringp try)
203 (setq val try))
204 (cdr (assoc val items)))
205 nil)))))
206
207(defun widget-get-sibling (widget)
208 "Get the item WIDGET is assumed to toggle.
209This is only meaningful for radio buttons or checkboxes in a list."
210 (let* ((parent (widget-get widget :parent))
211 (children (widget-get parent :children))
212 child)
213 (catch 'child
214 (while children
215 (setq child (car children)
216 children (cdr children))
217 (when (eq (widget-get child :button) widget)
218 (throw 'child child)))
219 nil)))
220
221;;; Widget text specifications.
222;;
223;; These functions are for specifying text properties.
224
225(defun widget-specify-none (from to)
226 ;; Clear all text properties between FROM and TO.
227 (set-text-properties from to nil))
228
229(defun widget-specify-text (from to)
230 ;; Default properties.
231 (add-text-properties from to (list 'read-only t
232 'front-sticky t
233 'start-open t
234 'end-open t
235 'rear-nonsticky nil)))
236
237(defun widget-specify-field (widget from to)
238 ;; Specify editable button for WIDGET between FROM and TO.
239 (widget-specify-field-update widget from to)
240
241 ;; Make it possible to edit the front end of the field.
242 (add-text-properties (1- from) from (list 'rear-nonsticky t
243 'end-open t
244 'invisible t))
245 (when (or (string-match "\\(.\\|\n\\)%v" (widget-get widget :format))
246 (widget-get widget :hide-front-space))
247 ;; WARNING: This is going to lose horrible if the character just
248 ;; before the field can be modified (e.g. if it belongs to a
249 ;; choice widget). We try to compensate by checking the format
250 ;; string, and hope the user hasn't changed the :create method.
251 (widget-make-intangible (- from 2) from 'end-open))
252
253 ;; Make it possible to edit back end of the field.
254 (add-text-properties to (1+ to) (list 'front-sticky nil
255 'read-only t
256 'start-open t))
257
258 (cond ((widget-get widget :size)
259 (put-text-property to (1+ to) 'invisible t)
260 (when (or (string-match "%v\\(.\\|\n\\)" (widget-get widget :format))
261 (widget-get widget :hide-rear-space))
262 ;; WARNING: This is going to lose horrible if the character just
263 ;; after the field can be modified (e.g. if it belongs to a
264 ;; choice widget). We try to compensate by checking the format
265 ;; string, and hope the user hasn't changed the :create method.
266 (widget-make-intangible to (+ to 2) 'start-open)))
267 ((string-match "XEmacs" emacs-version)
268 ;; XEmacs does not allow you to insert before a read-only
269 ;; character, even if it is start.open.
270 ;; XEmacs does allow you to delete an read-only extent, so
271 ;; making the terminating newline read only doesn't help.
272 ;; I tried putting an invisible intangible read-only space
273 ;; before the newline, which gave really weird effects.
274 ;; So for now, we just have trust the user not to delete the
275 ;; newline.
276 (put-text-property to (1+ to) 'read-only nil))))
277
278(defun widget-specify-field-update (widget from to)
279 ;; Specify editable button for WIDGET between FROM and TO.
280 (let ((map (widget-get widget :keymap))
281 (secret (widget-get widget :secret))
282 (secret-to to)
283 (size (widget-get widget :size))
284 (face (or (widget-get widget :value-face)
285 'widget-field-face))
286 (help-echo (widget-get widget :help-echo))
287 (help-property (if (featurep 'balloon-help)
288 'balloon-help
289 'help-echo)))
290 (unless (or (stringp help-echo) (null help-echo))
291 (setq help-echo 'widget-mouse-help))
292
293 (when secret
294 (while (and size
295 (not (zerop size))
296 (> secret-to from)
297 (eq (char-after (1- secret-to)) ?\ ))
298 (setq secret-to (1- secret-to)))
299
300 (save-excursion
301 (goto-char from)
302 (while (< (point) secret-to)
303 (let ((old (get-text-property (point) 'secret)))
304 (when old
305 (subst-char-in-region (point) (1+ (point)) secret old)))
306 (forward-char))))
307
308 (set-text-properties from to (list 'field widget
309 'read-only nil
310 'keymap map
311 'local-map map
312 help-property help-echo
313 'face face))
314
315 (when secret
316 (save-excursion
317 (goto-char from)
318 (while (< (point) secret-to)
319 (let ((old (following-char)))
320 (subst-char-in-region (point) (1+ (point)) old secret)
321 (put-text-property (point) (1+ (point)) 'secret old))
322 (forward-char))))
323
324 (unless (widget-get widget :size)
325 (add-text-properties to (1+ to) (list 'field widget
326 help-property help-echo
327 'face face)))
328 (add-text-properties to (1+ to) (list 'local-map map
329 'keymap map))))
330
331(defun widget-specify-button (widget from to)
332 ;; Specify button for WIDGET between FROM and TO.
333 (let ((face (widget-apply widget :button-face-get))
334 (help-echo (widget-get widget :help-echo))
335 (help-property (if (featurep 'balloon-help)
336 'balloon-help
337 'help-echo)))
338 (unless (or (null help-echo) (stringp help-echo))
339 (setq help-echo 'widget-mouse-help))
340 (add-text-properties from to (list 'button widget
341 'mouse-face widget-mouse-face
342 'start-open t
343 'end-open t
344 help-property help-echo
345 'face face))))
346
347(defun widget-mouse-help (extent)
348 "Find mouse help string for button in extent."
349 (let* ((widget (widget-at (extent-start-position extent)))
350 (help-echo (and widget (widget-get widget :help-echo))))
351 (cond ((stringp help-echo)
352 help-echo)
353 ((and (symbolp help-echo) (fboundp help-echo)
354 (stringp (setq help-echo (funcall help-echo widget))))
355 help-echo)
356 (t
357 (format "(widget %S :help-echo %S)" widget help-echo)))))
358
359(defun widget-specify-sample (widget from to)
360 ;; Specify sample for WIDGET between FROM and TO.
361 (let ((face (widget-apply widget :sample-face-get)))
362 (when face
363 (add-text-properties from to (list 'start-open t
364 'end-open t
365 'face face)))))
366
367(defun widget-specify-doc (widget from to)
368 ;; Specify documentation for WIDGET between FROM and TO.
369 (add-text-properties from to (list 'widget-doc widget
370 'face 'widget-documentation-face)))
371
372(defmacro widget-specify-insert (&rest form)
373 ;; Execute FORM without inheriting any text properties.
374 `(save-restriction
375 (let ((inhibit-read-only t)
376 result
377 after-change-functions)
378 (insert "<>")
379 (narrow-to-region (- (point) 2) (point))
380 (widget-specify-none (point-min) (point-max))
381 (goto-char (1+ (point-min)))
382 (setq result (progn ,@form))
383 (delete-region (point-min) (1+ (point-min)))
384 (delete-region (1- (point-max)) (point-max))
385 (goto-char (point-max))
386 result)))
387
388(defface widget-inactive-face '((((class grayscale color)
389 (background dark))
390 (:foreground "light gray"))
391 (((class grayscale color)
392 (background light))
393 (:foreground "dark gray"))
394 (t
395 (:italic t)))
396 "Face used for inactive widgets."
397 :group 'widgets)
398
399(defun widget-specify-inactive (widget from to)
400 "Make WIDGET inactive for user modifications."
401 (unless (widget-get widget :inactive)
402 (let ((overlay (make-overlay from to nil t nil)))
403 (overlay-put overlay 'face 'widget-inactive-face)
404 (overlay-put overlay 'evaporate 't)
405 (overlay-put overlay (if (string-match "XEmacs" emacs-version)
406 'read-only
407 'modification-hooks) '(widget-overlay-inactive))
408 (widget-put widget :inactive overlay))))
409
410(defun widget-overlay-inactive (&rest junk)
411 "Ignoring the arguments, signal an error."
412 (unless inhibit-read-only
413 (error "Attempt to modify inactive widget")))
414
415
416(defun widget-specify-active (widget)
417 "Make WIDGET active for user modifications."
418 (let ((inactive (widget-get widget :inactive)))
419 (when inactive
420 (delete-overlay inactive)
421 (widget-put widget :inactive nil))))
422
423;;; Widget Properties.
424
425(defsubst widget-type (widget)
426 "Return the type of WIDGET, a symbol."
427 (car widget))
428
429(defun widget-put (widget property value)
430 "In WIDGET set PROPERTY to VALUE.
431The value can later be retrived with `widget-get'."
432 (setcdr widget (plist-put (cdr widget) property value)))
433
434(defun widget-get (widget property)
435 "In WIDGET, get the value of PROPERTY.
436The value could either be specified when the widget was created, or
437later with `widget-put'."
438 (let ((missing t)
439 value tmp)
440 (while missing
441 (cond ((setq tmp (widget-plist-member (cdr widget) property))
442 (setq value (car (cdr tmp))
443 missing nil))
444 ((setq tmp (car widget))
445 (setq widget (get tmp 'widget-type)))
446 (t
447 (setq missing nil))))
448 value))
449
450(defun widget-member (widget property)
451 "Non-nil iff there is a definition in WIDGET for PROPERTY."
452 (cond ((widget-plist-member (cdr widget) property)
453 t)
454 ((car widget)
455 (widget-member (get (car widget) 'widget-type) property))
456 (t nil)))
457
458;;;###autoload
459(defun widget-apply (widget property &rest args)
460 "Apply the value of WIDGET's PROPERTY to the widget itself.
461ARGS are passed as extra arguments to the function."
462 (apply (widget-get widget property) widget args))
463
464(defun widget-value (widget)
465 "Extract the current value of WIDGET."
466 (widget-apply widget
467 :value-to-external (widget-apply widget :value-get)))
468
469(defun widget-value-set (widget value)
470 "Set the current value of WIDGET to VALUE."
471 (widget-apply widget
472 :value-set (widget-apply widget
473 :value-to-internal value)))
474
475(defun widget-match-inline (widget vals)
476 ;; In WIDGET, match the start of VALS.
477 (cond ((widget-get widget :inline)
478 (widget-apply widget :match-inline vals))
479 ((and vals
480 (widget-apply widget :match (car vals)))
481 (cons (list (car vals)) (cdr vals)))
482 (t nil)))
483
484(defun widget-apply-action (widget &optional event)
485 "Apply :action in WIDGET in response to EVENT."
486 (if (widget-apply widget :active)
487 (widget-apply widget :action event)
488 (error "Attempt to perform action on inactive widget")))
489
490;;; Glyphs.
491
492(defcustom widget-glyph-directory (concat data-directory "custom/")
493 "Where widget glyphs are located.
494If this variable is nil, widget will try to locate the directory
495automatically. This does not work yet."
496 :group 'widgets
497 :type 'directory)
498
499(defcustom widget-glyph-enable t
500 "If non nil, use glyphs in images when available."
501 :group 'widgets
502 :type 'boolean)
503
504(defun widget-glyph-insert (widget tag image)
505 "In WIDGET, insert the text TAG or, if supported, IMAGE.
506IMAGE should either be a glyph, or a name sans extension of an xpm or
507xbm file located in `widget-glyph-directory'.
508
509WARNING: If you call this with a glyph, and you want the user to be
510able to activate the glyph, make sure it is unique. If you use the
511same glyph for multiple widgets, activating any of the glyphs will
512cause the last created widget to be activated."
513 (cond ((not (and (string-match "XEmacs" emacs-version)
514 widget-glyph-enable
515 (fboundp 'make-glyph)
516 image))
517 ;; We don't want or can't use glyphs.
518 (insert tag))
519 ((and (fboundp 'glyphp)
520 (glyphp image))
521 ;; Already a glyph. Insert it.
522 (widget-glyph-insert-glyph widget tag image))
523 (t
524 ;; A string. Look it up in.
525 (let ((file (concat widget-glyph-directory
526 (if (string-match "/\\'" widget-glyph-directory)
527 ""
528 "/")
529 image
530 (if (featurep 'xpm) ".xpm" ".xbm"))))
531 (if (file-readable-p file)
532 (widget-glyph-insert-glyph widget tag (make-glyph file))
533 ;; File not readable, give up.
534 (insert tag))))))
535
536(defun widget-glyph-insert-glyph (widget tag glyph)
537 "In WIDGET, with alternative text TAG, insert GLYPH."
538 (set-glyph-image glyph (cons 'tty tag))
539 (set-glyph-property glyph 'widget widget)
540 (insert "*")
541 (add-text-properties (1- (point)) (point)
542 (list 'invisible t
543 'end-glyph glyph))
544 (let ((help-echo (widget-get widget :help-echo)))
545 (when help-echo
546 (let ((extent (extent-at (1- (point)) nil 'end-glyph))
547 (help-property (if (featurep 'balloon-help)
548 'balloon-help
549 'help-echo)))
550 (set-extent-property extent help-property (if (stringp help-echo)
551 help-echo
552 'widget-mouse-help))))))
553
554;;; Creating Widgets.
555
556;;;###autoload
557(defun widget-create (type &rest args)
558 "Create widget of TYPE.
559The optional ARGS are additional keyword arguments."
560 (let ((widget (apply 'widget-convert type args)))
561 (widget-apply widget :create)
562 widget))
563
564(defun widget-create-child-and-convert (parent type &rest args)
565 "As part of the widget PARENT, create a child widget TYPE.
566The child is converted, using the keyword arguments ARGS."
567 (let ((widget (apply 'widget-convert type args)))
568 (widget-put widget :parent parent)
569 (unless (widget-get widget :indent)
570 (widget-put widget :indent (+ (or (widget-get parent :indent) 0)
571 (or (widget-get widget :extra-offset) 0)
572 (widget-get parent :offset))))
573 (widget-apply widget :create)
574 widget))
575
576(defun widget-create-child (parent type)
577 "Create widget of TYPE."
578 (let ((widget (copy-list type)))
579 (widget-put widget :parent parent)
580 (unless (widget-get widget :indent)
581 (widget-put widget :indent (+ (or (widget-get parent :indent) 0)
582 (or (widget-get widget :extra-offset) 0)
583 (widget-get parent :offset))))
584 (widget-apply widget :create)
585 widget))
586
587(defun widget-create-child-value (parent type value)
588 "Create widget of TYPE with value VALUE."
589 (let ((widget (copy-list type)))
590 (widget-put widget :value (widget-apply widget :value-to-internal value))
591 (widget-put widget :parent parent)
592 (unless (widget-get widget :indent)
593 (widget-put widget :indent (+ (or (widget-get parent :indent) 0)
594 (or (widget-get widget :extra-offset) 0)
595 (widget-get parent :offset))))
596 (widget-apply widget :create)
597 widget))
598
599;;;###autoload
600(defun widget-delete (widget)
601 "Delete WIDGET."
602 (widget-apply widget :delete))
603
604(defun widget-convert (type &rest args)
605 "Convert TYPE to a widget without inserting it in the buffer.
606The optional ARGS are additional keyword arguments."
607 ;; Don't touch the type.
608 (let* ((widget (if (symbolp type)
609 (list type)
610 (copy-list type)))
611 (current widget)
612 (keys args))
613 ;; First set the :args keyword.
614 (while (cdr current) ;Look in the type.
615 (let ((next (car (cdr current))))
616 (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:))
617 (setq current (cdr (cdr current)))
618 (setcdr current (list :args (cdr current)))
619 (setq current nil))))
620 (while args ;Look in the args.
621 (let ((next (nth 0 args)))
622 (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:))
623 (setq args (nthcdr 2 args))
624 (widget-put widget :args args)
625 (setq args nil))))
626 ;; Then Convert the widget.
627 (setq type widget)
628 (while type
629 (let ((convert-widget (plist-get (cdr type) :convert-widget)))
630 (if convert-widget
631 (setq widget (funcall convert-widget widget))))
632 (setq type (get (car type) 'widget-type)))
633 ;; Finally set the keyword args.
634 (while keys
635 (let ((next (nth 0 keys)))
636 (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:))
637 (progn
638 (widget-put widget next (nth 1 keys))
639 (setq keys (nthcdr 2 keys)))
640 (setq keys nil))))
641 ;; Convert the :value to internal format.
642 (if (widget-member widget :value)
643 (let ((value (widget-get widget :value)))
644 (widget-put widget
645 :value (widget-apply widget :value-to-internal value))))
646 ;; Return the newly create widget.
647 widget))
648
649(defun widget-insert (&rest args)
650 "Call `insert' with ARGS and make the text read only."
651 (let ((inhibit-read-only t)
652 after-change-functions
653 (from (point)))
654 (apply 'insert args)
655 (widget-specify-text from (point))))
656
657;;; Keymap and Commands.
658
659(defvar widget-keymap nil
660 "Keymap containing useful binding for buffers containing widgets.
661Recommended as a parent keymap for modes using widgets.")
662
663(unless widget-keymap
664 (setq widget-keymap (make-sparse-keymap))
665 (define-key widget-keymap "\C-k" 'widget-kill-line)
666 (define-key widget-keymap "\t" 'widget-forward)
667 (define-key widget-keymap "\M-\t" 'widget-backward)
668 (define-key widget-keymap [(shift tab)] 'widget-backward)
669 (define-key widget-keymap [backtab] 'widget-backward)
670 (if (string-match "XEmacs" (emacs-version))
671 (progn
672 (define-key widget-keymap [button2] 'widget-button-click)
673 (define-key widget-keymap [button1] 'widget-button1-click))
674 (define-key widget-keymap [mouse-2] 'ignore)
675 (define-key widget-keymap [down-mouse-2] 'widget-button-click))
676 (define-key widget-keymap "\C-m" 'widget-button-press))
677
678(defvar widget-global-map global-map
679 "Keymap used for events the widget does not handle themselves.")
680(make-variable-buffer-local 'widget-global-map)
681
682(defvar widget-field-keymap nil
683 "Keymap used inside an editable field.")
684
685(unless widget-field-keymap
686 (setq widget-field-keymap (copy-keymap widget-keymap))
687 (unless (string-match "XEmacs" (emacs-version))
688 (define-key widget-field-keymap [menu-bar] 'nil))
689 (define-key widget-field-keymap "\C-m" 'widget-field-activate)
690 (define-key widget-field-keymap "\C-a" 'widget-beginning-of-line)
691 (define-key widget-field-keymap "\C-e" 'widget-end-of-line)
692 (set-keymap-parent widget-field-keymap global-map))
693
694(defvar widget-text-keymap nil
695 "Keymap used inside a text field.")
696
697(unless widget-text-keymap
698 (setq widget-text-keymap (copy-keymap widget-keymap))
699 (unless (string-match "XEmacs" (emacs-version))
700 (define-key widget-text-keymap [menu-bar] 'nil))
701 (define-key widget-text-keymap "\C-a" 'widget-beginning-of-line)
702 (define-key widget-text-keymap "\C-e" 'widget-end-of-line)
703 (set-keymap-parent widget-text-keymap global-map))
704
705(defun widget-field-activate (pos &optional event)
706 "Activate the ediable field at point."
707 (interactive "@d")
708 (let ((field (get-text-property pos 'field)))
709 (if field
710 (widget-apply-action field event)
711 (call-interactively
712 (lookup-key widget-global-map (this-command-keys))))))
713
714(defun widget-button-click (event)
715 "Activate button below mouse pointer."
716 (interactive "@e")
717 (cond ((and (fboundp 'event-glyph)
718 (event-glyph event))
719 (let ((widget (glyph-property (event-glyph event) 'widget)))
720 (if widget
721 (widget-apply-action widget event)
722 (message "You clicked on a glyph."))))
723 ((event-point event)
724 (let ((button (get-text-property (event-point event) 'button)))
725 (if button
726 (widget-apply-action button event)
727 (call-interactively
728 (or (lookup-key widget-global-map [ button2 ])
729 (lookup-key widget-global-map [ down-mouse-2 ])
730 (lookup-key widget-global-map [ mouse-2]))))))
731 (t
732 (message "You clicked somewhere weird."))))
733
734(defun widget-button1-click (event)
735 "Activate glyph below mouse pointer."
736 (interactive "@e")
737 (if (and (fboundp 'event-glyph)
738 (event-glyph event))
739 (let ((widget (glyph-property (event-glyph event) 'widget)))
740 (if widget
741 (widget-apply-action widget event)
742 (message "You clicked on a glyph.")))
743 (call-interactively (lookup-key widget-global-map (this-command-keys)))))
744
745(defun widget-button-press (pos &optional event)
746 "Activate button at POS."
747 (interactive "@d")
748 (let ((button (get-text-property pos 'button)))
749 (if button
750 (widget-apply-action button event)
751 (let ((command (lookup-key widget-global-map (this-command-keys))))
752 (when (commandp command)
753 (call-interactively command))))))
754
755(defun widget-move (arg)
756 "Move point to the ARG next field or button.
757ARG may be negative to move backward."
758 (while (> arg 0)
759 (setq arg (1- arg))
760 (let ((next (cond ((get-text-property (point) 'button)
761 (next-single-property-change (point) 'button))
762 ((get-text-property (point) 'field)
763 (next-single-property-change (point) 'field))
764 (t
765 (point)))))
766 (if (null next) ; Widget extends to end. of buffer
767 (setq next (point-min)))
768 (let ((button (next-single-property-change next 'button))
769 (field (next-single-property-change next 'field)))
770 (cond ((or (get-text-property next 'button)
771 (get-text-property next 'field))
772 (goto-char next))
773 ((and button field)
774 (goto-char (min button field)))
775 (button (goto-char button))
776 (field (goto-char field))
777 (t
778 (let ((button (next-single-property-change (point-min) 'button))
779 (field (next-single-property-change (point-min) 'field)))
780 (cond ((and button field) (goto-char (min button field)))
781 (button (goto-char button))
782 (field (goto-char field))
783 (t
784 (error "No buttons or fields found"))))))
785 (setq button (widget-at (point)))
786 (if (and button (widget-get button :tab-order)
787 (< (widget-get button :tab-order) 0))
788 (setq arg (1+ arg))))))
789 (while (< arg 0)
790 (if (= (point-min) (point))
791 (forward-char 1))
792 (setq arg (1+ arg))
793 (let ((previous (cond ((get-text-property (1- (point)) 'button)
794 (previous-single-property-change (point) 'button))
795 ((get-text-property (1- (point)) 'field)
796 (previous-single-property-change (point) 'field))
797 (t
798 (point)))))
799 (if (null previous) ; Widget extends to beg. of buffer
800 (setq previous (point-max)))
801 (let ((button (previous-single-property-change previous 'button))
802 (field (previous-single-property-change previous 'field)))
803 (cond ((and button field)
804 (goto-char (max button field)))
805 (button (goto-char button))
806 (field (goto-char field))
807 (t
808 (let ((button (previous-single-property-change
809 (point-max) 'button))
810 (field (previous-single-property-change
811 (point-max) 'field)))
812 (cond ((and button field) (goto-char (max button field)))
813 (button (goto-char button))
814 (field (goto-char field))
815 (t
816 (error "No buttons or fields found"))))))))
817 (let ((button (previous-single-property-change (point) 'button))
818 (field (previous-single-property-change (point) 'field)))
819 (cond ((and button field)
820 (goto-char (max button field)))
821 (button (goto-char button))
822 (field (goto-char field)))
823 (setq button (widget-at (point)))
824 (if (and button (widget-get button :tab-order)
825 (< (widget-get button :tab-order) 0))
826 (setq arg (1- arg)))))
827 (widget-echo-help (point))
828 (run-hooks 'widget-move-hook))
829
830(defun widget-forward (arg)
831 "Move point to the next field or button.
832With optional ARG, move across that many fields."
833 (interactive "p")
834 (run-hooks 'widget-forward-hook)
835 (widget-move arg))
836
837(defun widget-backward (arg)
838 "Move point to the previous field or button.
839With optional ARG, move across that many fields."
840 (interactive "p")
841 (run-hooks 'widget-backward-hook)
842 (widget-move (- arg)))
843
844(defun widget-beginning-of-line ()
845 "Go to beginning of field or beginning of line, whichever is first."
846 (interactive)
847 (let ((bol (save-excursion (beginning-of-line) (point)))
848 (prev (previous-single-property-change (point) 'field)))
849 (goto-char (max bol (or prev bol)))))
850
851(defun widget-end-of-line ()
852 "Go to end of field or end of line, whichever is first."
853 (interactive)
854 (let ((bol (save-excursion (end-of-line) (point)))
855 (prev (next-single-property-change (point) 'field)))
856 (goto-char (min bol (or prev bol)))))
857
858(defun widget-kill-line ()
859 "Kill to end of field or end of line, whichever is first."
860 (interactive)
861 (let ((field (get-text-property (point) 'field))
862 (newline (save-excursion (search-forward "\n")))
863 (next (next-single-property-change (point) 'field)))
864 (if (and field (> newline next))
865 (kill-region (point) next)
866 (call-interactively 'kill-line))))
867
868;;; Setting up the buffer.
869
870(defvar widget-field-new nil)
871;; List of all newly created editable fields in the buffer.
872(make-variable-buffer-local 'widget-field-new)
873
874(defvar widget-field-list nil)
875;; List of all editable fields in the buffer.
876(make-variable-buffer-local 'widget-field-list)
877
878(defun widget-setup ()
879 "Setup current buffer so editing string widgets works."
880 (let ((inhibit-read-only t)
881 (after-change-functions nil)
882 field)
883 (while widget-field-new
884 (setq field (car widget-field-new)
885 widget-field-new (cdr widget-field-new)
886 widget-field-list (cons field widget-field-list))
887 (let ((from (widget-get field :value-from))
888 (to (widget-get field :value-to)))
889 (widget-specify-field field from to)
890 (move-marker from (1- from))
891 (move-marker to (1+ to)))))
892 (widget-clear-undo)
893 ;; We need to maintain text properties and size of the editing fields.
894 (make-local-variable 'after-change-functions)
895 (if widget-field-list
896 (setq after-change-functions '(widget-after-change))
897 (setq after-change-functions nil)))
898
899(defvar widget-field-last nil)
900;; Last field containing point.
901(make-variable-buffer-local 'widget-field-last)
902
903(defvar widget-field-was nil)
904;; The widget data before the change.
905(make-variable-buffer-local 'widget-field-was)
906
907(defun widget-field-find (pos)
908 ;; Find widget whose editing field is located at POS.
909 ;; Return nil if POS is not inside and editing field.
910 ;;
911 ;; This is only used in `widget-field-modified', since ordinarily
912 ;; you would just test the field property.
913 (let ((fields widget-field-list)
914 field found)
915 (while fields
916 (setq field (car fields)
917 fields (cdr fields))
918 (let ((from (widget-get field :value-from))
919 (to (widget-get field :value-to)))
920 (if (and from to (< from pos) (> to pos))
921 (setq fields nil
922 found field))))
923 found))
924
925(defun widget-after-change (from to old)
926 ;; Adjust field size and text properties.
927 (condition-case nil
928 (let ((field (widget-field-find from))
929 (inhibit-read-only t))
930 (cond ((null field))
931 ((not (eq field (widget-field-find to)))
932 (debug)
933 (message "Error: `widget-after-change' called on two fields"))
934 (t
935 (let ((size (widget-get field :size)))
936 (if size
937 (let ((begin (1+ (widget-get field :value-from)))
938 (end (1- (widget-get field :value-to))))
939 (widget-specify-field-update field begin end)
940 (cond ((< (- end begin) size)
941 ;; Field too small.
942 (save-excursion
943 (goto-char end)
944 (insert-char ?\ (- (+ begin size) end))
945 (widget-specify-field-update field
946 begin
947 (+ begin size))))
948 ((> (- end begin) size)
949 ;; Field too large and
950 (if (or (< (point) (+ begin size))
951 (> (point) end))
952 ;; Point is outside extra space.
953 (setq begin (+ begin size))
954 ;; Point is within the extra space.
955 (setq begin (point)))
956 (save-excursion
957 (goto-char end)
958 (while (and (eq (preceding-char) ?\ )
959 (> (point) begin))
960 (delete-backward-char 1))))))
961 (widget-specify-field-update field from to)))
962 (widget-apply field :notify field))))
963 (error (debug))))
964
965;;; Widget Functions
966;;
967;; These functions are used in the definition of multiple widgets.
968
969(defun widget-children-value-delete (widget)
970 "Delete all :children and :buttons in WIDGET."
971 (mapcar 'widget-delete (widget-get widget :children))
972 (widget-put widget :children nil)
973 (mapcar 'widget-delete (widget-get widget :buttons))
974 (widget-put widget :buttons nil))
975
976(defun widget-types-convert-widget (widget)
977 "Convert :args as widget types in WIDGET."
978 (widget-put widget :args (mapcar 'widget-convert (widget-get widget :args)))
979 widget)
980
981;;; The `default' Widget.
982
983(define-widget 'default nil
984 "Basic widget other widgets are derived from."
985 :value-to-internal (lambda (widget value) value)
986 :value-to-external (lambda (widget value) value)
987 :create 'widget-default-create
988 :indent nil
989 :offset 0
990 :format-handler 'widget-default-format-handler
991 :button-face-get 'widget-default-button-face-get
992 :sample-face-get 'widget-default-sample-face-get
993 :delete 'widget-default-delete
994 :value-set 'widget-default-value-set
995 :value-inline 'widget-default-value-inline
996 :menu-tag-get 'widget-default-menu-tag-get
997 :validate (lambda (widget) nil)
998 :active 'widget-default-active
999 :activate 'widget-specify-active
1000 :deactivate 'widget-default-deactivate
1001 :action 'widget-default-action
1002 :notify 'widget-default-notify)
1003
1004(defun widget-default-create (widget)
1005 "Create WIDGET at point in the current buffer."
1006 (widget-specify-insert
1007 (let ((from (point))
1008 (tag (widget-get widget :tag))
1009 (glyph (widget-get widget :tag-glyph))
1010 (doc (widget-get widget :doc))
1011 button-begin button-end
1012 sample-begin sample-end
1013 doc-begin doc-end
1014 value-pos)
1015 (insert (widget-get widget :format))
1016 (goto-char from)
1017 ;; Parse escapes in format.
1018 (while (re-search-forward "%\\(.\\)" nil t)
1019 (let ((escape (aref (match-string 1) 0)))
1020 (replace-match "" t t)
1021 (cond ((eq escape ?%)
1022 (insert "%"))
1023 ((eq escape ?\[)
1024 (setq button-begin (point)))
1025 ((eq escape ?\])
1026 (setq button-end (point)))
1027 ((eq escape ?\{)
1028 (setq sample-begin (point)))
1029 ((eq escape ?\})
1030 (setq sample-end (point)))
1031 ((eq escape ?n)
1032 (when (widget-get widget :indent)
1033 (insert "\n")
1034 (insert-char ? (widget-get widget :indent))))
1035 ((eq escape ?t)
1036 (cond (glyph
1037 (widget-glyph-insert widget (or tag "image") glyph))
1038 (tag
1039 (insert tag))
1040 (t
1041 (let ((standard-output (current-buffer)))
1042 (princ (widget-get widget :value))))))
1043 ((eq escape ?d)
1044 (when doc
1045 (setq doc-begin (point))
1046 (insert doc)
1047 (while (eq (preceding-char) ?\n)
1048 (delete-backward-char 1))
1049 (insert "\n")
1050 (setq doc-end (point))))
1051 ((eq escape ?v)
1052 (if (and button-begin (not button-end))
1053 (widget-apply widget :value-create)
1054 (setq value-pos (point))))
1055 (t
1056 (widget-apply widget :format-handler escape)))))
1057 ;; Specify button, sample, and doc, and insert value.
1058 (and button-begin button-end
1059 (widget-specify-button widget button-begin button-end))
1060 (and sample-begin sample-end
1061 (widget-specify-sample widget sample-begin sample-end))
1062 (and doc-begin doc-end
1063 (widget-specify-doc widget doc-begin doc-end))
1064 (when value-pos
1065 (goto-char value-pos)
1066 (widget-apply widget :value-create)))
1067 (let ((from (copy-marker (point-min)))
1068 (to (copy-marker (point-max))))
1069 (widget-specify-text from to)
1070 (set-marker-insertion-type from t)
1071 (set-marker-insertion-type to nil)
1072 (widget-put widget :from from)
1073 (widget-put widget :to to))))
1074
1075(defun widget-default-format-handler (widget escape)
1076 ;; We recognize the %h escape by default.
1077 (let* ((buttons (widget-get widget :buttons))
1078 (doc-property (widget-get widget :documentation-property))
1079 (doc-try (cond ((widget-get widget :doc))
1080 ((symbolp doc-property)
1081 (documentation-property (widget-get widget :value)
1082 doc-property))
1083 (t
1084 (funcall doc-property (widget-get widget :value)))))
1085 (doc-text (and (stringp doc-try)
1086 (> (length doc-try) 1)
1087 doc-try)))
1088 (cond ((eq escape ?h)
1089 (when doc-text
1090 (and (eq (preceding-char) ?\n)
1091 (widget-get widget :indent)
1092 (insert-char ? (widget-get widget :indent)))
1093 ;; The `*' in the beginning is redundant.
1094 (when (eq (aref doc-text 0) ?*)
1095 (setq doc-text (substring doc-text 1)))
1096 ;; Get rid of trailing newlines.
1097 (when (string-match "\n+\\'" doc-text)
1098 (setq doc-text (substring doc-text 0 (match-beginning 0))))
1099 (push (if (string-match "\n." doc-text)
1100 ;; Allow multiline doc to be hiden.
1101 (widget-create-child-and-convert
1102 widget 'widget-help
1103 :doc (progn
1104 (string-match "\\`.*" doc-text)
1105 (match-string 0 doc-text))
1106 :widget-doc doc-text
1107 "?")
1108 ;; A single line is just inserted.
1109 (widget-create-child-and-convert
1110 widget 'item :format "%d" :doc doc-text nil))
1111 buttons)))
1112 (t
1113 (error "Unknown escape `%c'" escape)))
1114 (widget-put widget :buttons buttons)))
1115
1116(defun widget-default-button-face-get (widget)
1117 ;; Use :button-face or widget-button-face
1118 (or (widget-get widget :button-face) 'widget-button-face))
1119
1120(defun widget-default-sample-face-get (widget)
1121 ;; Use :sample-face.
1122 (widget-get widget :sample-face))
1123
1124(defun widget-default-delete (widget)
1125 ;; Remove widget from the buffer.
1126 (let ((from (widget-get widget :from))
1127 (to (widget-get widget :to))
1128 (inhibit-read-only t)
1129 after-change-functions)
1130 (widget-apply widget :value-delete)
1131 (when (< from to)
1132 ;; Kludge: this doesn't need to be true for empty formats.
1133 (delete-region from to))
1134 (set-marker from nil)
1135 (set-marker to nil)))
1136
1137(defun widget-default-value-set (widget value)
1138 ;; Recreate widget with new value.
1139 (save-excursion
1140 (goto-char (widget-get widget :from))
1141 (widget-apply widget :delete)
1142 (widget-put widget :value value)
1143 (widget-apply widget :create)))
1144
1145(defun widget-default-value-inline (widget)
1146 ;; Wrap value in a list unless it is inline.
1147 (if (widget-get widget :inline)
1148 (widget-value widget)
1149 (list (widget-value widget))))
1150
1151(defun widget-default-menu-tag-get (widget)
1152 ;; Use tag or value for menus.
1153 (or (widget-get widget :menu-tag)
1154 (widget-get widget :tag)
1155 (widget-princ-to-string (widget-get widget :value))))
1156
1157(defun widget-default-active (widget)
1158 "Return t iff this widget active (user modifiable)."
1159 (and (not (widget-get widget :inactive))
1160 (let ((parent (widget-get widget :parent)))
1161 (or (null parent)
1162 (widget-apply parent :active)))))
1163
1164(defun widget-default-deactivate (widget)
1165 "Make WIDGET inactive for user modifications."
1166 (widget-specify-inactive widget
1167 (widget-get widget :from)
1168 (widget-get widget :to)))
1169
1170(defun widget-default-action (widget &optional event)
1171 ;; Notify the parent when a widget change
1172 (let ((parent (widget-get widget :parent)))
1173 (when parent
1174 (widget-apply parent :notify widget event))))
1175
1176(defun widget-default-notify (widget child &optional event)
1177 ;; Pass notification to parent.
1178 (widget-default-action widget event))
1179
1180;;; The `item' Widget.
1181
1182(define-widget 'item 'default
1183 "Constant items for inclusion in other widgets."
1184 :convert-widget 'widget-item-convert-widget
1185 :value-create 'widget-item-value-create
1186 :value-delete 'ignore
1187 :value-get 'widget-item-value-get
1188 :match 'widget-item-match
1189 :match-inline 'widget-item-match-inline
1190 :action 'widget-item-action
1191 :format "%t\n")
1192
1193(defun widget-item-convert-widget (widget)
1194 ;; Initialize :value from :args in WIDGET.
1195 (let ((args (widget-get widget :args)))
1196 (when args
1197 (widget-put widget :value (widget-apply widget
1198 :value-to-internal (car args)))
1199 (widget-put widget :args nil)))
1200 widget)
1201
1202(defun widget-item-value-create (widget)
1203 ;; Insert the printed representation of the value.
1204 (let ((standard-output (current-buffer)))
1205 (princ (widget-get widget :value))))
1206
1207(defun widget-item-match (widget value)
1208 ;; Match if the value is the same.
1209 (equal (widget-get widget :value) value))
1210
1211(defun widget-item-match-inline (widget values)
1212 ;; Match if the value is the same.
1213 (let ((value (widget-get widget :value)))
1214 (and (listp value)
1215 (<= (length value) (length values))
1216 (let ((head (subseq values 0 (length value))))
1217 (and (equal head value)
1218 (cons head (subseq values (length value))))))))
1219
1220(defun widget-item-action (widget &optional event)
1221 ;; Just notify itself.
1222 (widget-apply widget :notify widget event))
1223
1224(defun widget-item-value-get (widget)
1225 ;; Items are simple.
1226 (widget-get widget :value))
1227
1228;;; The `push-button' Widget.
1229
1230(defcustom widget-push-button-gui t
1231 "If non nil, use GUI push buttons when available."
1232 :group 'widgets
1233 :type 'boolean)
1234
1235;; Cache already created GUI objects.
1236(defvar widget-push-button-cache nil)
1237
1238(define-widget 'push-button 'item
1239 "A pushable button."
1240 :value-create 'widget-push-button-value-create
1241 :format "%[%v%]")
1242
1243(defun widget-push-button-value-create (widget)
1244 ;; Insert text representing the `on' and `off' states.
1245 (let* ((tag (or (widget-get widget :tag)
1246 (widget-get widget :value)))
1247 (text (concat "[" tag "]"))
1248 (gui (cdr (assoc tag widget-push-button-cache))))
1249 (if (and (fboundp 'make-gui-button)
1250 (fboundp 'make-glyph)
1251 widget-push-button-gui
1252 (fboundp 'device-on-window-system-p)
1253 (device-on-window-system-p)
1254 (string-match "XEmacs" emacs-version))
1255 (progn
1256 (unless gui
1257 (setq gui (make-gui-button tag 'widget-gui-action widget))
1258 (push (cons tag gui) widget-push-button-cache))
1259 (widget-glyph-insert-glyph widget text
1260 (make-glyph (car (aref gui 1)))))
1261 (insert text))))
1262
1263(defun widget-gui-action (widget)
1264 "Apply :action for WIDGET."
1265 (widget-apply-action widget (this-command-keys)))
1266
1267;;; The `link' Widget.
1268
1269(define-widget 'link 'item
1270 "An embedded link."
1271 :help-echo "Follow the link."
1272 :format "%[_%t_%]")
1273
1274;;; The `info-link' Widget.
1275
1276(define-widget 'info-link 'link
1277 "A link to an info file."
1278 :action 'widget-info-link-action)
1279
1280(defun widget-info-link-action (widget &optional event)
1281 "Open the info node specified by WIDGET."
1282 (Info-goto-node (widget-value widget)))
1283
1284;;; The `url-link' Widget.
1285
1286(define-widget 'url-link 'link
1287 "A link to an www page."
1288 :action 'widget-url-link-action)
1289
1290(defun widget-url-link-action (widget &optional event)
1291 "Open the url specified by WIDGET."
1292 (require 'browse-url)
1293 (funcall browse-url-browser-function (widget-value widget)))
1294
1295;;; The `editable-field' Widget.
1296
1297(define-widget 'editable-field 'default
1298 "An editable text field."
1299 :convert-widget 'widget-item-convert-widget
1300 :keymap widget-field-keymap
1301 :format "%v"
1302 :value ""
1303 :action 'widget-field-action
1304 :validate 'widget-field-validate
1305 :valid-regexp ""
1306 :error "No match"
1307 :value-create 'widget-field-value-create
1308 :value-delete 'widget-field-value-delete
1309 :value-get 'widget-field-value-get
1310 :match 'widget-field-match)
1311
1312;; History of field minibuffer edits.
1313(defvar widget-field-history nil)
1314
1315(defun widget-field-action (widget &optional event)
1316 ;; Edit the value in the minibuffer.
1317 (let ((tag (widget-apply widget :menu-tag-get))
1318 (invalid (widget-apply widget :validate)))
1319 (when invalid
1320 (error (widget-get invalid :error)))
1321 (widget-value-set widget
1322 (widget-apply widget
1323 :value-to-external
1324 (read-string (concat tag ": ")
1325 (widget-apply
1326 widget
1327 :value-to-internal
1328 (widget-value widget))
1329 'widget-field-history)))
1330 (widget-apply widget :notify widget event)
1331 (widget-setup)))
1332
1333(defun widget-field-validate (widget)
1334 ;; Valid if the content matches `:valid-regexp'.
1335 (save-excursion
1336 (let ((value (widget-apply widget :value-get))
1337 (regexp (widget-get widget :valid-regexp)))
1338 (if (string-match regexp value)
1339 nil
1340 widget))))
1341
1342(defun widget-field-value-create (widget)
1343 ;; Create an editable text field.
1344 (insert " ")
1345 (let ((size (widget-get widget :size))
1346 (value (widget-get widget :value))
1347 (from (point)))
1348 (insert value)
1349 (and size
1350 (< (length value) size)
1351 (insert-char ?\ (- size (length value))))
1352 (unless (memq widget widget-field-list)
1353 (setq widget-field-new (cons widget widget-field-new)))
1354 (widget-put widget :value-to (copy-marker (point)))
1355 (set-marker-insertion-type (widget-get widget :value-to) nil)
1356 (if (null size)
1357 (insert ?\n)
1358 (insert ?\ ))
1359 (widget-put widget :value-from (copy-marker from))
1360 (set-marker-insertion-type (widget-get widget :value-from) t)))
1361
1362(defun widget-field-value-delete (widget)
1363 ;; Remove the widget from the list of active editing fields.
1364 (setq widget-field-list (delq widget widget-field-list))
1365 ;; These are nil if the :format string doesn't contain `%v'.
1366 (when (widget-get widget :value-from)
1367 (set-marker (widget-get widget :value-from) nil))
1368 (when (widget-get widget :value-from)
1369 (set-marker (widget-get widget :value-to) nil)))
1370
1371(defun widget-field-value-get (widget)
1372 ;; Return current text in editing field.
1373 (let ((from (widget-get widget :value-from))
1374 (to (widget-get widget :value-to))
1375 (size (widget-get widget :size))
1376 (secret (widget-get widget :secret))
1377 (old (current-buffer)))
1378 (if (and from to)
1379 (progn
1380 (set-buffer (marker-buffer from))
1381 (setq from (1+ from)
1382 to (1- to))
1383 (while (and size
1384 (not (zerop size))
1385 (> to from)
1386 (eq (char-after (1- to)) ?\ ))
1387 (setq to (1- to)))
1388 (let ((result (buffer-substring-no-properties from to)))
1389 (when secret
1390 (let ((index 0))
1391 (while (< (+ from index) to)
1392 (aset result index
1393 (get-text-property (+ from index) 'secret))
1394 (setq index (1+ index)))))
1395 (set-buffer old)
1396 result))
1397 (widget-get widget :value))))
1398
1399(defun widget-field-match (widget value)
1400 ;; Match any string.
1401 (stringp value))
1402
1403;;; The `text' Widget.
1404
1405(define-widget 'text 'editable-field
1406 :keymap widget-text-keymap
1407 "A multiline text area.")
1408
1409;;; The `menu-choice' Widget.
1410
1411(define-widget 'menu-choice 'default
1412 "A menu of options."
1413 :convert-widget 'widget-types-convert-widget
1414 :format "%[%t%]: %v"
1415 :case-fold t
1416 :tag "choice"
1417 :void '(item :format "invalid (%t)\n")
1418 :value-create 'widget-choice-value-create
1419 :value-delete 'widget-children-value-delete
1420 :value-get 'widget-choice-value-get
1421 :value-inline 'widget-choice-value-inline
1422 :action 'widget-choice-action
1423 :error "Make a choice"
1424 :validate 'widget-choice-validate
1425 :match 'widget-choice-match
1426 :match-inline 'widget-choice-match-inline)
1427
1428(defun widget-choice-value-create (widget)
1429 ;; Insert the first choice that matches the value.
1430 (let ((value (widget-get widget :value))
1431 (args (widget-get widget :args))
1432 current)
1433 (while args
1434 (setq current (car args)
1435 args (cdr args))
1436 (when (widget-apply current :match value)
1437 (widget-put widget :children (list (widget-create-child-value
1438 widget current value)))
1439 (widget-put widget :choice current)
1440 (setq args nil
1441 current nil)))
1442 (when current
1443 (let ((void (widget-get widget :void)))
1444 (widget-put widget :children (list (widget-create-child-and-convert
1445 widget void :value value)))
1446 (widget-put widget :choice void)))))
1447
1448(defun widget-choice-value-get (widget)
1449 ;; Get value of the child widget.
1450 (widget-value (car (widget-get widget :children))))
1451
1452(defun widget-choice-value-inline (widget)
1453 ;; Get value of the child widget.
1454 (widget-apply (car (widget-get widget :children)) :value-inline))
1455
1456(defun widget-choice-action (widget &optional event)
1457 ;; Make a choice.
1458 (let ((args (widget-get widget :args))
1459 (old (widget-get widget :choice))
1460 (tag (widget-apply widget :menu-tag-get))
1461 (completion-ignore-case (widget-get widget :case-fold))
1462 current choices)
1463 ;; Remember old value.
1464 (if (and old (not (widget-apply widget :validate)))
1465 (let* ((external (widget-value widget))
1466 (internal (widget-apply old :value-to-internal external)))
1467 (widget-put old :value internal)))
1468 ;; Find new choice.
1469 (setq current
1470 (cond ((= (length args) 0)
1471 nil)
1472 ((= (length args) 1)
1473 (nth 0 args))
1474 ((and (= (length args) 2)
1475 (memq old args))
1476 (if (eq old (nth 0 args))
1477 (nth 1 args)
1478 (nth 0 args)))
1479 (t
1480 (while args
1481 (setq current (car args)
1482 args (cdr args))
1483 (setq choices
1484 (cons (cons (widget-apply current :menu-tag-get)
1485 current)
1486 choices)))
1487 (widget-choose tag (reverse choices) event))))
1488 (when current
1489 (widget-value-set widget
1490 (widget-apply current :value-to-external
1491 (widget-get current :value)))
1492 (widget-apply widget :notify widget event)
1493 (widget-setup)))
1494 ;; Notify parent.
1495 (widget-apply widget :notify widget event)
1496 (widget-clear-undo))
1497
1498(defun widget-choice-validate (widget)
1499 ;; Valid if we have made a valid choice.
1500 (let ((void (widget-get widget :void))
1501 (choice (widget-get widget :choice))
1502 (child (car (widget-get widget :children))))
1503 (if (eq void choice)
1504 widget
1505 (widget-apply child :validate))))
1506
1507(defun widget-choice-match (widget value)
1508 ;; Matches if one of the choices matches.
1509 (let ((args (widget-get widget :args))
1510 current found)
1511 (while (and args (not found))
1512 (setq current (car args)
1513 args (cdr args)
1514 found (widget-apply current :match value)))
1515 found))
1516
1517(defun widget-choice-match-inline (widget values)
1518 ;; Matches if one of the choices matches.
1519 (let ((args (widget-get widget :args))
1520 current found)
1521 (while (and args (null found))
1522 (setq current (car args)
1523 args (cdr args)
1524 found (widget-match-inline current values)))
1525 found))
1526
1527;;; The `toggle' Widget.
1528
1529(define-widget 'toggle 'item
1530 "Toggle between two states."
1531 :format "%[%v%]\n"
1532 :value-create 'widget-toggle-value-create
1533 :action 'widget-toggle-action
1534 :match (lambda (widget value) t)
1535 :on "on"
1536 :off "off")
1537
1538(defun widget-toggle-value-create (widget)
1539 ;; Insert text representing the `on' and `off' states.
1540 (if (widget-value widget)
1541 (widget-glyph-insert widget
1542 (widget-get widget :on)
1543 (widget-get widget :on-glyph))
1544 (widget-glyph-insert widget
1545 (widget-get widget :off)
1546 (widget-get widget :off-glyph))))
1547
1548(defun widget-toggle-action (widget &optional event)
1549 ;; Toggle value.
1550 (widget-value-set widget (not (widget-value widget)))
1551 (widget-apply widget :notify widget event))
1552
1553;;; The `checkbox' Widget.
1554
1555(define-widget 'checkbox 'toggle
1556 "A checkbox toggle."
1557 :format "%[%v%]"
1558 :on "[X]"
1559 :on-glyph "check1"
1560 :off "[ ]"
1561 :off-glyph "check0"
1562 :action 'widget-checkbox-action)
1563
1564(defun widget-checkbox-action (widget &optional event)
1565 "Toggle checkbox, notify parent, and set active state of sibling."
1566 (widget-toggle-action widget event)
1567 (let ((sibling (widget-get-sibling widget)))
1568 (when sibling
1569 (if (widget-value widget)
1570 (widget-apply sibling :activate)
1571 (widget-apply sibling :deactivate)))))
1572
1573;;; The `checklist' Widget.
1574
1575(define-widget 'checklist 'default
1576 "A multiple choice widget."
1577 :convert-widget 'widget-types-convert-widget
1578 :format "%v"
1579 :offset 4
1580 :entry-format "%b %v"
1581 :menu-tag "checklist"
1582 :greedy nil
1583 :value-create 'widget-checklist-value-create
1584 :value-delete 'widget-children-value-delete
1585 :value-get 'widget-checklist-value-get
1586 :validate 'widget-checklist-validate
1587 :match 'widget-checklist-match
1588 :match-inline 'widget-checklist-match-inline)
1589
1590(defun widget-checklist-value-create (widget)
1591 ;; Insert all values
1592 (let ((alist (widget-checklist-match-find widget (widget-get widget :value)))
1593 (args (widget-get widget :args)))
1594 (while args
1595 (widget-checklist-add-item widget (car args) (assq (car args) alist))
1596 (setq args (cdr args)))
1597 (widget-put widget :children (nreverse (widget-get widget :children)))))
1598
1599(defun widget-checklist-add-item (widget type chosen)
1600 ;; Create checklist item in WIDGET of type TYPE.
1601 ;; If the item is checked, CHOSEN is a cons whose cdr is the value.
1602 (and (eq (preceding-char) ?\n)
1603 (widget-get widget :indent)
1604 (insert-char ? (widget-get widget :indent)))
1605 (widget-specify-insert
1606 (let* ((children (widget-get widget :children))
1607 (buttons (widget-get widget :buttons))
1608 (button-args (or (widget-get type :sibling-args)
1609 (widget-get widget :button-args)))
1610 (from (point))
1611 child button)
1612 (insert (widget-get widget :entry-format))
1613 (goto-char from)
1614 ;; Parse % escapes in format.
1615 (while (re-search-forward "%\\([bv%]\\)" nil t)
1616 (let ((escape (aref (match-string 1) 0)))
1617 (replace-match "" t t)
1618 (cond ((eq escape ?%)
1619 (insert "%"))
1620 ((eq escape ?b)
1621 (setq button (apply 'widget-create-child-and-convert
1622 widget 'checkbox
1623 :value (not (null chosen))
1624 button-args)))
1625 ((eq escape ?v)
1626 (setq child
1627 (cond ((not chosen)
1628 (let ((child (widget-create-child widget type)))
1629 (widget-apply child :deactivate)
1630 child))
1631 ((widget-get type :inline)
1632 (widget-create-child-value
1633 widget type (cdr chosen)))
1634 (t
1635 (widget-create-child-value
1636 widget type (car (cdr chosen)))))))
1637 (t
1638 (error "Unknown escape `%c'" escape)))))
1639 ;; Update properties.
1640 (and button child (widget-put child :button button))
1641 (and button (widget-put widget :buttons (cons button buttons)))
1642 (and child (widget-put widget :children (cons child children))))))
1643
1644(defun widget-checklist-match (widget values)
1645 ;; All values must match a type in the checklist.
1646 (and (listp values)
1647 (null (cdr (widget-checklist-match-inline widget values)))))
1648
1649(defun widget-checklist-match-inline (widget values)
1650 ;; Find the values which match a type in the checklist.
1651 (let ((greedy (widget-get widget :greedy))
1652 (args (copy-list (widget-get widget :args)))
1653 found rest)
1654 (while values
1655 (let ((answer (widget-checklist-match-up args values)))
1656 (cond (answer
1657 (let ((vals (widget-match-inline answer values)))
1658 (setq found (append found (car vals))
1659 values (cdr vals)
1660 args (delq answer args))))
1661 (greedy
1662 (setq rest (append rest (list (car values)))
1663 values (cdr values)))
1664 (t
1665 (setq rest (append rest values)
1666 values nil)))))
1667 (cons found rest)))
1668
1669(defun widget-checklist-match-find (widget vals)
1670 ;; Find the vals which match a type in the checklist.
1671 ;; Return an alist of (TYPE MATCH).
1672 (let ((greedy (widget-get widget :greedy))
1673 (args (copy-list (widget-get widget :args)))
1674 found)
1675 (while vals
1676 (let ((answer (widget-checklist-match-up args vals)))
1677 (cond (answer
1678 (let ((match (widget-match-inline answer vals)))
1679 (setq found (cons (cons answer (car match)) found)
1680 vals (cdr match)
1681 args (delq answer args))))
1682 (greedy
1683 (setq vals (cdr vals)))
1684 (t
1685 (setq vals nil)))))
1686 found))
1687
1688(defun widget-checklist-match-up (args vals)
1689 ;; Rerturn the first type from ARGS that matches VALS.
1690 (let (current found)
1691 (while (and args (null found))
1692 (setq current (car args)
1693 args (cdr args)
1694 found (widget-match-inline current vals)))
1695 (if found
1696 current
1697 nil)))
1698
1699(defun widget-checklist-value-get (widget)
1700 ;; The values of all selected items.
1701 (let ((children (widget-get widget :children))
1702 child result)
1703 (while children
1704 (setq child (car children)
1705 children (cdr children))
1706 (if (widget-value (widget-get child :button))
1707 (setq result (append result (widget-apply child :value-inline)))))
1708 result))
1709
1710(defun widget-checklist-validate (widget)
1711 ;; Ticked chilren must be valid.
1712 (let ((children (widget-get widget :children))
1713 child button found)
1714 (while (and children (not found))
1715 (setq child (car children)
1716 children (cdr children)
1717 button (widget-get child :button)
1718 found (and (widget-value button)
1719 (widget-apply child :validate))))
1720 found))
1721
1722;;; The `option' Widget
1723
1724(define-widget 'option 'checklist
1725 "An widget with an optional item."
1726 :inline t)
1727
1728;;; The `choice-item' Widget.
1729
1730(define-widget 'choice-item 'item
1731 "Button items that delegate action events to their parents."
1732 :action 'widget-choice-item-action
1733 :format "%[%t%] \n")
1734
1735(defun widget-choice-item-action (widget &optional event)
1736 ;; Tell parent what happened.
1737 (widget-apply (widget-get widget :parent) :action event))
1738
1739;;; The `radio-button' Widget.
1740
1741(define-widget 'radio-button 'toggle
1742 "A radio button for use in the `radio' widget."
1743 :notify 'widget-radio-button-notify
1744 :format "%[%v%]"
1745 :on "(*)"
1746 :on-glyph "radio1"
1747 :off "( )"
1748 :off-glyph "radio0")
1749
1750(defun widget-radio-button-notify (widget child &optional event)
1751 ;; Tell daddy.
1752 (widget-apply (widget-get widget :parent) :action widget event))
1753
1754;;; The `radio-button-choice' Widget.
1755
1756(define-widget 'radio-button-choice 'default
1757 "Select one of multiple options."
1758 :convert-widget 'widget-types-convert-widget
1759 :offset 4
1760 :format "%v"
1761 :entry-format "%b %v"
1762 :menu-tag "radio"
1763 :value-create 'widget-radio-value-create
1764 :value-delete 'widget-children-value-delete
1765 :value-get 'widget-radio-value-get
1766 :value-inline 'widget-radio-value-inline
1767 :value-set 'widget-radio-value-set
1768 :error "You must push one of the buttons"
1769 :validate 'widget-radio-validate
1770 :match 'widget-choice-match
1771 :match-inline 'widget-choice-match-inline
1772 :action 'widget-radio-action)
1773
1774(defun widget-radio-value-create (widget)
1775 ;; Insert all values
1776 (let ((args (widget-get widget :args))
1777 arg)
1778 (while args
1779 (setq arg (car args)
1780 args (cdr args))
1781 (widget-radio-add-item widget arg))))
1782
1783(defun widget-radio-add-item (widget type)
1784 "Add to radio widget WIDGET a new radio button item of type TYPE."
1785 ;; (setq type (widget-convert type))
1786 (and (eq (preceding-char) ?\n)
1787 (widget-get widget :indent)
1788 (insert-char ? (widget-get widget :indent)))
1789 (widget-specify-insert
1790 (let* ((value (widget-get widget :value))
1791 (children (widget-get widget :children))
1792 (buttons (widget-get widget :buttons))
1793 (button-args (or (widget-get type :sibling-args)
1794 (widget-get widget :button-args)))
1795 (from (point))
1796 (chosen (and (null (widget-get widget :choice))
1797 (widget-apply type :match value)))
1798 child button)
1799 (insert (widget-get widget :entry-format))
1800 (goto-char from)
1801 ;; Parse % escapes in format.
1802 (while (re-search-forward "%\\([bv%]\\)" nil t)
1803 (let ((escape (aref (match-string 1) 0)))
1804 (replace-match "" t t)
1805 (cond ((eq escape ?%)
1806 (insert "%"))
1807 ((eq escape ?b)
1808 (setq button (apply 'widget-create-child-and-convert
1809 widget 'radio-button
1810 :value (not (null chosen))
1811 button-args)))
1812 ((eq escape ?v)
1813 (setq child (if chosen
1814 (widget-create-child-value
1815 widget type value)
1816 (widget-create-child widget type)))
1817 (unless chosen
1818 (widget-apply child :deactivate)))
1819 (t
1820 (error "Unknown escape `%c'" escape)))))
1821 ;; Update properties.
1822 (when chosen
1823 (widget-put widget :choice type))
1824 (when button
1825 (widget-put child :button button)
1826 (widget-put widget :buttons (nconc buttons (list button))))
1827 (when child
1828 (widget-put widget :children (nconc children (list child))))
1829 child)))
1830
1831(defun widget-radio-value-get (widget)
1832 ;; Get value of the child widget.
1833 (let ((chosen (widget-radio-chosen widget)))
1834 (and chosen (widget-value chosen))))
1835
1836(defun widget-radio-chosen (widget)
1837 "Return the widget representing the chosen radio button."
1838 (let ((children (widget-get widget :children))
1839 current found)
1840 (while children
1841 (setq current (car children)
1842 children (cdr children))
1843 (let* ((button (widget-get current :button))
1844 (value (widget-apply button :value-get)))
1845 (when value
1846 (setq found current
1847 children nil))))
1848 found))
1849
1850(defun widget-radio-value-inline (widget)
1851 ;; Get value of the child widget.
1852 (let ((children (widget-get widget :children))
1853 current found)
1854 (while children
1855 (setq current (car children)
1856 children (cdr children))
1857 (let* ((button (widget-get current :button))
1858 (value (widget-apply button :value-get)))
1859 (when value
1860 (setq found (widget-apply current :value-inline)
1861 children nil))))
1862 found))
1863
1864(defun widget-radio-value-set (widget value)
1865 ;; We can't just delete and recreate a radio widget, since children
1866 ;; can be added after the original creation and won't be recreated
1867 ;; by `:create'.
1868 (let ((children (widget-get widget :children))
1869 current found)
1870 (while children
1871 (setq current (car children)
1872 children (cdr children))
1873 (let* ((button (widget-get current :button))
1874 (match (and (not found)
1875 (widget-apply current :match value))))
1876 (widget-value-set button match)
1877 (if match
1878 (progn
1879 (widget-value-set current value)
1880 (widget-apply current :activate))
1881 (widget-apply current :deactivate))
1882 (setq found (or found match))))))
1883
1884(defun widget-radio-validate (widget)
1885 ;; Valid if we have made a valid choice.
1886 (let ((children (widget-get widget :children))
1887 current found button)
1888 (while (and children (not found))
1889 (setq current (car children)
1890 children (cdr children)
1891 button (widget-get current :button)
1892 found (widget-apply button :value-get)))
1893 (if found
1894 (widget-apply current :validate)
1895 widget)))
1896
1897(defun widget-radio-action (widget child event)
1898 ;; Check if a radio button was pressed.
1899 (let ((children (widget-get widget :children))
1900 (buttons (widget-get widget :buttons))
1901 current)
1902 (when (memq child buttons)
1903 (while children
1904 (setq current (car children)
1905 children (cdr children))
1906 (let* ((button (widget-get current :button)))
1907 (cond ((eq child button)
1908 (widget-value-set button t)
1909 (widget-apply current :activate))
1910 ((widget-value button)
1911 (widget-value-set button nil)
1912 (widget-apply current :deactivate)))))))
1913 ;; Pass notification to parent.
1914 (widget-apply widget :notify child event))
1915
1916;;; The `insert-button' Widget.
1917
1918(define-widget 'insert-button 'push-button
1919 "An insert button for the `editable-list' widget."
1920 :tag "INS"
1921 :help-echo "Insert a new item into the list at this position."
1922 :action 'widget-insert-button-action)
1923
1924(defun widget-insert-button-action (widget &optional event)
1925 ;; Ask the parent to insert a new item.
1926 (widget-apply (widget-get widget :parent)
1927 :insert-before (widget-get widget :widget)))
1928
1929;;; The `delete-button' Widget.
1930
1931(define-widget 'delete-button 'push-button
1932 "A delete button for the `editable-list' widget."
1933 :tag "DEL"
1934 :help-echo "Delete this item from the list."
1935 :action 'widget-delete-button-action)
1936
1937(defun widget-delete-button-action (widget &optional event)
1938 ;; Ask the parent to insert a new item.
1939 (widget-apply (widget-get widget :parent)
1940 :delete-at (widget-get widget :widget)))
1941
1942;;; The `editable-list' Widget.
1943
1944(defcustom widget-editable-list-gui nil
1945 "If non nil, use GUI push-buttons in editable list when available."
1946 :type 'boolean
1947 :group 'widgets)
1948
1949(define-widget 'editable-list 'default
1950 "A variable list of widgets of the same type."
1951 :convert-widget 'widget-types-convert-widget
1952 :offset 12
1953 :format "%v%i\n"
1954 :format-handler 'widget-editable-list-format-handler
1955 :entry-format "%i %d %v"
1956 :menu-tag "editable-list"
1957 :value-create 'widget-editable-list-value-create
1958 :value-delete 'widget-children-value-delete
1959 :value-get 'widget-editable-list-value-get
1960 :validate 'widget-editable-list-validate
1961 :match 'widget-editable-list-match
1962 :match-inline 'widget-editable-list-match-inline
1963 :insert-before 'widget-editable-list-insert-before
1964 :delete-at 'widget-editable-list-delete-at)
1965
1966(defun widget-editable-list-format-handler (widget escape)
1967 ;; We recognize the insert button.
1968 (let ((widget-push-button-gui widget-editable-list-gui))
1969 (cond ((eq escape ?i)
1970 (and (widget-get widget :indent)
1971 (insert-char ? (widget-get widget :indent)))
1972 (apply 'widget-create-child-and-convert
1973 widget 'insert-button
1974 (widget-get widget :append-button-args)))
1975 (t
1976 (widget-default-format-handler widget escape)))))
1977
1978(defun widget-editable-list-value-create (widget)
1979 ;; Insert all values
1980 (let* ((value (widget-get widget :value))
1981 (type (nth 0 (widget-get widget :args)))
1982 (inlinep (widget-get type :inline))
1983 children)
1984 (widget-put widget :value-pos (copy-marker (point)))
1985 (set-marker-insertion-type (widget-get widget :value-pos) t)
1986 (while value
1987 (let ((answer (widget-match-inline type value)))
1988 (if answer
1989 (setq children (cons (widget-editable-list-entry-create
1990 widget
1991 (if inlinep
1992 (car answer)
1993 (car (car answer)))
1994 t)
1995 children)
1996 value (cdr answer))
1997 (setq value nil))))
1998 (widget-put widget :children (nreverse children))))
1999
2000(defun widget-editable-list-value-get (widget)
2001 ;; Get value of the child widget.
2002 (apply 'append (mapcar (lambda (child) (widget-apply child :value-inline))
2003 (widget-get widget :children))))
2004
2005(defun widget-editable-list-validate (widget)
2006 ;; All the chilren must be valid.
2007 (let ((children (widget-get widget :children))
2008 child found)
2009 (while (and children (not found))
2010 (setq child (car children)
2011 children (cdr children)
2012 found (widget-apply child :validate)))
2013 found))
2014
2015(defun widget-editable-list-match (widget value)
2016 ;; Value must be a list and all the members must match the type.
2017 (and (listp value)
2018 (null (cdr (widget-editable-list-match-inline widget value)))))
2019
2020(defun widget-editable-list-match-inline (widget value)
2021 (let ((type (nth 0 (widget-get widget :args)))
2022 (ok t)
2023 found)
2024 (while (and value ok)
2025 (let ((answer (widget-match-inline type value)))
2026 (if answer
2027 (setq found (append found (car answer))
2028 value (cdr answer))
2029 (setq ok nil))))
2030 (cons found value)))
2031
2032(defun widget-editable-list-insert-before (widget before)
2033 ;; Insert a new child in the list of children.
2034 (save-excursion
2035 (let ((children (widget-get widget :children))
2036 (inhibit-read-only t)
2037 after-change-functions)
2038 (cond (before
2039 (goto-char (widget-get before :entry-from)))
2040 (t
2041 (goto-char (widget-get widget :value-pos))))
2042 (let ((child (widget-editable-list-entry-create
2043 widget nil nil)))
2044 (when (< (widget-get child :entry-from) (widget-get widget :from))
2045 (set-marker (widget-get widget :from)
2046 (widget-get child :entry-from)))
2047 (widget-specify-text (widget-get child :entry-from)
2048 (widget-get child :entry-to))
2049 (if (eq (car children) before)
2050 (widget-put widget :children (cons child children))
2051 (while (not (eq (car (cdr children)) before))
2052 (setq children (cdr children)))
2053 (setcdr children (cons child (cdr children)))))))
2054 (widget-setup)
2055 widget (widget-apply widget :notify widget))
2056
2057(defun widget-editable-list-delete-at (widget child)
2058 ;; Delete child from list of children.
2059 (save-excursion
2060 (let ((buttons (copy-list (widget-get widget :buttons)))
2061 button
2062 (inhibit-read-only t)
2063 after-change-functions)
2064 (while buttons
2065 (setq button (car buttons)
2066 buttons (cdr buttons))
2067 (when (eq (widget-get button :widget) child)
2068 (widget-put widget
2069 :buttons (delq button (widget-get widget :buttons)))
2070 (widget-delete button))))
2071 (let ((entry-from (widget-get child :entry-from))
2072 (entry-to (widget-get child :entry-to))
2073 (inhibit-read-only t)
2074 after-change-functions)
2075 (widget-delete child)
2076 (delete-region entry-from entry-to)
2077 (set-marker entry-from nil)
2078 (set-marker entry-to nil))
2079 (widget-put widget :children (delq child (widget-get widget :children))))
2080 (widget-setup)
2081 (widget-apply widget :notify widget))
2082
2083(defun widget-editable-list-entry-create (widget value conv)
2084 ;; Create a new entry to the list.
2085 (let ((type (nth 0 (widget-get widget :args)))
2086 (widget-push-button-gui widget-editable-list-gui)
2087 child delete insert)
2088 (widget-specify-insert
2089 (save-excursion
2090 (and (widget-get widget :indent)
2091 (insert-char ? (widget-get widget :indent)))
2092 (insert (widget-get widget :entry-format)))
2093 ;; Parse % escapes in format.
2094 (while (re-search-forward "%\\(.\\)" nil t)
2095 (let ((escape (aref (match-string 1) 0)))
2096 (replace-match "" t t)
2097 (cond ((eq escape ?%)
2098 (insert "%"))
2099 ((eq escape ?i)
2100 (setq insert (apply 'widget-create-child-and-convert
2101 widget 'insert-button
2102 (widget-get widget :insert-button-args))))
2103 ((eq escape ?d)
2104 (setq delete (apply 'widget-create-child-and-convert
2105 widget 'delete-button
2106 (widget-get widget :delete-button-args))))
2107 ((eq escape ?v)
2108 (if conv
2109 (setq child (widget-create-child-value
2110 widget type value))
2111 (setq child (widget-create-child widget type))))
2112 (t
2113 (error "Unknown escape `%c'" escape)))))
2114 (widget-put widget
2115 :buttons (cons delete
2116 (cons insert
2117 (widget-get widget :buttons))))
2118 (let ((entry-from (copy-marker (point-min)))
2119 (entry-to (copy-marker (point-max))))
2120 (widget-specify-text entry-from entry-to)
2121 (set-marker-insertion-type entry-from t)
2122 (set-marker-insertion-type entry-to nil)
2123 (widget-put child :entry-from entry-from)
2124 (widget-put child :entry-to entry-to)))
2125 (widget-put insert :widget child)
2126 (widget-put delete :widget child)
2127 child))
2128
2129;;; The `group' Widget.
2130
2131(define-widget 'group 'default
2132 "A widget which group other widgets inside."
2133 :convert-widget 'widget-types-convert-widget
2134 :format "%v"
2135 :value-create 'widget-group-value-create
2136 :value-delete 'widget-children-value-delete
2137 :value-get 'widget-editable-list-value-get
2138 :validate 'widget-editable-list-validate
2139 :match 'widget-group-match
2140 :match-inline 'widget-group-match-inline)
2141
2142(defun widget-group-value-create (widget)
2143 ;; Create each component.
2144 (let ((args (widget-get widget :args))
2145 (value (widget-get widget :value))
2146 arg answer children)
2147 (while args
2148 (setq arg (car args)
2149 args (cdr args)
2150 answer (widget-match-inline arg value)
2151 value (cdr answer))
2152 (and (eq (preceding-char) ?\n)
2153 (widget-get widget :indent)
2154 (insert-char ? (widget-get widget :indent)))
2155 (push (cond ((null answer)
2156 (widget-create-child widget arg))
2157 ((widget-get arg :inline)
2158 (widget-create-child-value widget arg (car answer)))
2159 (t
2160 (widget-create-child-value widget arg (car (car answer)))))
2161 children))
2162 (widget-put widget :children (nreverse children))))
2163
2164(defun widget-group-match (widget values)
2165 ;; Match if the components match.
2166 (and (listp values)
2167 (let ((match (widget-group-match-inline widget values)))
2168 (and match (null (cdr match))))))
2169
2170(defun widget-group-match-inline (widget vals)
2171 ;; Match if the components match.
2172 (let ((args (widget-get widget :args))
2173 argument answer found)
2174 (while args
2175 (setq argument (car args)
2176 args (cdr args)
2177 answer (widget-match-inline argument vals))
2178 (if answer
2179 (setq vals (cdr answer)
2180 found (append found (car answer)))
2181 (setq vals nil
2182 args nil)))
2183 (if answer
2184 (cons found vals)
2185 nil)))
2186
2187;;; The `widget-help' Widget.
2188
2189(define-widget 'widget-help 'push-button
2190 "The widget documentation button."
2191 :format "%[[%t]%] %d"
2192 :help-echo "Toggle display of documentation."
2193 :action 'widget-help-action)
2194
2195(defun widget-help-action (widget &optional event)
2196 "Toggle documentation for WIDGET."
2197 (let ((old (widget-get widget :doc))
2198 (new (widget-get widget :widget-doc)))
2199 (widget-put widget :doc new)
2200 (widget-put widget :widget-doc old))
2201 (widget-value-set widget (widget-value widget)))
2202
2203;;; The Sexp Widgets.
2204
2205(define-widget 'const 'item
2206 "An immutable sexp."
2207 :format "%t\n%d")
2208
2209(define-widget 'function-item 'item
2210 "An immutable function name."
2211 :format "%v\n%h"
2212 :documentation-property (lambda (symbol)
2213 (condition-case nil
2214 (documentation symbol t)
2215 (error nil))))
2216
2217(define-widget 'variable-item 'item
2218 "An immutable variable name."
2219 :format "%v\n%h"
2220 :documentation-property 'variable-documentation)
2221
2222(define-widget 'string 'editable-field
2223 "A string"
2224 :tag "String"
2225 :format "%[%t%]: %v")
2226
2227(define-widget 'regexp 'string
2228 "A regular expression."
2229 ;; Should do validation.
2230 :tag "Regexp")
2231
2232(define-widget 'file 'string
2233 "A file widget.
2234It will read a file name from the minibuffer when activated."
2235 :format "%[%t%]: %v"
2236 :tag "File"
2237 :action 'widget-file-action)
2238
2239(defun widget-file-action (widget &optional event)
2240 ;; Read a file name from the minibuffer.
2241 (let* ((value (widget-value widget))
2242 (dir (file-name-directory value))
2243 (file (file-name-nondirectory value))
2244 (menu-tag (widget-apply widget :menu-tag-get))
2245 (must-match (widget-get widget :must-match))
2246 (answer (read-file-name (concat menu-tag ": (default `" value "') ")
2247 dir nil must-match file)))
2248 (widget-value-set widget (abbreviate-file-name answer))
2249 (widget-apply widget :notify widget event)
2250 (widget-setup)))
2251
2252(define-widget 'directory 'file
2253 "A directory widget.
2254It will read a directory name from the minibuffer when activated."
2255 :tag "Directory")
2256
2257(define-widget 'symbol 'string
2258 "A lisp symbol."
2259 :value nil
2260 :tag "Symbol"
2261 :match (lambda (widget value) (symbolp value))
2262 :value-to-internal (lambda (widget value)
2263 (if (symbolp value)
2264 (symbol-name value)
2265 value))
2266 :value-to-external (lambda (widget value)
2267 (if (stringp value)
2268 (intern value)
2269 value)))
2270
2271(define-widget 'function 'sexp
2272 ;; Should complete on functions.
2273 "A lisp function."
2274 :tag "Function")
2275
2276(define-widget 'variable 'symbol
2277 ;; Should complete on variables.
2278 "A lisp variable."
2279 :tag "Variable")
2280
2281(define-widget 'sexp 'string
2282 "An arbitrary lisp expression."
2283 :tag "Lisp expression"
2284 :value nil
2285 :validate 'widget-sexp-validate
2286 :match (lambda (widget value) t)
2287 :value-to-internal 'widget-sexp-value-to-internal
2288 :value-to-external (lambda (widget value) (read value)))
2289
2290(defun widget-sexp-value-to-internal (widget value)
2291 ;; Use pp for printer representation.
2292 (let ((pp (pp-to-string value)))
2293 (while (string-match "\n\\'" pp)
2294 (setq pp (substring pp 0 -1)))
2295 (if (or (string-match "\n\\'" pp)
2296 (> (length pp) 40))
2297 (concat "\n" pp)
2298 pp)))
2299
2300(defun widget-sexp-validate (widget)
2301 ;; Valid if we can read the string and there is no junk left after it.
2302 (save-excursion
2303 (let ((buffer (set-buffer (get-buffer-create " *Widget Scratch*"))))
2304 (erase-buffer)
2305 (insert (widget-apply widget :value-get))
2306 (goto-char (point-min))
2307 (condition-case data
2308 (let ((value (read buffer)))
2309 (if (eobp)
2310 (if (widget-apply widget :match value)
2311 nil
2312 (widget-put widget :error (widget-get widget :type-error))
2313 widget)
2314 (widget-put widget
2315 :error (format "Junk at end of expression: %s"
2316 (buffer-substring (point)
2317 (point-max))))
2318 widget))
2319 (error (widget-put widget :error (error-message-string data))
2320 widget)))))
2321
2322(define-widget 'integer 'sexp
2323 "An integer."
2324 :tag "Integer"
2325 :value 0
2326 :type-error "This field should contain an integer"
2327 :value-to-internal (lambda (widget value)
2328 (if (integerp value)
2329 (prin1-to-string value)
2330 value))
2331 :match (lambda (widget value) (integerp value)))
2332
2333(define-widget 'character 'string
2334 "An character."
2335 :tag "Character"
2336 :value 0
2337 :size 1
2338 :format "%{%t%}: %v\n"
2339 :type-error "This field should contain a character"
2340 :value-to-internal (lambda (widget value)
2341 (if (integerp value)
2342 (char-to-string value)
2343 value))
2344 :value-to-external (lambda (widget value)
2345 (if (stringp value)
2346 (aref value 0)
2347 value))
2348 :match (lambda (widget value) (integerp value)))
2349
2350(define-widget 'number 'sexp
2351 "A floating point number."
2352 :tag "Number"
2353 :value 0.0
2354 :type-error "This field should contain a number"
2355 :value-to-internal (lambda (widget value)
2356 (if (numberp value)
2357 (prin1-to-string value)
2358 value))
2359 :match (lambda (widget value) (numberp value)))
2360
2361(define-widget 'list 'group
2362 "A lisp list."
2363 :tag "List"
2364 :format "%{%t%}:\n%v")
2365
2366(define-widget 'vector 'group
2367 "A lisp vector."
2368 :tag "Vector"
2369 :format "%{%t%}:\n%v"
2370 :match 'widget-vector-match
2371 :value-to-internal (lambda (widget value) (append value nil))
2372 :value-to-external (lambda (widget value) (apply 'vector value)))
2373
2374(defun widget-vector-match (widget value)
2375 (and (vectorp value)
2376 (widget-group-match widget
2377 (widget-apply :value-to-internal widget value))))
2378
2379(define-widget 'cons 'group
2380 "A cons-cell."
2381 :tag "Cons-cell"
2382 :format "%{%t%}:\n%v"
2383 :match 'widget-cons-match
2384 :value-to-internal (lambda (widget value)
2385 (list (car value) (cdr value)))
2386 :value-to-external (lambda (widget value)
2387 (cons (nth 0 value) (nth 1 value))))
2388
2389(defun widget-cons-match (widget value)
2390 (and (consp value)
2391 (widget-group-match widget
2392 (widget-apply widget :value-to-internal value))))
2393
2394(define-widget 'choice 'menu-choice
2395 "A union of several sexp types."
2396 :tag "Choice"
2397 :format "%[%t%]: %v")
2398
2399(define-widget 'radio 'radio-button-choice
2400 "A union of several sexp types."
2401 :tag "Choice"
2402 :format "%{%t%}:\n%v")
2403
2404(define-widget 'repeat 'editable-list
2405 "A variable length homogeneous list."
2406 :tag "Repeat"
2407 :format "%{%t%}:\n%v%i\n")
2408
2409(define-widget 'set 'checklist
2410 "A list of members from a fixed set."
2411 :tag "Set"
2412 :format "%{%t%}:\n%v")
2413
2414(define-widget 'boolean 'toggle
2415 "To be nil or non-nil, that is the question."
2416 :tag "Boolean"
2417 :format "%{%t%}: %[%v%]\n")
2418
2419;;; The `color' Widget.
2420
2421(define-widget 'color-item 'choice-item
2422 "A color name (with sample)."
2423 :format "%v (%{sample%})\n"
2424 :sample-face-get 'widget-color-item-button-face-get)
2425
2426(defun widget-color-item-button-face-get (widget)
2427 ;; We create a face from the value.
2428 (require 'facemenu)
2429 (condition-case nil
2430 (facemenu-get-face (intern (concat "fg:" (widget-value widget))))
2431 (error 'default)))
2432
2433(define-widget 'color 'push-button
2434 "Choose a color name (with sample)."
2435 :format "%[%t%]: %v"
2436 :tag "Color"
2437 :value "black"
2438 :value-create 'widget-color-value-create
2439 :value-delete 'widget-children-value-delete
2440 :value-get 'widget-color-value-get
2441 :value-set 'widget-color-value-set
2442 :action 'widget-color-action
2443 :match 'widget-field-match
2444 :tag "Color")
2445
2446(defvar widget-color-choice-list nil)
2447;; Variable holding the possible colors.
2448
2449(defun widget-color-choice-list ()
2450 (unless widget-color-choice-list
2451 (setq widget-color-choice-list
2452 (mapcar '(lambda (color) (list color))
2453 (x-defined-colors))))
2454 widget-color-choice-list)
2455
2456(defun widget-color-value-create (widget)
2457 (let ((child (widget-create-child-and-convert
2458 widget 'color-item (widget-get widget :value))))
2459 (widget-put widget :children (list child))))
2460
2461(defun widget-color-value-get (widget)
2462 ;; Pass command to first child.
2463 (widget-apply (car (widget-get widget :children)) :value-get))
2464
2465(defun widget-color-value-set (widget value)
2466 ;; Pass command to first child.
2467 (widget-apply (car (widget-get widget :children)) :value-set value))
2468
2469(defvar widget-color-history nil
2470 "History of entered colors")
2471
2472(defun widget-color-action (widget &optional event)
2473 ;; Prompt for a color.
2474 (let* ((tag (widget-apply widget :menu-tag-get))
2475 (prompt (concat tag ": "))
2476 (answer (cond ((string-match "XEmacs" emacs-version)
2477 (read-color prompt))
2478 ((fboundp 'x-defined-colors)
2479 (completing-read (concat tag ": ")
2480 (widget-color-choice-list)
2481 nil nil nil 'widget-color-history))
2482 (t
2483 (read-string prompt (widget-value widget))))))
2484 (unless (zerop (length answer))
2485 (widget-value-set widget answer)
2486 (widget-apply widget :notify widget event)
2487 (widget-setup))))
2488
2489;;; The Help Echo
2490
2491(defun widget-echo-help-mouse ()
2492 "Display the help message for the widget under the mouse.
2493Enable with (run-with-idle-timer 1 t 'widget-echo-help-mouse)"
2494 (let* ((pos (mouse-position))
2495 (frame (car pos))
2496 (x (car (cdr pos)))
2497 (y (cdr (cdr pos)))
2498 (win (window-at x y frame))
2499 (where (coordinates-in-window-p (cons x y) win)))
2500 (when (consp where)
2501 (save-window-excursion
2502 (progn ; save-excursion
2503 (select-window win)
2504 (let* ((result (compute-motion (window-start win)
2505 '(0 . 0)
2506 (window-end win)
2507 where
2508 (window-width win)
2509 (cons (window-hscroll) 0)
2510 win)))
2511 (when (and (eq (nth 1 result) x)
2512 (eq (nth 2 result) y))
2513 (widget-echo-help (nth 0 result))))))))
2514 (unless track-mouse
2515 (setq track-mouse t)
2516 (add-hook 'post-command-hook 'widget-stop-mouse-tracking)))
2517
2518(defun widget-stop-mouse-tracking (&rest args)
2519 "Stop the mouse tracking done while idle."
2520 (remove-hook 'post-command-hook 'widget-stop-mouse-tracking)
2521 (setq track-mouse nil))
2522
2523(defun widget-at (pos)
2524 "The button or field at POS."
2525 (or (get-text-property pos 'button)
2526 (get-text-property pos 'field)))
2527
2528(defun widget-echo-help (pos)
2529 "Display the help echo for widget at POS."
2530 (let* ((widget (widget-at pos))
2531 (help-echo (and widget (widget-get widget :help-echo))))
2532 (cond ((stringp help-echo)
2533 (message "%s" help-echo))
2534 ((and (symbolp help-echo) (fboundp help-echo)
2535 (stringp (setq help-echo (funcall help-echo widget))))
2536 (message "%s" help-echo)))))
2537
2538;;; The End:
2539
2540(provide 'wid-edit)
2541
2542;; wid-edit.el ends here
diff --git a/lisp/widget.el b/lisp/widget.el
new file mode 100644
index 00000000000..4e1f2ca804c
--- /dev/null
+++ b/lisp/widget.el
@@ -0,0 +1,76 @@
1;;; widget.el --- a library of user interface components.
2;;
3;; Copyright (C) 1996, 1997 Free Software Foundation, Inc.
4;;
5;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6;; Keywords: help, extensions, faces, hypermedia
7;; Version: 1.71
8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
9
10;;; Commentary:
11;;
12;; If you want to use this code, please visit the URL above.
13;;
14;; This file only contain the code needed to define new widget types.
15;; Everything else is autoloaded from `wid-edit.el'.
16
17;;; Code:
18
19(eval-when-compile (require 'cl))
20
21(defmacro define-widget-keywords (&rest keys)
22 (`
23 (eval-and-compile
24 (let ((keywords (quote (, keys))))
25 (while keywords
26 (or (boundp (car keywords))
27 (set (car keywords) (car keywords)))
28 (setq keywords (cdr keywords)))))))
29
30(define-widget-keywords :deactivate :active :inactive :activate
31 :sibling-args :delete-button-args
32 :insert-button-args :append-button-args :button-args
33 :tag-glyph :off-glyph :on-glyph :valid-regexp
34 :secret :sample-face :sample-face-get :case-fold :widget-doc
35 :create :convert-widget :format :value-create :offset :extra-offset
36 :tag :doc :from :to :args :value :value-from :value-to :action
37 :value-set :value-delete :match :parent :delete :menu-tag-get
38 :value-get :choice :void :menu-tag :on :off :on-type :off-type
39 :notify :entry-format :button :children :buttons :insert-before
40 :delete-at :format-handler :widget :value-pos :value-to-internal
41 :indent :size :value-to-external :validate :error :directory
42 :must-match :type-error :value-inline :inline :match-inline :greedy
43 :button-face-get :button-face :value-face :keymap :entry-from
44 :entry-to :help-echo :documentation-property :hide-front-space
45 :hide-rear-space :tab-order)
46
47;; These autoloads should be deleted when the file is added to Emacs.
48(unless (fboundp 'load-gc)
49 (autoload 'widget-apply "wid-edit")
50 (autoload 'widget-create "wid-edit")
51 (autoload 'widget-insert "wid-edit")
52 (autoload 'widget-browse "wid-browse" nil t)
53 (autoload 'widget-browse-at "wid-browse" nil t))
54
55(defun define-widget (name class doc &rest args)
56 "Define a new widget type named NAME from CLASS.
57
58NAME and CLASS should both be symbols, CLASS should be one of the
59existing widget types, or nil to create the widget from scratch.
60
61After the new widget has been defined, the following two calls will
62create identical widgets:
63
64* (widget-create NAME)
65
66* (apply 'widget-create CLASS ARGS)
67
68The third argument DOC is a documentation string for the widget."
69 (put name 'widget-type (cons class args))
70 (put name 'widget-documentation doc))
71
72;;; The End.
73
74(provide 'widget)
75
76;; widget.el ends here