]> code.delx.au - gnu-emacs/blob - lisp/cus-edit.el
Synched with 1.9905
[gnu-emacs] / lisp / cus-edit.el
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.9905
8 ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26
27 ;;; Commentary:
28 ;;
29 ;; This file implements the code to create and edit customize buffers.
30 ;;
31 ;; See `custom.el'.
32
33 ;;; Code:
34
35 (require 'cus-face)
36 (require 'wid-edit)
37 (require 'easymenu)
38 (eval-when-compile (require 'cl))
39
40 (condition-case nil
41 (require 'cus-load)
42 (error nil))
43
44 (condition-case nil
45 (require 'cus-start)
46 (error nil))
47
48 (define-widget-keywords :custom-category :custom-prefixes :custom-menu
49 :custom-show
50 :custom-magic :custom-state :custom-level :custom-form
51 :custom-set :custom-save :custom-reset-current :custom-reset-saved
52 :custom-reset-standard)
53
54 (put 'custom-define-hook 'custom-type 'hook)
55 (put 'custom-define-hook 'standard-value '(nil))
56 (custom-add-to-group 'customize 'custom-define-hook 'custom-variable)
57
58 ;;; Customization Groups.
59
60 (defgroup emacs nil
61 "Customization of the One True Editor."
62 :link '(custom-manual "(emacs)Top"))
63
64 ;; Most of these groups are stolen from `finder.el',
65 (defgroup editing nil
66 "Basic text editing facilities."
67 :group 'emacs)
68
69 (defgroup abbrev nil
70 "Abbreviation handling, typing shortcuts, macros."
71 :tag "Abbreviations"
72 :group 'editing)
73
74 (defgroup matching nil
75 "Various sorts of searching and matching."
76 :group 'editing)
77
78 (defgroup emulations nil
79 "Emulations of other editors."
80 :group 'editing)
81
82 (defgroup mouse nil
83 "Mouse support."
84 :group 'editing)
85
86 (defgroup outlines nil
87 "Support for hierarchical outlining."
88 :group 'editing)
89
90 (defgroup external nil
91 "Interfacing to external utilities."
92 :group 'emacs)
93
94 (defgroup bib nil
95 "Code related to the `bib' bibliography processor."
96 :tag "Bibliography"
97 :group 'external)
98
99 (defgroup processes nil
100 "Process, subshell, compilation, and job control support."
101 :group 'external
102 :group 'development)
103
104 (defgroup programming nil
105 "Support for programming in other languages."
106 :group 'emacs)
107
108 (defgroup languages nil
109 "Specialized modes for editing programming languages."
110 :group 'programming)
111
112 (defgroup lisp nil
113 "Lisp support, including Emacs Lisp."
114 :group 'languages
115 :group 'development)
116
117 (defgroup c nil
118 "Support for the C language and related languages."
119 :group 'languages)
120
121 (defgroup tools nil
122 "Programming tools."
123 :group 'programming)
124
125 (defgroup oop nil
126 "Support for object-oriented programming."
127 :group 'programming)
128
129 (defgroup applications nil
130 "Applications written in Emacs."
131 :group 'emacs)
132
133 (defgroup calendar nil
134 "Calendar and time management support."
135 :group 'applications)
136
137 (defgroup mail nil
138 "Modes for electronic-mail handling."
139 :group 'applications)
140
141 (defgroup news nil
142 "Support for netnews reading and posting."
143 :group 'applications)
144
145 (defgroup games nil
146 "Games, jokes and amusements."
147 :group 'applications)
148
149 (defgroup development nil
150 "Support for further development of Emacs."
151 :group 'emacs)
152
153 (defgroup docs nil
154 "Support for Emacs documentation."
155 :group 'development)
156
157 (defgroup extensions nil
158 "Emacs Lisp language extensions."
159 :group 'development)
160
161 (defgroup internal nil
162 "Code for Emacs internals, build process, defaults."
163 :group 'development)
164
165 (defgroup maint nil
166 "Maintenance aids for the Emacs development group."
167 :tag "Maintenance"
168 :group 'development)
169
170 (defgroup environment nil
171 "Fitting Emacs with its environment."
172 :group 'emacs)
173
174 (defgroup comm nil
175 "Communications, networking, remote access to files."
176 :tag "Communication"
177 :group 'environment)
178
179 (defgroup hardware nil
180 "Support for interfacing with exotic hardware."
181 :group 'environment)
182
183 (defgroup terminals nil
184 "Support for terminal types."
185 :group 'environment)
186
187 (defgroup unix nil
188 "Front-ends/assistants for, or emulators of, UNIX features."
189 :group 'environment)
190
191 (defgroup vms nil
192 "Support code for vms."
193 :group 'environment)
194
195 (defgroup i18n nil
196 "Internationalization and alternate character-set support."
197 :group 'environment
198 :group 'editing)
199
200 (defgroup x nil
201 "The X Window system."
202 :group 'environment)
203
204 (defgroup frames nil
205 "Support for Emacs frames and window systems."
206 :group 'environment)
207
208 (defgroup data nil
209 "Support editing files of data."
210 :group 'emacs)
211
212 (defgroup wp nil
213 "Word processing."
214 :group 'emacs)
215
216 (defgroup tex nil
217 "Code related to the TeX formatter."
218 :group 'wp)
219
220 (defgroup faces nil
221 "Support for multiple fonts."
222 :group 'emacs)
223
224 (defgroup hypermedia nil
225 "Support for links between text or other media types."
226 :group 'emacs)
227
228 (defgroup help nil
229 "Support for on-line help systems."
230 :group 'emacs)
231
232 (defgroup local nil
233 "Code local to your site."
234 :group 'emacs)
235
236 (defgroup customize '((widgets custom-group))
237 "Customization of the Customization support."
238 :link '(custom-manual "(custom)Top")
239 :link '(url-link :tag "Development Page"
240 "http://www.dina.kvl.dk/~abraham/custom/")
241 :prefix "custom-"
242 :group 'help)
243
244 (defgroup custom-faces nil
245 "Faces used by customize."
246 :group 'customize
247 :group 'faces)
248
249 (defgroup abbrev-mode nil
250 "Word abbreviations mode."
251 :group 'abbrev)
252
253 (defgroup alloc nil
254 "Storage allocation and gc for GNU Emacs Lisp interpreter."
255 :tag "Storage Allocation"
256 :group 'internal)
257
258 (defgroup undo nil
259 "Undoing changes in buffers."
260 :group 'editing)
261
262 (defgroup modeline nil
263 "Content of the modeline."
264 :group 'environment)
265
266 (defgroup fill nil
267 "Indenting and filling text."
268 :group 'editing)
269
270 (defgroup editing-basics nil
271 "Most basic editing facilities."
272 :group 'editing)
273
274 (defgroup display nil
275 "How characters are displayed in buffers."
276 :group 'environment)
277
278 (defgroup execute nil
279 "Executing external commands."
280 :group 'processes)
281
282 (defgroup installation nil
283 "The Emacs installation."
284 :group 'environment)
285
286 (defgroup dired nil
287 "Directory editing."
288 :group 'environment)
289
290 (defgroup limits nil
291 "Internal Emacs limits."
292 :group 'internal)
293
294 (defgroup debug nil
295 "Debugging Emacs itself."
296 :group 'development)
297
298 (defgroup minibuffer nil
299 "Controling the behaviour of the minibuffer."
300 :group 'environment)
301
302 (defgroup keyboard nil
303 "Input from the keyboard."
304 :group 'environment)
305
306 (defgroup mouse nil
307 "Input from the mouse."
308 :group 'environment)
309
310 (defgroup menu nil
311 "Input from the menus."
312 :group 'environment)
313
314 (defgroup auto-save nil
315 "Preventing accidential loss of data."
316 :group 'data)
317
318 (defgroup processes-basics nil
319 "Basic stuff dealing with processes."
320 :group 'processes)
321
322 (defgroup mule nil
323 "MULE Emacs internationalization."
324 :group 'i18n)
325
326 (defgroup windows nil
327 "Windows within a frame."
328 :group 'environment)
329
330 ;;; Utilities.
331
332 (defun custom-quote (sexp)
333 "Quote SEXP iff it is not self quoting."
334 (if (or (memq sexp '(t nil))
335 (and (symbolp sexp)
336 (eq (aref (symbol-name sexp) 0) ?:))
337 (and (listp sexp)
338 (memq (car sexp) '(lambda)))
339 (stringp sexp)
340 (numberp sexp)
341 (and (fboundp 'characterp)
342 (characterp sexp)))
343 sexp
344 (list 'quote sexp)))
345
346 (defun custom-split-regexp-maybe (regexp)
347 "If REGEXP is a string, split it to a list at `\\|'.
348 You can get the original back with from the result with:
349 (mapconcat 'identity result \"\\|\")
350
351 IF REGEXP is not a string, return it unchanged."
352 (if (stringp regexp)
353 (let ((start 0)
354 all)
355 (while (string-match "\\\\|" regexp start)
356 (setq all (cons (substring regexp start (match-beginning 0)) all)
357 start (match-end 0)))
358 (nreverse (cons (substring regexp start) all)))
359 regexp))
360
361 (defun custom-variable-prompt ()
362 ;; Code stolen from `help.el'.
363 "Prompt for a variable, defaulting to the variable at point.
364 Return a list suitable for use in `interactive'."
365 (let ((v (variable-at-point))
366 (enable-recursive-minibuffers t)
367 val)
368 (setq val (completing-read
369 (if (symbolp v)
370 (format "Customize variable: (default %s) " v)
371 "Customize variable: ")
372 obarray (lambda (symbol)
373 (and (boundp symbol)
374 (or (get symbol 'custom-type)
375 (user-variable-p symbol))))))
376 (list (if (equal val "")
377 (if (symbolp v) v nil)
378 (intern val)))))
379
380 (defun custom-menu-filter (menu widget)
381 "Convert MENU to the form used by `widget-choose'.
382 MENU should be in the same format as `custom-variable-menu'.
383 WIDGET is the widget to apply the filter entries of MENU on."
384 (let ((result nil)
385 current name action filter)
386 (while menu
387 (setq current (car menu)
388 name (nth 0 current)
389 action (nth 1 current)
390 filter (nth 2 current)
391 menu (cdr menu))
392 (if (or (null filter) (funcall filter widget))
393 (push (cons name action) result)
394 (push name result)))
395 (nreverse result)))
396
397 ;;; Unlispify.
398
399 (defvar custom-prefix-list nil
400 "List of prefixes that should be ignored by `custom-unlispify'")
401
402 (defcustom custom-unlispify-menu-entries t
403 "Display menu entries as words instead of symbols if non nil."
404 :group 'customize
405 :type 'boolean)
406
407 (defun custom-unlispify-menu-entry (symbol &optional no-suffix)
408 "Convert symbol into a menu entry."
409 (cond ((not custom-unlispify-menu-entries)
410 (symbol-name symbol))
411 ((get symbol 'custom-tag)
412 (if no-suffix
413 (get symbol 'custom-tag)
414 (concat (get symbol 'custom-tag) "...")))
415 (t
416 (save-excursion
417 (set-buffer (get-buffer-create " *Custom-Work*"))
418 (erase-buffer)
419 (princ symbol (current-buffer))
420 (goto-char (point-min))
421 (when (and (eq (get symbol 'custom-type) 'boolean)
422 (re-search-forward "-p\\'" nil t))
423 (replace-match "" t t)
424 (goto-char (point-min)))
425 (let ((prefixes custom-prefix-list)
426 prefix)
427 (while prefixes
428 (setq prefix (car prefixes))
429 (if (search-forward prefix (+ (point) (length prefix)) t)
430 (progn
431 (setq prefixes nil)
432 (delete-region (point-min) (point)))
433 (setq prefixes (cdr prefixes)))))
434 (subst-char-in-region (point-min) (point-max) ?- ?\ t)
435 (capitalize-region (point-min) (point-max))
436 (unless no-suffix
437 (goto-char (point-max))
438 (insert "..."))
439 (buffer-string)))))
440
441 (defcustom custom-unlispify-tag-names t
442 "Display tag names as words instead of symbols if non nil."
443 :group 'customize
444 :type 'boolean)
445
446 (defun custom-unlispify-tag-name (symbol)
447 "Convert symbol into a menu entry."
448 (let ((custom-unlispify-menu-entries custom-unlispify-tag-names))
449 (custom-unlispify-menu-entry symbol t)))
450
451 (defun custom-prefix-add (symbol prefixes)
452 ;; Addd SYMBOL to list of ignored PREFIXES.
453 (cons (or (get symbol 'custom-prefix)
454 (concat (symbol-name symbol) "-"))
455 prefixes))
456
457 ;;; Guess.
458
459 (defcustom custom-guess-name-alist
460 '(("-p\\'" boolean)
461 ("-hook\\'" hook)
462 ("-face\\'" face)
463 ("-file\\'" file)
464 ("-function\\'" function)
465 ("-functions\\'" (repeat function))
466 ("-list\\'" (repeat sexp))
467 ("-alist\\'" (repeat (cons sexp sexp))))
468 "Alist of (MATCH TYPE).
469
470 MATCH should be a regexp matching the name of a symbol, and TYPE should
471 be a widget suitable for editing the value of that symbol. The TYPE
472 of the first entry where MATCH matches the name of the symbol will be
473 used.
474
475 This is used for guessing the type of variables not declared with
476 customize."
477 :type '(repeat (group (regexp :tag "Match") (sexp :tag "Type")))
478 :group 'customize)
479
480 (defcustom custom-guess-doc-alist
481 '(("\\`\\*?Non-nil " boolean))
482 "Alist of (MATCH TYPE).
483
484 MATCH should be a regexp matching a documentation string, and TYPE
485 should be a widget suitable for editing the value of a variable with
486 that documentation string. The TYPE of the first entry where MATCH
487 matches the name of the symbol will be used.
488
489 This is used for guessing the type of variables not declared with
490 customize."
491 :type '(repeat (group (regexp :tag "Match") (sexp :tag "Type")))
492 :group 'customize)
493
494 (defun custom-guess-type (symbol)
495 "Guess a widget suitable for editing the value of SYMBOL.
496 This is done by matching SYMBOL with `custom-guess-name-alist' and
497 if that fails, the doc string with `custom-guess-doc-alist'."
498 (let ((name (symbol-name symbol))
499 (names custom-guess-name-alist)
500 current found)
501 (while names
502 (setq current (car names)
503 names (cdr names))
504 (when (string-match (nth 0 current) name)
505 (setq found (nth 1 current)
506 names nil)))
507 (unless found
508 (let ((doc (documentation-property symbol 'variable-documentation))
509 (docs custom-guess-doc-alist))
510 (when doc
511 (while docs
512 (setq current (car docs)
513 docs (cdr docs))
514 (when (string-match (nth 0 current) doc)
515 (setq found (nth 1 current)
516 docs nil))))))
517 found))
518
519 ;;; Sorting.
520
521 (defcustom custom-buffer-sort-predicate 'custom-buffer-sort-alphabetically
522 "Function used for sorting group members in buffers.
523 The value should be useful as a predicate for `sort'.
524 The list to be sorted is the value of the groups `custom-group' property."
525 :type '(radio (function-item custom-buffer-sort-alphabetically)
526 (function :tag "Other"))
527 :group 'customize)
528
529 (defun custom-buffer-sort-alphabetically (a b)
530 "Return t iff is A should be before B.
531 A and B should be members of a `custom-group' property.
532 The members are sorted alphabetically, except that all groups are
533 sorted after all non-groups."
534 (cond ((and (eq (nth 1 a) 'custom-group)
535 (not (eq (nth 1 b) 'custom-group)))
536 nil)
537 ((and (eq (nth 1 b) 'custom-group)
538 (not (eq (nth 1 a) 'custom-group)))
539 t)
540 (t
541 (string-lessp (symbol-name (nth 0 a)) (symbol-name (nth 0 b))))))
542
543 (defcustom custom-menu-sort-predicate 'custom-menu-sort-alphabetically
544 "Function used for sorting group members in menus.
545 The value should be useful as a predicate for `sort'.
546 The list to be sorted is the value of the groups `custom-group' property."
547 :type '(radio (function-item custom-menu-sort-alphabetically)
548 (function :tag "Other"))
549 :group 'customize)
550
551 (defun custom-menu-sort-alphabetically (a b)
552 "Return t iff is A should be before B.
553 A and B should be members of a `custom-group' property.
554 The members are sorted alphabetically, except that all groups are
555 sorted before all non-groups."
556 (cond ((and (eq (nth 1 a) 'custom-group)
557 (not (eq (nth 1 b) 'custom-group)))
558 t)
559 ((and (eq (nth 1 b) 'custom-group)
560 (not (eq (nth 1 a) 'custom-group)))
561 nil)
562 (t
563 (string-lessp (symbol-name (nth 0 a)) (symbol-name (nth 0 b))))))
564
565 ;;; Custom Mode Commands.
566
567 (defvar custom-options nil
568 "Customization widgets in the current buffer.")
569
570 (defun custom-set ()
571 "Set changes in all modified options."
572 (interactive)
573 (let ((children custom-options))
574 (mapcar (lambda (child)
575 (when (eq (widget-get child :custom-state) 'modified)
576 (widget-apply child :custom-set)))
577 children)))
578
579 (defun custom-save ()
580 "Set all modified group members and save them."
581 (interactive)
582 (let ((children custom-options))
583 (mapcar (lambda (child)
584 (when (memq (widget-get child :custom-state) '(modified set))
585 (widget-apply child :custom-save)))
586 children))
587 (custom-save-all))
588
589 (defvar custom-reset-menu
590 '(("Current" . custom-reset-current)
591 ("Saved" . custom-reset-saved)
592 ("Standard Settings" . custom-reset-standard))
593 "Alist of actions for the `Reset' button.
594 The key is a string containing the name of the action, the value is a
595 lisp function taking the widget as an element which will be called
596 when the action is chosen.")
597
598 (defun custom-reset (event)
599 "Select item from reset menu."
600 (let* ((completion-ignore-case t)
601 (answer (widget-choose "Reset to"
602 custom-reset-menu
603 event)))
604 (if answer
605 (funcall answer))))
606
607 (defun custom-reset-current (&rest ignore)
608 "Reset all modified group members to their current value."
609 (interactive)
610 (let ((children custom-options))
611 (mapcar (lambda (child)
612 (when (eq (widget-get child :custom-state) 'modified)
613 (widget-apply child :custom-reset-current)))
614 children)))
615
616 (defun custom-reset-saved (&rest ignore)
617 "Reset all modified or set group members to their saved value."
618 (interactive)
619 (let ((children custom-options))
620 (mapcar (lambda (child)
621 (when (eq (widget-get child :custom-state) 'modified)
622 (widget-apply child :custom-reset-current)))
623 children)))
624
625 (defun custom-reset-standard (&rest ignore)
626 "Reset all modified, set, or saved group members to their standard settings."
627 (interactive)
628 (let ((children custom-options))
629 (mapcar (lambda (child)
630 (when (eq (widget-get child :custom-state) 'modified)
631 (widget-apply child :custom-reset-current)))
632 children)))
633
634 ;;; The Customize Commands
635
636 (defun custom-prompt-variable (prompt-var prompt-val)
637 "Prompt for a variable and a value and return them as a list.
638 PROMPT-VAR is the prompt for the variable, and PROMPT-VAL is the
639 prompt for the value. The %s escape in PROMPT-VAL is replaced with
640 the name of the variable.
641
642 If the variable has a `variable-interactive' property, that is used as if
643 it were the arg to `interactive' (which see) to interactively read the value.
644
645 If the variable has a `custom-type' property, it must be a widget and the
646 `:prompt-value' property of that widget will be used for reading the value."
647 (let* ((var (read-variable prompt-var))
648 (minibuffer-help-form '(describe-variable var)))
649 (list var
650 (let ((prop (get var 'variable-interactive))
651 (type (get var 'custom-type))
652 (prompt (format prompt-val var)))
653 (unless (listp type)
654 (setq type (list type)))
655 (cond (prop
656 ;; Use VAR's `variable-interactive' property
657 ;; as an interactive spec for prompting.
658 (call-interactively (list 'lambda '(arg)
659 (list 'interactive prop)
660 'arg)))
661 (type
662 (widget-prompt-value type
663 prompt
664 (if (boundp var)
665 (symbol-value var))
666 (not (boundp var))))
667 (t
668 (eval-minibuffer prompt)))))))
669
670 ;;;###autoload
671 (defun custom-set-value (var val)
672 "Set VARIABLE to VALUE. VALUE is a Lisp object.
673
674 If VARIABLE has a `variable-interactive' property, that is used as if
675 it were the arg to `interactive' (which see) to interactively read the value.
676
677 If VARIABLE has a `custom-type' property, it must be a widget and the
678 `:prompt-value' property of that widget will be used for reading the value."
679 (interactive (custom-prompt-variable "Set variable: "
680 "Set %s to value: "))
681
682 (set var val))
683
684 ;;;###autoload
685 (defun custom-set-variable (var val)
686 "Set the default for VARIABLE to VALUE. VALUE is a Lisp object.
687
688 If VARIABLE has a `custom-set' property, that is used for setting
689 VARIABLE, otherwise `set-default' is used.
690
691 The `customized-value' property of the VARIABLE will be set to a list
692 with a quoted VALUE as its sole list member.
693
694 If VARIABLE has a `variable-interactive' property, that is used as if
695 it were the arg to `interactive' (which see) to interactively read the value.
696
697 If VARIABLE has a `custom-type' property, it must be a widget and the
698 `:prompt-value' property of that widget will be used for reading the value. "
699 (interactive (custom-prompt-variable "Set variable: "
700 "Set customized value for %s to: "))
701 (funcall (or (get var 'custom-set) 'set-default) var val)
702 (put var 'customized-value (list (custom-quote val))))
703
704 ;;;###autoload
705 (defun customize ()
706 "Select a customization buffer which you can use to set user options.
707 User options are structured into \"groups\".
708 Initially the top-level group `Emacs' and its immediate subgroups
709 are shown; the contents of those subgroups are initially hidden."
710 (interactive)
711 (customize-group 'emacs))
712
713 ;;;###autoload
714 (defun customize-group (group)
715 "Customize GROUP, which must be a customization group."
716 (interactive (list (completing-read "Customize group: (default emacs) "
717 obarray
718 (lambda (symbol)
719 (get symbol 'custom-group))
720 t)))
721
722 (when (stringp group)
723 (if (string-equal "" group)
724 (setq group 'emacs)
725 (setq group (intern group))))
726 (custom-buffer-create (list (list group 'custom-group))
727 (format "*Customize Group: %s*"
728 (custom-unlispify-tag-name group))))
729
730 ;;;###autoload
731 (defun customize-group-other-window (symbol)
732 "Customize SYMBOL, which must be a customization group."
733 (interactive (list (completing-read "Customize group: (default emacs) "
734 obarray
735 (lambda (symbol)
736 (get symbol 'custom-group))
737 t)))
738
739 (when (stringp symbol)
740 (if (string-equal "" symbol)
741 (setq symbol 'emacs)
742 (setq symbol (intern symbol))))
743 (custom-buffer-create-other-window
744 (list (list symbol 'custom-group))
745 (format "*Customize Group: %s*" (custom-unlispify-tag-name symbol))))
746
747 ;;;###autoload
748 (defalias 'customize-variable 'customize-option)
749
750 ;;;###autoload
751 (defun customize-option (symbol)
752 "Customize SYMBOL, which must be a user option variable."
753 (interactive (custom-variable-prompt))
754 (custom-buffer-create (list (list symbol 'custom-variable))
755 (format "*Customize Option: %s*"
756 (custom-unlispify-tag-name symbol))))
757
758 ;;;###autoload
759 (defun customize-option-other-window (symbol)
760 "Customize SYMBOL, which must be a user option variable.
761 Show the buffer in another window, but don't select it."
762 (interactive (custom-variable-prompt))
763 (custom-buffer-create-other-window
764 (list (list symbol 'custom-variable))
765 (format "*Customize Option: %s*" (custom-unlispify-tag-name symbol))))
766
767 ;;;###autoload
768 (defun customize-face (&optional symbol)
769 "Customize SYMBOL, which should be a face name or nil.
770 If SYMBOL is nil, customize all faces."
771 (interactive (list (completing-read "Customize face: (default all) "
772 obarray 'custom-facep)))
773 (if (or (null symbol) (and (stringp symbol) (zerop (length symbol))))
774 (let ((found nil))
775 (message "Looking for faces...")
776 (mapcar (lambda (symbol)
777 (setq found (cons (list symbol 'custom-face) found)))
778 (nreverse (mapcar 'intern
779 (sort (mapcar 'symbol-name (face-list))
780 'string<))))
781
782 (custom-buffer-create found "*Customize Faces*"))
783 (if (stringp symbol)
784 (setq symbol (intern symbol)))
785 (unless (symbolp symbol)
786 (error "Should be a symbol %S" symbol))
787 (custom-buffer-create (list (list symbol 'custom-face))
788 (format "*Customize Face: %s*"
789 (custom-unlispify-tag-name symbol)))))
790
791 ;;;###autoload
792 (defun customize-face-other-window (&optional symbol)
793 "Show customization buffer for FACE in other window."
794 (interactive (list (completing-read "Customize face: "
795 obarray 'custom-facep)))
796 (if (or (null symbol) (and (stringp symbol) (zerop (length symbol))))
797 ()
798 (if (stringp symbol)
799 (setq symbol (intern symbol)))
800 (unless (symbolp symbol)
801 (error "Should be a symbol %S" symbol))
802 (custom-buffer-create-other-window
803 (list (list symbol 'custom-face))
804 (format "*Customize Face: %s*" (custom-unlispify-tag-name symbol)))))
805
806 ;;;###autoload
807 (defun customize-customized ()
808 "Customize all user options set since the last save in this session."
809 (interactive)
810 (let ((found nil))
811 (mapatoms (lambda (symbol)
812 (and (get symbol 'customized-face)
813 (custom-facep symbol)
814 (setq found (cons (list symbol 'custom-face) found)))
815 (and (get symbol 'customized-value)
816 (boundp symbol)
817 (setq found
818 (cons (list symbol 'custom-variable) found)))))
819 (if found
820 (custom-buffer-create found "*Customize Customized*")
821 (error "No customized user options"))))
822
823 ;;;###autoload
824 (defun customize-saved ()
825 "Customize all already saved user options."
826 (interactive)
827 (let ((found nil))
828 (mapatoms (lambda (symbol)
829 (and (get symbol 'saved-face)
830 (custom-facep symbol)
831 (setq found (cons (list symbol 'custom-face) found)))
832 (and (get symbol 'saved-value)
833 (boundp symbol)
834 (setq found
835 (cons (list symbol 'custom-variable) found)))))
836 (if found
837 (custom-buffer-create found "*Customize Saved*")
838 (error "No saved user options"))))
839
840 ;;;###autoload
841 (defun customize-apropos (regexp &optional all)
842 "Customize all user options matching REGEXP.
843 If ALL (e.g., started with a prefix key), include options which are not
844 user-settable."
845 (interactive "sCustomize regexp: \nP")
846 (let ((found nil))
847 (mapatoms (lambda (symbol)
848 (when (string-match regexp (symbol-name symbol))
849 (when (get symbol 'custom-group)
850 (setq found (cons (list symbol 'custom-group) found)))
851 (when (custom-facep symbol)
852 (setq found (cons (list symbol 'custom-face) found)))
853 (when (and (boundp symbol)
854 (or (get symbol 'saved-value)
855 (get symbol 'standard-value)
856 (if all
857 (get symbol 'variable-documentation)
858 (user-variable-p symbol))))
859 (setq found
860 (cons (list symbol 'custom-variable) found))))))
861 (if found
862 (custom-buffer-create found "*Customize Apropos*")
863 (error "No matches"))))
864
865 ;;; Buffer.
866
867 ;;;###autoload
868 (defun custom-buffer-create (options &optional name)
869 "Create a buffer containing OPTIONS.
870 Optional NAME is the name of the buffer.
871 OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
872 SYMBOL is a customization option, and WIDGET is a widget for editing
873 that option."
874 (unless name (setq name "*Customization*"))
875 (kill-buffer (get-buffer-create name))
876 (switch-to-buffer (get-buffer-create name))
877 (custom-buffer-create-internal options))
878
879 ;;;###autoload
880 (defun custom-buffer-create-other-window (options &optional name)
881 "Create a buffer containing OPTIONS.
882 Optional NAME is the name of the buffer.
883 OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
884 SYMBOL is a customization option, and WIDGET is a widget for editing
885 that option."
886 (unless name (setq name "*Customization*"))
887 (kill-buffer (get-buffer-create name))
888 (let ((window (selected-window)))
889 (switch-to-buffer-other-window (get-buffer-create name))
890 (custom-buffer-create-internal options)
891 (select-window window)))
892
893 (defcustom custom-reset-button-menu nil
894 "If non-nil, only show a single reset button in customize buffers.
895 This button will have a menu with all three reset operations."
896 :type 'boolean
897 :group 'customize)
898
899 (defun custom-buffer-create-internal (options)
900 (message "Creating customization buffer...")
901 (custom-mode)
902 (widget-insert "This is a customization buffer.
903 Push RET or click mouse-2 on the word ")
904 (widget-create 'info-link
905 :tag "help"
906 :help-echo "Read the online help."
907 "(emacs)Easy Customization")
908 (widget-insert " for more information.\n\n")
909 (message "Creating customization buttons...")
910 (widget-create 'push-button
911 :tag "Set"
912 :help-echo "Set all modifications for this session."
913 :action (lambda (widget &optional event)
914 (custom-set)))
915 (widget-insert " ")
916 (widget-create 'push-button
917 :tag "Save"
918 :help-echo "\
919 Make the modifications default for future sessions."
920 :action (lambda (widget &optional event)
921 (custom-save)))
922 (widget-insert " ")
923 (if custom-reset-button-menu
924 (widget-create 'push-button
925 :tag "Reset"
926 :help-echo "Undo all modifications."
927 :mouse-down-action (lambda (&rest junk) t)
928 :action (lambda (widget &optional event)
929 (custom-reset event)))
930 (widget-create 'push-button
931 :tag "Reset"
932 :help-echo "Undo all modifications."
933 :action 'custom-reset-current)
934 (widget-insert " ")
935 (widget-create 'push-button
936 :tag "Reset to Saved"
937 :help-echo "Undo all modifications."
938 :action 'custom-reset-saved)
939 (widget-insert " ")
940 (widget-create 'push-button
941 :tag "Reset to Standard"
942 :help-echo "Undo all modifications."
943 :action 'custom-reset-standard))
944 (widget-insert " ")
945 (widget-create 'push-button
946 :tag "Done"
947 :help-echo "Bury the buffer."
948 :action (lambda (widget &optional event)
949 (bury-buffer)))
950 (widget-insert "\n\n")
951 (message "Creating customization items...")
952 (setq custom-options
953 (if (= (length options) 1)
954 (mapcar (lambda (entry)
955 (widget-create (nth 1 entry)
956 :custom-state 'unknown
957 :tag (custom-unlispify-tag-name
958 (nth 0 entry))
959 :value (nth 0 entry)))
960 options)
961 (let ((count 0)
962 (length (length options)))
963 (mapcar (lambda (entry)
964 (prog2
965 (message "Creating customization items %2d%%..."
966 (/ (* 100.0 count) length))
967 (widget-create (nth 1 entry)
968 :tag (custom-unlispify-tag-name
969 (nth 0 entry))
970 :value (nth 0 entry))
971 (setq count (1+ count))
972 (unless (eq (preceding-char) ?\n)
973 (widget-insert "\n"))
974 (widget-insert "\n")))
975 options))))
976 (unless (eq (preceding-char) ?\n)
977 (widget-insert "\n"))
978 (message "Creating customization magic...")
979 (mapcar 'custom-magic-reset custom-options)
980 (message "Creating customization setup...")
981 (widget-setup)
982 (goto-char (point-min))
983 (message "Creating customization buffer...done"))
984
985 ;;; Modification of Basic Widgets.
986 ;;
987 ;; We add extra properties to the basic widgets needed here. This is
988 ;; fine, as long as we are careful to stay within out own namespace.
989 ;;
990 ;; We want simple widgets to be displayed by default, but complex
991 ;; widgets to be hidden.
992
993 (widget-put (get 'item 'widget-type) :custom-show t)
994 (widget-put (get 'editable-field 'widget-type)
995 :custom-show (lambda (widget value)
996 (let ((pp (pp-to-string value)))
997 (cond ((string-match "\n" pp)
998 nil)
999 ((> (length pp) 40)
1000 nil)
1001 (t t)))))
1002 (widget-put (get 'menu-choice 'widget-type) :custom-show t)
1003
1004 ;;; The `custom-manual' Widget.
1005
1006 (define-widget 'custom-manual 'info-link
1007 "Link to the manual entry for this customization option."
1008 :help-echo "Read the manual entry for this option."
1009 :tag "Manual")
1010
1011 ;;; The `custom-magic' Widget.
1012
1013 (defface custom-invalid-face '((((class color))
1014 (:foreground "yellow" :background "red"))
1015 (t
1016 (:bold t :italic t :underline t)))
1017 "Face used when the customize item is invalid.")
1018
1019 (defface custom-rogue-face '((((class color))
1020 (:foreground "pink" :background "black"))
1021 (t
1022 (:underline t)))
1023 "Face used when the customize item is not defined for customization.")
1024
1025 (defface custom-modified-face '((((class color))
1026 (:foreground "white" :background "blue"))
1027 (t
1028 (:italic t :bold)))
1029 "Face used when the customize item has been modified.")
1030
1031 (defface custom-set-face '((((class color))
1032 (:foreground "blue" :background "white"))
1033 (t
1034 (:italic t)))
1035 "Face used when the customize item has been set.")
1036
1037 (defface custom-changed-face '((((class color))
1038 (:foreground "white" :background "blue"))
1039 (t
1040 (:italic t)))
1041 "Face used when the customize item has been changed.")
1042
1043 (defface custom-saved-face '((t (:underline t)))
1044 "Face used when the customize item has been saved.")
1045
1046 (defconst custom-magic-alist '((nil "#" underline "\
1047 uninitialized, you should not see this.")
1048 (unknown "?" italic "\
1049 unknown, you should not see this.")
1050 (hidden "-" default "\
1051 hidden, invoke the dots above to show." "\
1052 group now hidden, invoke the dots above to show contents.")
1053 (invalid "x" custom-invalid-face "\
1054 the value displayed for this %c is invalid and cannot be set.")
1055 (modified "*" custom-modified-face "\
1056 you have edited the value, and can now set the %c." "\
1057 you have edited something in this group, and can now set it.")
1058 (set "+" custom-set-face "\
1059 you have set this %c, but not saved it." "\
1060 something in this group has been set, but not yet saved.")
1061 (changed ":" custom-changed-face "\
1062 this %c has been changed outside the customize buffer." "\
1063 something in this group has been changed outside customize.")
1064 (saved "!" custom-saved-face "\
1065 this %c has been set and saved." "\
1066 something in this group has been set and saved.")
1067 (rogue "@" custom-rogue-face "\
1068 this %c has not been changed with customize." "\
1069 something in this group is not prepared for customization.")
1070 (standard " " nil "\
1071 this %c is unchanged from its standard setting." "\
1072 the visible members of this group are all at standard settings."))
1073 "Alist of customize option states.
1074 Each entry is of the form (STATE MAGIC FACE ITEM-DESC [ GROUP-DESC ]), where
1075
1076 STATE is one of the following symbols:
1077
1078 `nil'
1079 For internal use, should never occur.
1080 `unknown'
1081 For internal use, should never occur.
1082 `hidden'
1083 This item is not being displayed.
1084 `invalid'
1085 This item is modified, but has an invalid form.
1086 `modified'
1087 This item is modified, and has a valid form.
1088 `set'
1089 This item has been set but not saved.
1090 `changed'
1091 The current value of this item has been changed temporarily.
1092 `saved'
1093 This item is marked for saving.
1094 `rogue'
1095 This item has no customization information.
1096 `standard'
1097 This item is unchanged from the standard setting.
1098
1099 MAGIC is a string used to present that state.
1100
1101 FACE is a face used to present the state.
1102
1103 ITEM-DESC is a string describing the state for options.
1104
1105 GROUP-DESC is a string describing the state for groups. If this is
1106 left out, ITEM-DESC will be used.
1107
1108 The string %c in either description will be replaced with the
1109 category of the item. These are `group'. `option', and `face'.
1110
1111 The list should be sorted most significant first.")
1112
1113 (defcustom custom-magic-show 'long
1114 "If non-nil, show textual description of the state.
1115 If non-nil and not the symbol `long', only show first word."
1116 :type '(choice (const :tag "no" nil)
1117 (const short)
1118 (const long))
1119 :group 'customize)
1120
1121 (defcustom custom-magic-show-hidden '(option face)
1122 "Control whether the state button is shown for hidden items.
1123 The value should be a list with the custom categories where the state
1124 button should be visible. Possible categories are `group', `option',
1125 and `face'."
1126 :type '(set (const group) (const option) (const face))
1127 :group 'customize)
1128
1129 (defcustom custom-magic-show-button nil
1130 "Show a magic button indicating the state of each customization option."
1131 :type 'boolean
1132 :group 'customize)
1133
1134 (define-widget 'custom-magic 'default
1135 "Show and manipulate state for a customization option."
1136 :format "%v"
1137 :action 'widget-parent-action
1138 :notify 'ignore
1139 :value-get 'ignore
1140 :value-create 'custom-magic-value-create
1141 :value-delete 'widget-children-value-delete)
1142
1143 (defun widget-magic-mouse-down-action (widget &optional event)
1144 ;; Non-nil unless hidden.
1145 (not (eq (widget-get (widget-get (widget-get widget :parent) :parent)
1146 :custom-state)
1147 'hidden)))
1148
1149 (defun custom-magic-value-create (widget)
1150 ;; Create compact status report for WIDGET.
1151 (let* ((parent (widget-get widget :parent))
1152 (state (widget-get parent :custom-state))
1153 (hidden (eq state 'hidden))
1154 (entry (assq state custom-magic-alist))
1155 (magic (nth 1 entry))
1156 (face (nth 2 entry))
1157 (category (widget-get parent :custom-category))
1158 (text (or (and (eq category 'group)
1159 (nth 4 entry))
1160 (nth 3 entry)))
1161 (lisp (eq (widget-get parent :custom-form) 'lisp))
1162 children)
1163 (while (string-match "\\`\\(.*\\)%c\\(.*\\)\\'" text)
1164 (setq text (concat (match-string 1 text)
1165 (symbol-name category)
1166 (match-string 2 text))))
1167 (when (and custom-magic-show
1168 (or (not hidden)
1169 (memq category custom-magic-show-hidden)))
1170 (insert " ")
1171 (push (widget-create-child-and-convert
1172 widget 'choice-item
1173 :help-echo "Change the state of this item."
1174 :format (if hidden "%t" "%[%t%]")
1175 :button-prefix 'widget-push-button-prefix
1176 :button-suffix 'widget-push-button-suffix
1177 :mouse-down-action 'widget-magic-mouse-down-action
1178 :tag "State")
1179 children)
1180 (insert ": ")
1181 (if (eq custom-magic-show 'long)
1182 (insert text)
1183 (insert (symbol-name state)))
1184 (when lisp
1185 (insert " (lisp)"))
1186 (insert "\n"))
1187 (when custom-magic-show-button
1188 (when custom-magic-show
1189 (let ((indent (widget-get parent :indent)))
1190 (when indent
1191 (insert-char ? indent))))
1192 (push (widget-create-child-and-convert
1193 widget 'choice-item
1194 :mouse-down-action 'widget-magic-mouse-down-action
1195 :button-face face
1196 :button-prefix ""
1197 :button-suffix ""
1198 :help-echo "Change the state."
1199 :format (if hidden "%t" "%[%t%]")
1200 :tag (if lisp
1201 (concat "(" magic ")")
1202 (concat "[" magic "]")))
1203 children)
1204 (insert " "))
1205 (widget-put widget :children children)))
1206
1207 (defun custom-magic-reset (widget)
1208 "Redraw the :custom-magic property of WIDGET."
1209 (let ((magic (widget-get widget :custom-magic)))
1210 (widget-value-set magic (widget-value magic))))
1211
1212 ;;; The `custom' Widget.
1213
1214 (define-widget 'custom 'default
1215 "Customize a user option."
1216 :convert-widget 'custom-convert-widget
1217 :format-handler 'custom-format-handler
1218 :notify 'custom-notify
1219 :custom-level 1
1220 :custom-state 'hidden
1221 :documentation-property 'widget-subclass-responsibility
1222 :value-create 'widget-subclass-responsibility
1223 :value-delete 'widget-children-value-delete
1224 :value-get 'widget-value-value-get
1225 :validate 'widget-children-validate
1226 :match (lambda (widget value) (symbolp value)))
1227
1228 (defun custom-convert-widget (widget)
1229 ;; Initialize :value and :tag from :args in WIDGET.
1230 (let ((args (widget-get widget :args)))
1231 (when args
1232 (widget-put widget :value (widget-apply widget
1233 :value-to-internal (car args)))
1234 (widget-put widget :tag (custom-unlispify-tag-name (car args)))
1235 (widget-put widget :args nil)))
1236 widget)
1237
1238 (defun custom-format-handler (widget escape)
1239 ;; We recognize extra escape sequences.
1240 (let* ((buttons (widget-get widget :buttons))
1241 (state (widget-get widget :custom-state))
1242 (level (widget-get widget :custom-level)))
1243 (cond ((eq escape ?l)
1244 (when level
1245 (insert-char ?\ (1- level))
1246 (if (eq state 'hidden)
1247 (insert-char ?- (1+ level))
1248 (insert "/")
1249 (insert-char ?- level))))
1250 ((eq escape ?e)
1251 (when (and level (not (eq state 'hidden)))
1252 (insert "\n")
1253 (insert-char ?\ (1- level))
1254 (insert "\\")
1255 (insert-char ?- level)
1256 (insert " " (widget-get widget :tag) " group end ")
1257 (insert-char ?- (- 75 (current-column) level))
1258 (insert "/\n")))
1259 ((eq escape ?-)
1260 (when (and level (not (eq state 'hidden)))
1261 (insert-char ?- (- 76 (current-column) level))
1262 (insert "\\")))
1263 ((eq escape ?L)
1264 (push (widget-create-child-and-convert
1265 widget 'visibility
1266 :action 'custom-toggle-parent
1267 (not (eq state 'hidden)))
1268 buttons))
1269 ((eq escape ?m)
1270 (and (eq (preceding-char) ?\n)
1271 (widget-get widget :indent)
1272 (insert-char ? (widget-get widget :indent)))
1273 (let ((magic (widget-create-child-and-convert
1274 widget 'custom-magic nil)))
1275 (widget-put widget :custom-magic magic)
1276 (push magic buttons)
1277 (widget-put widget :buttons buttons)))
1278 ((eq escape ?a)
1279 (unless (eq state 'hidden)
1280 (let* ((symbol (widget-get widget :value))
1281 (links (get symbol 'custom-links))
1282 (many (> (length links) 2)))
1283 (when links
1284 (and (eq (preceding-char) ?\n)
1285 (widget-get widget :indent)
1286 (insert-char ? (widget-get widget :indent)))
1287 (insert "See also ")
1288 (while links
1289 (push (widget-create-child-and-convert widget (car links))
1290 buttons)
1291 (setq links (cdr links))
1292 (cond ((null links)
1293 (insert ".\n"))
1294 ((null (cdr links))
1295 (if many
1296 (insert ", and ")
1297 (insert " and ")))
1298 (t
1299 (insert ", "))))
1300 (widget-put widget :buttons buttons)))))
1301 (t
1302 (widget-default-format-handler widget escape)))))
1303
1304 (defun custom-notify (widget &rest args)
1305 "Keep track of changes."
1306 (let ((state (widget-get widget :custom-state)))
1307 (unless (eq state 'modified)
1308 (unless (memq state '(nil unknown hidden))
1309 (widget-put widget :custom-state 'modified))
1310 (custom-magic-reset widget)
1311 (apply 'widget-default-notify widget args))))
1312
1313 (defun custom-redraw (widget)
1314 "Redraw WIDGET with current settings."
1315 (let ((line (count-lines (point-min) (point)))
1316 (column (current-column))
1317 (pos (point))
1318 (from (marker-position (widget-get widget :from)))
1319 (to (marker-position (widget-get widget :to))))
1320 (save-excursion
1321 (widget-value-set widget (widget-value widget))
1322 (custom-redraw-magic widget))
1323 (when (and (>= pos from) (<= pos to))
1324 (condition-case nil
1325 (progn
1326 (if (> column 0)
1327 (goto-line line)
1328 (goto-line (1+ line)))
1329 (move-to-column column))
1330 (error nil)))))
1331
1332 (defun custom-redraw-magic (widget)
1333 "Redraw WIDGET state with current settings."
1334 (while widget
1335 (let ((magic (widget-get widget :custom-magic)))
1336 (unless magic
1337 (debug))
1338 (widget-value-set magic (widget-value magic))
1339 (when (setq widget (widget-get widget :group))
1340 (custom-group-state-update widget))))
1341 (widget-setup))
1342
1343 (defun custom-show (widget value)
1344 "Non-nil if WIDGET should be shown with VALUE by default."
1345 (let ((show (widget-get widget :custom-show)))
1346 (cond ((null show)
1347 nil)
1348 ((eq t show)
1349 t)
1350 (t
1351 (funcall show widget value)))))
1352
1353 (defvar custom-load-recursion nil
1354 "Hack to avoid recursive dependencies.")
1355
1356 (defun custom-load-symbol (symbol)
1357 "Load all dependencies for SYMBOL."
1358 (unless custom-load-recursion
1359 (let ((custom-load-recursion t)
1360 (loads (get symbol 'custom-loads))
1361 load)
1362 (while loads
1363 (setq load (car loads)
1364 loads (cdr loads))
1365 (cond ((symbolp load)
1366 (condition-case nil
1367 (require load)
1368 (error nil)))
1369 ;; Don't reload a file already loaded.
1370 ((assoc load load-history))
1371 ((assoc (locate-library load) load-history))
1372 (t
1373 (condition-case nil
1374 ;; Without this, we would load cus-edit recursively.
1375 ;; We are still loading it when we call this,
1376 ;; and it is not in load-history yet.
1377 (or (equal load "cus-edit")
1378 (load-library load))
1379 (error nil))))))))
1380
1381 (defun custom-load-widget (widget)
1382 "Load all dependencies for WIDGET."
1383 (custom-load-symbol (widget-value widget)))
1384
1385 (defun custom-toggle-hide (widget)
1386 "Toggle visibility of WIDGET."
1387 (let ((state (widget-get widget :custom-state)))
1388 (cond ((memq state '(invalid modified))
1389 (error "There are unset changes"))
1390 ((eq state 'hidden)
1391 (widget-put widget :custom-state 'unknown))
1392 (t
1393 (widget-put widget :documentation-shown nil)
1394 (widget-put widget :custom-state 'hidden)))
1395 (custom-redraw widget)))
1396
1397 (defun custom-toggle-parent (widget &rest ignore)
1398 "Toggle visibility of parent to WIDGET."
1399 (custom-toggle-hide (widget-get widget :parent)))
1400
1401 ;;; The `custom-variable' Widget.
1402
1403 (defface custom-variable-sample-face '((t (:underline t)))
1404 "Face used for unpushable variable tags."
1405 :group 'custom-faces)
1406
1407 (defface custom-variable-button-face '((t (:underline t :bold t)))
1408 "Face used for pushable variable tags."
1409 :group 'custom-faces)
1410
1411 (define-widget 'custom-variable 'custom
1412 "Customize variable."
1413 :format "%v%m%h%a"
1414 :help-echo "Set or reset this variable."
1415 :documentation-property 'variable-documentation
1416 :custom-category 'option
1417 :custom-state nil
1418 :custom-menu 'custom-variable-menu-create
1419 :custom-form 'edit
1420 :value-create 'custom-variable-value-create
1421 :action 'custom-variable-action
1422 :custom-set 'custom-variable-set
1423 :custom-save 'custom-variable-save
1424 :custom-reset-current 'custom-redraw
1425 :custom-reset-saved 'custom-variable-reset-saved
1426 :custom-reset-standard 'custom-variable-reset-standard)
1427
1428 (defun custom-variable-type (symbol)
1429 "Return a widget suitable for editing the value of SYMBOL.
1430 If SYMBOL has a `custom-type' property, use that.
1431 Otherwise, look up symbol in `custom-guess-type-alist'."
1432 (let* ((type (or (get symbol 'custom-type)
1433 (and (not (get symbol 'standard-value))
1434 (custom-guess-type symbol))
1435 'sexp))
1436 (options (get symbol 'custom-options))
1437 (tmp (if (listp type)
1438 (copy-sequence type)
1439 (list type))))
1440 (when options
1441 (widget-put tmp :options options))
1442 tmp))
1443
1444 (defun custom-variable-value-create (widget)
1445 "Here is where you edit the variables value."
1446 (custom-load-widget widget)
1447 (let* ((buttons (widget-get widget :buttons))
1448 (children (widget-get widget :children))
1449 (form (widget-get widget :custom-form))
1450 (state (widget-get widget :custom-state))
1451 (symbol (widget-get widget :value))
1452 (tag (widget-get widget :tag))
1453 (type (custom-variable-type symbol))
1454 (conv (widget-convert type))
1455 (get (or (get symbol 'custom-get) 'default-value))
1456 (value (if (default-boundp symbol)
1457 (funcall get symbol)
1458 (widget-get conv :value))))
1459 ;; If the widget is new, the child determine whether it is hidden.
1460 (cond (state)
1461 ((custom-show type value)
1462 (setq state 'unknown))
1463 (t
1464 (setq state 'hidden)))
1465 ;; If we don't know the state, see if we need to edit it in lisp form.
1466 (when (eq state 'unknown)
1467 (unless (widget-apply conv :match value)
1468 ;; (widget-apply (widget-convert type) :match value)
1469 (setq form 'lisp)))
1470 ;; Now we can create the child widget.
1471 (cond ((eq state 'hidden)
1472 ;; Indicate hidden value.
1473 (push (widget-create-child-and-convert
1474 widget 'item
1475 :format "%{%t%}: "
1476 :sample-face 'custom-variable-sample-face
1477 :tag tag
1478 :parent widget)
1479 buttons)
1480 (push (widget-create-child-and-convert
1481 widget 'visibility
1482 :action 'custom-toggle-parent
1483 nil)
1484 buttons))
1485 ((eq form 'lisp)
1486 ;; In lisp mode edit the saved value when possible.
1487 (let* ((value (cond ((get symbol 'saved-value)
1488 (car (get symbol 'saved-value)))
1489 ((get symbol 'standard-value)
1490 (car (get symbol 'standard-value)))
1491 ((default-boundp symbol)
1492 (custom-quote (funcall get symbol)))
1493 (t
1494 (custom-quote (widget-get conv :value))))))
1495 (insert (symbol-name symbol) ": ")
1496 (push (widget-create-child-and-convert
1497 widget 'visibility
1498 :action 'custom-toggle-parent
1499 t)
1500 buttons)
1501 (insert " ")
1502 (push (widget-create-child-and-convert
1503 widget 'sexp
1504 :button-face 'custom-variable-button-face
1505 :format "%v"
1506 :tag (symbol-name symbol)
1507 :parent widget
1508 :value value)
1509 children)))
1510 (t
1511 ;; Edit mode.
1512 (let* ((format (widget-get type :format))
1513 tag-format value-format)
1514 (unless (string-match ":" format)
1515 (error "Bad format."))
1516 (setq tag-format (substring format 0 (match-end 0)))
1517 (setq value-format (substring format (match-end 0)))
1518 (push (widget-create-child-and-convert
1519 widget 'item
1520 :format tag-format
1521 :action 'custom-tag-action
1522 :mouse-down-action 'custom-tag-mouse-down-action
1523 :button-face 'custom-variable-button-face
1524 :sample-face 'custom-variable-sample-face
1525 tag)
1526 buttons)
1527 (insert " ")
1528 (push (widget-create-child-and-convert
1529 widget 'visibility
1530 :action 'custom-toggle-parent
1531 t)
1532 buttons)
1533 (push (widget-create-child-and-convert
1534 widget type
1535 :format value-format
1536 :value value)
1537 children))))
1538 ;; Now update the state.
1539 (unless (eq (preceding-char) ?\n)
1540 (widget-insert "\n"))
1541 (if (eq state 'hidden)
1542 (widget-put widget :custom-state state)
1543 (custom-variable-state-set widget))
1544 (widget-put widget :custom-form form)
1545 (widget-put widget :buttons buttons)
1546 (widget-put widget :children children)))
1547
1548 (defun custom-tag-action (widget &rest args)
1549 "Pass :action to first child of WIDGET's parent."
1550 (apply 'widget-apply (car (widget-get (widget-get widget :parent) :children))
1551 :action args))
1552
1553 (defun custom-tag-mouse-down-action (widget &rest args)
1554 "Pass :mouse-down-action to first child of WIDGET's parent."
1555 (apply 'widget-apply (car (widget-get (widget-get widget :parent) :children))
1556 :mouse-down-action args))
1557
1558 (defun custom-variable-state-set (widget)
1559 "Set the state of WIDGET."
1560 (let* ((symbol (widget-value widget))
1561 (get (or (get symbol 'custom-get) 'default-value))
1562 (value (if (default-boundp symbol)
1563 (funcall get symbol)
1564 (widget-get widget :value)))
1565 tmp
1566 (state (cond ((setq tmp (get symbol 'customized-value))
1567 (if (condition-case nil
1568 (equal value (eval (car tmp)))
1569 (error nil))
1570 'set
1571 'changed))
1572 ((setq tmp (get symbol 'saved-value))
1573 (if (condition-case nil
1574 (equal value (eval (car tmp)))
1575 (error nil))
1576 'saved
1577 'changed))
1578 ((setq tmp (get symbol 'standard-value))
1579 (if (condition-case nil
1580 (equal value (eval (car tmp)))
1581 (error nil))
1582 'standard
1583 'changed))
1584 (t 'rogue))))
1585 (widget-put widget :custom-state state)))
1586
1587 (defvar custom-variable-menu
1588 '(("Edit" custom-variable-edit
1589 (lambda (widget)
1590 (not (eq (widget-get widget :custom-form) 'edit))))
1591 ("Edit Lisp" custom-variable-edit-lisp
1592 (lambda (widget)
1593 (not (eq (widget-get widget :custom-form) 'lisp))))
1594 ("Set" custom-variable-set
1595 (lambda (widget)
1596 (eq (widget-get widget :custom-state) 'modified)))
1597 ("Save" custom-variable-save
1598 (lambda (widget)
1599 (memq (widget-get widget :custom-state) '(modified set changed rogue))))
1600 ("Reset to Current" custom-redraw
1601 (lambda (widget)
1602 (and (default-boundp (widget-value widget))
1603 (memq (widget-get widget :custom-state) '(modified changed)))))
1604 ("Reset to Saved" custom-variable-reset-saved
1605 (lambda (widget)
1606 (and (get (widget-value widget) 'saved-value)
1607 (memq (widget-get widget :custom-state)
1608 '(modified set changed rogue)))))
1609 ("Reset to Standard Settings" custom-variable-reset-standard
1610 (lambda (widget)
1611 (and (get (widget-value widget) 'standard-value)
1612 (memq (widget-get widget :custom-state)
1613 '(modified set changed saved rogue))))))
1614 "Alist of actions for the `custom-variable' widget.
1615 Each entry has the form (NAME ACTION FILTER) where NAME is the name of
1616 the menu entry, ACTION is the function to call on the widget when the
1617 menu is selected, and FILTER is a predicate which takes a `custom-variable'
1618 widget as an argument, and returns non-nil if ACTION is valid on that
1619 widget. If FILTER is nil, ACTION is always valid.")
1620
1621 (defun custom-variable-action (widget &optional event)
1622 "Show the menu for `custom-variable' WIDGET.
1623 Optional EVENT is the location for the menu."
1624 (if (eq (widget-get widget :custom-state) 'hidden)
1625 (custom-toggle-hide widget)
1626 (unless (eq (widget-get widget :custom-state) 'modified)
1627 (custom-variable-state-set widget))
1628 (custom-redraw-magic widget)
1629 (let* ((completion-ignore-case t)
1630 (answer (widget-choose (concat "Operation on "
1631 (custom-unlispify-tag-name
1632 (widget-get widget :value)))
1633 (custom-menu-filter custom-variable-menu
1634 widget)
1635 event)))
1636 (if answer
1637 (funcall answer widget)))))
1638
1639 (defun custom-variable-edit (widget)
1640 "Edit value of WIDGET."
1641 (widget-put widget :custom-state 'unknown)
1642 (widget-put widget :custom-form 'edit)
1643 (custom-redraw widget))
1644
1645 (defun custom-variable-edit-lisp (widget)
1646 "Edit the lisp representation of the value of WIDGET."
1647 (widget-put widget :custom-state 'unknown)
1648 (widget-put widget :custom-form 'lisp)
1649 (custom-redraw widget))
1650
1651 (defun custom-variable-set (widget)
1652 "Set the current value for the variable being edited by WIDGET."
1653 (let* ((form (widget-get widget :custom-form))
1654 (state (widget-get widget :custom-state))
1655 (child (car (widget-get widget :children)))
1656 (symbol (widget-value widget))
1657 (set (or (get symbol 'custom-set) 'set-default))
1658 val)
1659 (cond ((eq state 'hidden)
1660 (error "Cannot set hidden variable."))
1661 ((setq val (widget-apply child :validate))
1662 (goto-char (widget-get val :from))
1663 (error "%s" (widget-get val :error)))
1664 ((eq form 'lisp)
1665 (funcall set symbol (eval (setq val (widget-value child))))
1666 (put symbol 'customized-value (list val)))
1667 (t
1668 (funcall set symbol (setq val (widget-value child)))
1669 (put symbol 'customized-value (list (custom-quote val)))))
1670 (custom-variable-state-set widget)
1671 (custom-redraw-magic widget)))
1672
1673 (defun custom-variable-save (widget)
1674 "Set the default value for the variable being edited by WIDGET."
1675 (let* ((form (widget-get widget :custom-form))
1676 (state (widget-get widget :custom-state))
1677 (child (car (widget-get widget :children)))
1678 (symbol (widget-value widget))
1679 (set (or (get symbol 'custom-set) 'set-default))
1680 val)
1681 (cond ((eq state 'hidden)
1682 (error "Cannot set hidden variable."))
1683 ((setq val (widget-apply child :validate))
1684 (goto-char (widget-get val :from))
1685 (error "%s" (widget-get val :error)))
1686 ((eq form 'lisp)
1687 (put symbol 'saved-value (list (widget-value child)))
1688 (funcall set symbol (eval (widget-value child))))
1689 (t
1690 (put symbol
1691 'saved-value (list (custom-quote (widget-value
1692 child))))
1693 (funcall set symbol (widget-value child))))
1694 (put symbol 'customized-value nil)
1695 (custom-save-all)
1696 (custom-variable-state-set widget)
1697 (custom-redraw-magic widget)))
1698
1699 (defun custom-variable-reset-saved (widget)
1700 "Restore the saved value for the variable being edited by WIDGET."
1701 (let* ((symbol (widget-value widget))
1702 (set (or (get symbol 'custom-set) 'set-default)))
1703 (if (get symbol 'saved-value)
1704 (condition-case nil
1705 (funcall set symbol (eval (car (get symbol 'saved-value))))
1706 (error nil))
1707 (error "No saved value for %s" symbol))
1708 (put symbol 'customized-value nil)
1709 (widget-put widget :custom-state 'unknown)
1710 (custom-redraw widget)))
1711
1712 (defun custom-variable-reset-standard (widget)
1713 "Restore the standard setting for the variable being edited by WIDGET."
1714 (let* ((symbol (widget-value widget))
1715 (set (or (get symbol 'custom-set) 'set-default)))
1716 (if (get symbol 'standard-value)
1717 (funcall set symbol (eval (car (get symbol 'standard-value))))
1718 (error "No standard setting known for %S" symbol))
1719 (put symbol 'customized-value nil)
1720 (when (get symbol 'saved-value)
1721 (put symbol 'saved-value nil)
1722 (custom-save-all))
1723 (widget-put widget :custom-state 'unknown)
1724 (custom-redraw widget)))
1725
1726 ;;; The `custom-face-edit' Widget.
1727
1728 (define-widget 'custom-face-edit 'checklist
1729 "Edit face attributes."
1730 :format "%t: %v"
1731 :tag "Attributes"
1732 :extra-offset 12
1733 :button-args '(:help-echo "Control whether this attribute have any effect.")
1734 :args (mapcar (lambda (att)
1735 (list 'group
1736 :inline t
1737 :sibling-args (widget-get (nth 1 att) :sibling-args)
1738 (list 'const :format "" :value (nth 0 att))
1739 (nth 1 att)))
1740 custom-face-attributes))
1741
1742 ;;; The `custom-display' Widget.
1743
1744 (define-widget 'custom-display 'menu-choice
1745 "Select a display type."
1746 :tag "Display"
1747 :value t
1748 :help-echo "Specify frames where the face attributes should be used."
1749 :args '((const :tag "all" t)
1750 (checklist
1751 :offset 0
1752 :extra-offset 9
1753 :args ((group :sibling-args (:help-echo "\
1754 Only match the specified window systems.")
1755 (const :format "Type: "
1756 type)
1757 (checklist :inline t
1758 :offset 0
1759 (const :format "X "
1760 :sibling-args (:help-echo "\
1761 The X11 Window System.")
1762 x)
1763 (const :format "PM "
1764 :sibling-args (:help-echo "\
1765 OS/2 Presentation Manager.")
1766 pm)
1767 (const :format "Win32 "
1768 :sibling-args (:help-echo "\
1769 Windows NT/95/97.")
1770 win32)
1771 (const :format "DOS "
1772 :sibling-args (:help-echo "\
1773 Plain MS-DOS.")
1774 pc)
1775 (const :format "TTY%n"
1776 :sibling-args (:help-echo "\
1777 Plain text terminals.")
1778 tty)))
1779 (group :sibling-args (:help-echo "\
1780 Only match the frames with the specified color support.")
1781 (const :format "Class: "
1782 class)
1783 (checklist :inline t
1784 :offset 0
1785 (const :format "Color "
1786 :sibling-args (:help-echo "\
1787 Match color frames.")
1788 color)
1789 (const :format "Grayscale "
1790 :sibling-args (:help-echo "\
1791 Match grayscale frames.")
1792 grayscale)
1793 (const :format "Monochrome%n"
1794 :sibling-args (:help-echo "\
1795 Match frames with no color support.")
1796 mono)))
1797 (group :sibling-args (:help-echo "\
1798 Only match frames with the specified intensity.")
1799 (const :format "\
1800 Background brightness: "
1801 background)
1802 (checklist :inline t
1803 :offset 0
1804 (const :format "Light "
1805 :sibling-args (:help-echo "\
1806 Match frames with light backgrounds.")
1807 light)
1808 (const :format "Dark\n"
1809 :sibling-args (:help-echo "\
1810 Match frames with dark backgrounds.")
1811 dark)))))))
1812
1813 ;;; The `custom-face' Widget.
1814
1815 (defface custom-face-tag-face '((t (:underline t)))
1816 "Face used for face tags."
1817 :group 'custom-faces)
1818
1819 (define-widget 'custom-face 'custom
1820 "Customize face."
1821 :format "%{%t%}: %s %L\n%m%h%a%v"
1822 :format-handler 'custom-face-format-handler
1823 :sample-face 'custom-face-tag-face
1824 :help-echo "Set or reset this face."
1825 :documentation-property '(lambda (face)
1826 (face-doc-string face))
1827 :value-create 'custom-face-value-create
1828 :action 'custom-face-action
1829 :custom-category 'face
1830 :custom-form 'selected
1831 :custom-set 'custom-face-set
1832 :custom-save 'custom-face-save
1833 :custom-reset-current 'custom-redraw
1834 :custom-reset-saved 'custom-face-reset-saved
1835 :custom-reset-standard 'custom-face-reset-standard
1836 :custom-menu 'custom-face-menu-create)
1837
1838 (defun custom-face-format-handler (widget escape)
1839 ;; We recognize extra escape sequences.
1840 (let (child
1841 (symbol (widget-get widget :value)))
1842 (cond ((eq escape ?s)
1843 (and (string-match "XEmacs" emacs-version)
1844 ;; XEmacs cannot display initialized faces.
1845 (not (custom-facep symbol))
1846 (copy-face 'custom-face-empty symbol))
1847 (setq child (widget-create-child-and-convert
1848 widget 'item
1849 :format "(%{%t%})"
1850 :sample-face symbol
1851 :tag "sample")))
1852 (t
1853 (custom-format-handler widget escape)))
1854 (when child
1855 (widget-put widget
1856 :buttons (cons child (widget-get widget :buttons))))))
1857
1858 (define-widget 'custom-face-all 'editable-list
1859 "An editable list of display specifications and attributes."
1860 :entry-format "%i %d %v"
1861 :insert-button-args '(:help-echo "Insert new display specification here.")
1862 :append-button-args '(:help-echo "Append new display specification here.")
1863 :delete-button-args '(:help-echo "Delete this display specification.")
1864 :args '((group :format "%v" custom-display custom-face-edit)))
1865
1866 (defconst custom-face-all (widget-convert 'custom-face-all)
1867 "Converted version of the `custom-face-all' widget.")
1868
1869 (define-widget 'custom-display-unselected 'item
1870 "A display specification that doesn't match the selected display."
1871 :match 'custom-display-unselected-match)
1872
1873 (defun custom-display-unselected-match (widget value)
1874 "Non-nil if VALUE is an unselected display specification."
1875 (not (face-spec-set-match-display value (selected-frame))))
1876
1877 (define-widget 'custom-face-selected 'group
1878 "Edit the attributes of the selected display in a face specification."
1879 :args '((repeat :format ""
1880 :inline t
1881 (group custom-display-unselected sexp))
1882 (group (sexp :format "") custom-face-edit)
1883 (repeat :format ""
1884 :inline t
1885 sexp)))
1886
1887 (defconst custom-face-selected (widget-convert 'custom-face-selected)
1888 "Converted version of the `custom-face-selected' widget.")
1889
1890 (defun custom-face-value-create (widget)
1891 ;; Create a list of the display specifications.
1892 (unless (eq (preceding-char) ?\n)
1893 (insert "\n"))
1894 (when (not (eq (widget-get widget :custom-state) 'hidden))
1895 (message "Creating face editor...")
1896 (custom-load-widget widget)
1897 (let* ((symbol (widget-value widget))
1898 (spec (or (get symbol 'saved-face)
1899 (get symbol 'face-defface-spec)
1900 ;; Attempt to construct it.
1901 (list (list t (custom-face-attributes-get
1902 symbol (selected-frame))))))
1903 (form (widget-get widget :custom-form))
1904 (indent (widget-get widget :indent))
1905 (edit (widget-create-child-and-convert
1906 widget
1907 (cond ((and (eq form 'selected)
1908 (widget-apply custom-face-selected :match spec))
1909 (when indent (insert-char ?\ indent))
1910 'custom-face-selected)
1911 ((and (not (eq form 'lisp))
1912 (widget-apply custom-face-all :match spec))
1913 'custom-face-all)
1914 (t
1915 (when indent (insert-char ?\ indent))
1916 'sexp))
1917 :value spec)))
1918 (custom-face-state-set widget)
1919 (widget-put widget :children (list edit)))
1920 (message "Creating face editor...done")))
1921
1922 (defvar custom-face-menu
1923 '(("Edit Selected" custom-face-edit-selected
1924 (lambda (widget)
1925 (not (eq (widget-get widget :custom-form) 'selected))))
1926 ("Edit All" custom-face-edit-all
1927 (lambda (widget)
1928 (not (eq (widget-get widget :custom-form) 'all))))
1929 ("Edit Lisp" custom-face-edit-lisp
1930 (lambda (widget)
1931 (not (eq (widget-get widget :custom-form) 'lisp))))
1932 ("Set" custom-face-set)
1933 ("Save" custom-face-save)
1934 ("Reset to Saved" custom-face-reset-saved
1935 (lambda (widget)
1936 (get (widget-value widget) 'saved-face)))
1937 ("Reset to Standard Setting" custom-face-reset-standard
1938 (lambda (widget)
1939 (get (widget-value widget) 'face-defface-spec))))
1940 "Alist of actions for the `custom-face' widget.
1941 Each entry has the form (NAME ACTION FILTER) where NAME is the name of
1942 the menu entry, ACTION is the function to call on the widget when the
1943 menu is selected, and FILTER is a predicate which takes a `custom-face'
1944 widget as an argument, and returns non-nil if ACTION is valid on that
1945 widget. If FILTER is nil, ACTION is always valid.")
1946
1947 (defun custom-face-edit-selected (widget)
1948 "Edit selected attributes of the value of WIDGET."
1949 (widget-put widget :custom-state 'unknown)
1950 (widget-put widget :custom-form 'selected)
1951 (custom-redraw widget))
1952
1953 (defun custom-face-edit-all (widget)
1954 "Edit all attributes of the value of WIDGET."
1955 (widget-put widget :custom-state 'unknown)
1956 (widget-put widget :custom-form 'all)
1957 (custom-redraw widget))
1958
1959 (defun custom-face-edit-lisp (widget)
1960 "Edit the lisp representation of the value of WIDGET."
1961 (widget-put widget :custom-state 'unknown)
1962 (widget-put widget :custom-form 'lisp)
1963 (custom-redraw widget))
1964
1965 (defun custom-face-state-set (widget)
1966 "Set the state of WIDGET."
1967 (let ((symbol (widget-value widget)))
1968 (widget-put widget :custom-state (cond ((get symbol 'customized-face)
1969 'set)
1970 ((get symbol 'saved-face)
1971 'saved)
1972 ((get symbol 'face-defface-spec)
1973 'standard)
1974 (t
1975 'rogue)))))
1976
1977 (defun custom-face-action (widget &optional event)
1978 "Show the menu for `custom-face' WIDGET.
1979 Optional EVENT is the location for the menu."
1980 (if (eq (widget-get widget :custom-state) 'hidden)
1981 (custom-toggle-hide widget)
1982 (let* ((completion-ignore-case t)
1983 (symbol (widget-get widget :value))
1984 (answer (widget-choose (concat "Operation on "
1985 (custom-unlispify-tag-name symbol))
1986 (custom-menu-filter custom-face-menu
1987 widget)
1988 event)))
1989 (if answer
1990 (funcall answer widget)))))
1991
1992 (defun custom-face-set (widget)
1993 "Make the face attributes in WIDGET take effect."
1994 (let* ((symbol (widget-value widget))
1995 (child (car (widget-get widget :children)))
1996 (value (widget-value child)))
1997 (put symbol 'customized-face value)
1998 (face-spec-set symbol value)
1999 (custom-face-state-set widget)
2000 (custom-redraw-magic widget)))
2001
2002 (defun custom-face-save (widget)
2003 "Make the face attributes in WIDGET default."
2004 (let* ((symbol (widget-value widget))
2005 (child (car (widget-get widget :children)))
2006 (value (widget-value child)))
2007 (face-spec-set symbol value)
2008 (put symbol 'saved-face value)
2009 (put symbol 'customized-face nil)
2010 (custom-face-state-set widget)
2011 (custom-redraw-magic widget)))
2012
2013 (defun custom-face-reset-saved (widget)
2014 "Restore WIDGET to the face's default attributes."
2015 (let* ((symbol (widget-value widget))
2016 (child (car (widget-get widget :children)))
2017 (value (get symbol 'saved-face)))
2018 (unless value
2019 (error "No saved value for this face"))
2020 (put symbol 'customized-face nil)
2021 (face-spec-set symbol value)
2022 (widget-value-set child value)
2023 (custom-face-state-set widget)
2024 (custom-redraw-magic widget)))
2025
2026 (defun custom-face-reset-standard (widget)
2027 "Restore WIDGET to the face's standard settings."
2028 (let* ((symbol (widget-value widget))
2029 (child (car (widget-get widget :children)))
2030 (value (get symbol 'face-defface-spec)))
2031 (unless value
2032 (error "No standard setting for this face"))
2033 (put symbol 'customized-face nil)
2034 (when (get symbol 'saved-face)
2035 (put symbol 'saved-face nil)
2036 (custom-save-all))
2037 (face-spec-set symbol value)
2038 (widget-value-set child value)
2039 (custom-face-state-set widget)
2040 (custom-redraw-magic widget)))
2041
2042 ;;; The `face' Widget.
2043
2044 (define-widget 'face 'default
2045 "Select and customize a face."
2046 :convert-widget 'widget-value-convert-widget
2047 :format "%[%t%]: %v"
2048 :tag "Face"
2049 :value 'default
2050 :value-create 'widget-face-value-create
2051 :value-delete 'widget-face-value-delete
2052 :value-get 'widget-value-value-get
2053 :validate 'widget-children-validate
2054 :action 'widget-face-action
2055 :match '(lambda (widget value) (symbolp value)))
2056
2057 (defun widget-face-value-create (widget)
2058 ;; Create a `custom-face' child.
2059 (let* ((symbol (widget-value widget))
2060 (child (widget-create-child-and-convert
2061 widget 'custom-face
2062 :format "%t %s %L\n%m%h%v"
2063 :custom-level nil
2064 :value symbol)))
2065 (custom-magic-reset child)
2066 (setq custom-options (cons child custom-options))
2067 (widget-put widget :children (list child))))
2068
2069 (defun widget-face-value-delete (widget)
2070 ;; Remove the child from the options.
2071 (let ((child (car (widget-get widget :children))))
2072 (setq custom-options (delq child custom-options))
2073 (widget-children-value-delete widget)))
2074
2075 (defvar face-history nil
2076 "History of entered face names.")
2077
2078 (defun widget-face-action (widget &optional event)
2079 "Prompt for a face."
2080 (let ((answer (completing-read "Face: "
2081 (mapcar (lambda (face)
2082 (list (symbol-name face)))
2083 (face-list))
2084 nil nil nil
2085 'face-history)))
2086 (unless (zerop (length answer))
2087 (widget-value-set widget (intern answer))
2088 (widget-apply widget :notify widget event)
2089 (widget-setup))))
2090
2091 ;;; The `hook' Widget.
2092
2093 (define-widget 'hook 'list
2094 "A emacs lisp hook"
2095 :convert-widget 'custom-hook-convert-widget
2096 :tag "Hook")
2097
2098 (defun custom-hook-convert-widget (widget)
2099 ;; Handle `:custom-options'.
2100 (let* ((options (widget-get widget :options))
2101 (other `(editable-list :inline t
2102 :entry-format "%i %d%v"
2103 (function :format " %v")))
2104 (args (if options
2105 (list `(checklist :inline t
2106 ,@(mapcar (lambda (entry)
2107 `(function-item ,entry))
2108 options))
2109 other)
2110 (list other))))
2111 (widget-put widget :args args)
2112 widget))
2113
2114 ;;; The `custom-group' Widget.
2115
2116 (defcustom custom-group-tag-faces '(custom-group-tag-face-1)
2117 ;; In XEmacs, this ought to play games with font size.
2118 "Face used for group tags.
2119 The first member is used for level 1 groups, the second for level 2,
2120 and so forth. The remaining group tags are shown with
2121 `custom-group-tag-face'."
2122 :type '(repeat face)
2123 :group 'custom-faces)
2124
2125 (defface custom-group-tag-face-1 '((((class color)
2126 (background dark))
2127 (:foreground "pink" :underline t))
2128 (((class color)
2129 (background light))
2130 (:foreground "red" :underline t))
2131 (t (:underline t)))
2132 "Face used for group tags.")
2133
2134 (defface custom-group-tag-face '((((class color)
2135 (background dark))
2136 (:foreground "light blue" :underline t))
2137 (((class color)
2138 (background light))
2139 (:foreground "blue" :underline t))
2140 (t (:underline t)))
2141 "Face used for low level group tags."
2142 :group 'custom-faces)
2143
2144 (define-widget 'custom-group 'custom
2145 "Customize group."
2146 :format "%l %{%t%} group: %L %-\n%m%h%a%v%e"
2147 :sample-face-get 'custom-group-sample-face-get
2148 :documentation-property 'group-documentation
2149 :help-echo "Set or reset all members of this group."
2150 :value-create 'custom-group-value-create
2151 :action 'custom-group-action
2152 :custom-category 'group
2153 :custom-set 'custom-group-set
2154 :custom-save 'custom-group-save
2155 :custom-reset-current 'custom-group-reset-current
2156 :custom-reset-saved 'custom-group-reset-saved
2157 :custom-reset-standard 'custom-group-reset-standard
2158 :custom-menu 'custom-group-menu-create)
2159
2160 (defun custom-group-sample-face-get (widget)
2161 ;; Use :sample-face.
2162 (or (nth (1- (widget-get widget :custom-level)) custom-group-tag-faces)
2163 'custom-group-tag-face))
2164
2165 (defun custom-group-value-create (widget)
2166 (let ((state (widget-get widget :custom-state)))
2167 (unless (eq state 'hidden)
2168 (message "Creating group...")
2169 (custom-load-widget widget)
2170 (let* ((level (widget-get widget :custom-level))
2171 (symbol (widget-value widget))
2172 (members (sort (get symbol 'custom-group)
2173 custom-buffer-sort-predicate))
2174 (prefixes (widget-get widget :custom-prefixes))
2175 (custom-prefix-list (custom-prefix-add symbol prefixes))
2176 (length (length members))
2177 (count 0)
2178 (children (mapcar (lambda (entry)
2179 (widget-insert "\n")
2180 (message "Creating group members... %2d%%"
2181 (/ (* 100.0 count) length))
2182 (setq count (1+ count))
2183 (prog1
2184 (widget-create-child-and-convert
2185 widget (nth 1 entry)
2186 :group widget
2187 :tag (custom-unlispify-tag-name
2188 (nth 0 entry))
2189 :custom-prefixes custom-prefix-list
2190 :custom-level (1+ level)
2191 :value (nth 0 entry))
2192 (unless (eq (preceding-char) ?\n)
2193 (widget-insert "\n"))))
2194 members)))
2195 (put symbol 'custom-group members)
2196 (message "Creating group magic...")
2197 (mapcar 'custom-magic-reset children)
2198 (message "Creating group state...")
2199 (widget-put widget :children children)
2200 (custom-group-state-update widget)
2201 (message "Creating group... done")))))
2202
2203 (defvar custom-group-menu
2204 '(("Set" custom-group-set
2205 (lambda (widget)
2206 (eq (widget-get widget :custom-state) 'modified)))
2207 ("Save" custom-group-save
2208 (lambda (widget)
2209 (memq (widget-get widget :custom-state) '(modified set))))
2210 ("Reset to Current" custom-group-reset-current
2211 (lambda (widget)
2212 (memq (widget-get widget :custom-state) '(modified))))
2213 ("Reset to Saved" custom-group-reset-saved
2214 (lambda (widget)
2215 (memq (widget-get widget :custom-state) '(modified set))))
2216 ("Reset to standard setting" custom-group-reset-standard
2217 (lambda (widget)
2218 (memq (widget-get widget :custom-state) '(modified set saved)))))
2219 "Alist of actions for the `custom-group' widget.
2220 Each entry has the form (NAME ACTION FILTER) where NAME is the name of
2221 the menu entry, ACTION is the function to call on the widget when the
2222 menu is selected, and FILTER is a predicate which takes a `custom-group'
2223 widget as an argument, and returns non-nil if ACTION is valid on that
2224 widget. If FILTER is nil, ACTION is always valid.")
2225
2226 (defun custom-group-action (widget &optional event)
2227 "Show the menu for `custom-group' WIDGET.
2228 Optional EVENT is the location for the menu."
2229 (if (eq (widget-get widget :custom-state) 'hidden)
2230 (custom-toggle-hide widget)
2231 (let* ((completion-ignore-case t)
2232 (answer (widget-choose (concat "Operation on "
2233 (custom-unlispify-tag-name
2234 (widget-get widget :value)))
2235 (custom-menu-filter custom-group-menu
2236 widget)
2237 event)))
2238 (if answer
2239 (funcall answer widget)))))
2240
2241 (defun custom-group-set (widget)
2242 "Set changes in all modified group members."
2243 (let ((children (widget-get widget :children)))
2244 (mapcar (lambda (child)
2245 (when (eq (widget-get child :custom-state) 'modified)
2246 (widget-apply child :custom-set)))
2247 children )))
2248
2249 (defun custom-group-save (widget)
2250 "Save all modified group members."
2251 (let ((children (widget-get widget :children)))
2252 (mapcar (lambda (child)
2253 (when (memq (widget-get child :custom-state) '(modified set))
2254 (widget-apply child :custom-save)))
2255 children )))
2256
2257 (defun custom-group-reset-current (widget)
2258 "Reset all modified group members."
2259 (let ((children (widget-get widget :children)))
2260 (mapcar (lambda (child)
2261 (when (eq (widget-get child :custom-state) 'modified)
2262 (widget-apply child :custom-reset-current)))
2263 children )))
2264
2265 (defun custom-group-reset-saved (widget)
2266 "Reset all modified or set group members."
2267 (let ((children (widget-get widget :children)))
2268 (mapcar (lambda (child)
2269 (when (memq (widget-get child :custom-state) '(modified set))
2270 (widget-apply child :custom-reset-saved)))
2271 children )))
2272
2273 (defun custom-group-reset-standard (widget)
2274 "Reset all modified, set, or saved group members."
2275 (let ((children (widget-get widget :children)))
2276 (mapcar (lambda (child)
2277 (when (memq (widget-get child :custom-state)
2278 '(modified set saved))
2279 (widget-apply child :custom-reset-standard)))
2280 children )))
2281
2282 (defun custom-group-state-update (widget)
2283 "Update magic."
2284 (unless (eq (widget-get widget :custom-state) 'hidden)
2285 (let* ((children (widget-get widget :children))
2286 (states (mapcar (lambda (child)
2287 (widget-get child :custom-state))
2288 children))
2289 (magics custom-magic-alist)
2290 (found 'standard))
2291 (while magics
2292 (let ((magic (car (car magics))))
2293 (if (and (not (eq magic 'hidden))
2294 (memq magic states))
2295 (setq found magic
2296 magics nil)
2297 (setq magics (cdr magics)))))
2298 (widget-put widget :custom-state found)))
2299 (custom-magic-reset widget))
2300
2301 ;;; The `custom-save-all' Function.
2302
2303 (defcustom custom-file "~/.emacs"
2304 "File used for storing customization information.
2305 If you change this from the default \"~/.emacs\" you need to
2306 explicitly load that file for the settings to take effect."
2307 :type 'file
2308 :group 'customize)
2309
2310 (defun custom-save-delete (symbol)
2311 "Delete the call to SYMBOL form `custom-file'.
2312 Leave point at the location of the call, or after the last expression."
2313 (set-buffer (find-file-noselect custom-file))
2314 (goto-char (point-min))
2315 (catch 'found
2316 (while t
2317 (let ((sexp (condition-case nil
2318 (read (current-buffer))
2319 (end-of-file (throw 'found nil)))))
2320 (when (and (listp sexp)
2321 (eq (car sexp) symbol))
2322 (delete-region (save-excursion
2323 (backward-sexp)
2324 (point))
2325 (point))
2326 (throw 'found nil))))))
2327
2328 (defun custom-save-variables ()
2329 "Save all customized variables in `custom-file'."
2330 (save-excursion
2331 (custom-save-delete 'custom-set-variables)
2332 (let ((standard-output (current-buffer)))
2333 (unless (bolp)
2334 (princ "\n"))
2335 (princ "(custom-set-variables")
2336 (mapatoms (lambda (symbol)
2337 (let ((value (get symbol 'saved-value))
2338 (requests (get symbol 'custom-requests))
2339 (now (not (or (get symbol 'standard-value)
2340 (and (not (boundp symbol))
2341 (not (get symbol 'force-value)))))))
2342 (when value
2343 (princ "\n '(")
2344 (princ symbol)
2345 (princ " ")
2346 (prin1 (car value))
2347 (cond (requests
2348 (if now
2349 (princ " t ")
2350 (princ " nil "))
2351 (prin1 requests)
2352 (princ ")"))
2353 (now
2354 (princ " t)"))
2355 (t
2356 (princ ")")))))))
2357 (princ ")")
2358 (unless (looking-at "\n")
2359 (princ "\n")))))
2360
2361 (defun custom-save-faces ()
2362 "Save all customized faces in `custom-file'."
2363 (save-excursion
2364 (custom-save-delete 'custom-set-faces)
2365 (let ((standard-output (current-buffer)))
2366 (unless (bolp)
2367 (princ "\n"))
2368 (princ "(custom-set-faces")
2369 (let ((value (get 'default 'saved-face)))
2370 ;; The default face must be first, since it affects the others.
2371 (when value
2372 (princ "\n '(default ")
2373 (prin1 value)
2374 (if (or (get 'default 'face-defface-spec)
2375 (and (not (custom-facep 'default))
2376 (not (get 'default 'force-face))))
2377 (princ ")")
2378 (princ " t)"))))
2379 (mapatoms (lambda (symbol)
2380 (let ((value (get symbol 'saved-face)))
2381 (when (and (not (eq symbol 'default))
2382 ;; Don't print default face here.
2383 value)
2384 (princ "\n '(")
2385 (princ symbol)
2386 (princ " ")
2387 (prin1 value)
2388 (if (or (get symbol 'face-defface-spec)
2389 (and (not (custom-facep symbol))
2390 (not (get symbol 'force-face))))
2391 (princ ")")
2392 (princ " t)"))))))
2393 (princ ")")
2394 (unless (looking-at "\n")
2395 (princ "\n")))))
2396
2397 ;;;###autoload
2398 (defun custom-save-customized ()
2399 "Save all user options which have been set in this session."
2400 (interactive)
2401 (mapatoms (lambda (symbol)
2402 (let ((face (get symbol 'customized-face))
2403 (value (get symbol 'customized-value)))
2404 (when face
2405 (put symbol 'saved-face face)
2406 (put symbol 'customized-face nil))
2407 (when value
2408 (put symbol 'saved-value value)
2409 (put symbol 'customized-value nil)))))
2410 ;; We really should update all custom buffers here.
2411 (custom-save-all))
2412
2413 ;;;###autoload
2414 (defun custom-save-all ()
2415 "Save all customizations in `custom-file'."
2416 (custom-save-variables)
2417 (custom-save-faces)
2418 (save-excursion
2419 (set-buffer (find-file-noselect custom-file))
2420 (save-buffer)))
2421
2422 ;;; The Customize Menu.
2423
2424 ;;; Menu support
2425
2426 (unless (string-match "XEmacs" emacs-version)
2427 (defconst custom-help-menu '("Customize"
2428 ["Update menu..." custom-menu-update t]
2429 ["Group..." customize-group t]
2430 ["Variable..." customize-variable t]
2431 ["Face..." customize-face t]
2432 ["Saved..." customize-saved t]
2433 ["Set..." customize-customized t]
2434 ["Apropos..." customize-apropos t])
2435 ;; This menu should be identical to the one defined in `menu-bar.el'.
2436 "Customize menu")
2437
2438 (defun custom-menu-reset ()
2439 "Reset customize menu."
2440 (remove-hook 'custom-define-hook 'custom-menu-reset)
2441 (define-key global-map [menu-bar help-menu customize-menu]
2442 (cons (car custom-help-menu)
2443 (easy-menu-create-keymaps (car custom-help-menu)
2444 (cdr custom-help-menu)))))
2445
2446 (defun custom-menu-update (event)
2447 "Update customize menu."
2448 (interactive "e")
2449 (add-hook 'custom-define-hook 'custom-menu-reset)
2450 (let* ((emacs (widget-apply '(custom-group) :custom-menu 'emacs))
2451 (menu `(,(car custom-help-menu)
2452 ,emacs
2453 ,@(cdr (cdr custom-help-menu)))))
2454 (let ((map (easy-menu-create-keymaps (car menu) (cdr menu))))
2455 (define-key global-map [menu-bar help-menu customize-menu]
2456 (cons (car menu) map))))))
2457
2458 (defcustom custom-menu-nesting 2
2459 "Maximum nesting in custom menus."
2460 :type 'integer
2461 :group 'customize)
2462
2463 (defun custom-face-menu-create (widget symbol)
2464 "Ignoring WIDGET, create a menu entry for customization face SYMBOL."
2465 (vector (custom-unlispify-menu-entry symbol)
2466 `(customize-face ',symbol)
2467 t))
2468
2469 (defun custom-variable-menu-create (widget symbol)
2470 "Ignoring WIDGET, create a menu entry for customization variable SYMBOL."
2471 (let ((type (get symbol 'custom-type)))
2472 (unless (listp type)
2473 (setq type (list type)))
2474 (if (and type (widget-get type :custom-menu))
2475 (widget-apply type :custom-menu symbol)
2476 (vector (custom-unlispify-menu-entry symbol)
2477 `(customize-variable ',symbol)
2478 t))))
2479
2480 ;; Add checkboxes to boolean variable entries.
2481 (widget-put (get 'boolean 'widget-type)
2482 :custom-menu (lambda (widget symbol)
2483 (vector (custom-unlispify-menu-entry symbol)
2484 `(customize-variable ',symbol)
2485 ':style 'toggle
2486 ':selected symbol)))
2487
2488 (if (string-match "XEmacs" emacs-version)
2489 ;; XEmacs can create menus dynamically.
2490 (defun custom-group-menu-create (widget symbol)
2491 "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
2492 `( ,(custom-unlispify-menu-entry symbol t)
2493 :filter (lambda (&rest junk)
2494 (cdr (custom-menu-create ',symbol)))))
2495 ;; But emacs can't.
2496 (defun custom-group-menu-create (widget symbol)
2497 "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
2498 ;; Limit the nesting.
2499 (let ((custom-menu-nesting (1- custom-menu-nesting)))
2500 (custom-menu-create symbol))))
2501
2502 ;;;###autoload
2503 (defun custom-menu-create (symbol)
2504 "Create menu for customization group SYMBOL.
2505 The menu is in a format applicable to `easy-menu-define'."
2506 (let* ((item (vector (custom-unlispify-menu-entry symbol)
2507 `(customize-group ',symbol)
2508 t)))
2509 (if (and (or (not (boundp 'custom-menu-nesting))
2510 (>= custom-menu-nesting 0))
2511 (< (length (get symbol 'custom-group)) widget-menu-max-size))
2512 (let ((custom-prefix-list (custom-prefix-add symbol
2513 custom-prefix-list))
2514 (members (sort (get symbol 'custom-group)
2515 custom-menu-sort-predicate)))
2516 (put symbol 'custom-group members)
2517 (custom-load-symbol symbol)
2518 `(,(custom-unlispify-menu-entry symbol t)
2519 ,item
2520 "--"
2521 ,@(mapcar (lambda (entry)
2522 (widget-apply (if (listp (nth 1 entry))
2523 (nth 1 entry)
2524 (list (nth 1 entry)))
2525 :custom-menu (nth 0 entry)))
2526 members)))
2527 item)))
2528
2529 ;;;###autoload
2530 (defun customize-menu-create (symbol &optional name)
2531 "Return a customize menu for customization group SYMBOL.
2532 If optional NAME is given, use that as the name of the menu.
2533 Otherwise the menu will be named `Customize'.
2534 The format is suitable for use with `easy-menu-define'."
2535 (unless name
2536 (setq name "Customize"))
2537 (if (string-match "XEmacs" emacs-version)
2538 ;; We can delay it under XEmacs.
2539 `(,name
2540 :filter (lambda (&rest junk)
2541 (cdr (custom-menu-create ',symbol))))
2542 ;; But we must create it now under Emacs.
2543 (cons name (cdr (custom-menu-create symbol)))))
2544
2545 ;;; The Custom Mode.
2546
2547 (defvar custom-mode-map nil
2548 "Keymap for `custom-mode'.")
2549
2550 (unless custom-mode-map
2551 (setq custom-mode-map (make-sparse-keymap))
2552 (set-keymap-parent custom-mode-map widget-keymap)
2553 (define-key custom-mode-map "q" 'bury-buffer))
2554
2555 (easy-menu-define custom-mode-customize-menu
2556 custom-mode-map
2557 "Menu used to customize customization buffers."
2558 (customize-menu-create 'customize))
2559
2560 (easy-menu-define custom-mode-menu
2561 custom-mode-map
2562 "Menu used in customization buffers."
2563 `("Custom"
2564 ["Set" custom-set t]
2565 ["Save" custom-save t]
2566 ["Reset to Current" custom-reset-current t]
2567 ["Reset to Saved" custom-reset-saved t]
2568 ["Reset to Standard Settings" custom-reset-standard t]
2569 ["Info" (Info-goto-node "(custom)The Customization Buffer") t]))
2570
2571 (defcustom custom-mode-hook nil
2572 "Hook called when entering custom-mode."
2573 :type 'hook
2574 :group 'customize)
2575
2576 (defun custom-mode ()
2577 "Major mode for editing customization buffers.
2578
2579 The following commands are available:
2580
2581 Move to next button or editable field. \\[widget-forward]
2582 Move to previous button or editable field. \\[widget-backward]
2583 Invoke button under the mouse pointer. \\[widget-button-click]
2584 Invoke button under point. \\[widget-button-press]
2585 Set all modifications. \\[custom-set]
2586 Make all modifications default. \\[custom-save]
2587 Reset all modified options. \\[custom-reset-current]
2588 Reset all modified or set options. \\[custom-reset-saved]
2589 Reset all options. \\[custom-reset-standard]
2590
2591 Entry to this mode calls the value of `custom-mode-hook'
2592 if that value is non-nil."
2593 (kill-all-local-variables)
2594 (setq major-mode 'custom-mode
2595 mode-name "Custom")
2596 (use-local-map custom-mode-map)
2597 (easy-menu-add custom-mode-customize-menu)
2598 (easy-menu-add custom-mode-menu)
2599 (make-local-variable 'custom-options)
2600 (run-hooks 'custom-mode-hook))
2601
2602 ;;; The End.
2603
2604 (provide 'cus-edit)
2605
2606 ;; cus-edit.el ends here