*** empty log message ***
[bpt/emacs.git] / lisp / cus-edit.el
CommitLineData
e8af40ee 1;;; cus-edit.el --- tools for customizing Emacs and Lisp packages
d543e20b 2;;
414fd702 3;; Copyright (C) 1996, 1997, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
d543e20b
PA
4;;
5;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6;; Keywords: help, faces
d543e20b 7
f2b98a56
RS
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs; see the file COPYING. If not, write to the
22;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23;; Boston, MA 02111-1307, USA.
24
d543e20b
PA
25;;; Commentary:
26;;
6d528fc5
PA
27;; This file implements the code to create and edit customize buffers.
28;;
d543e20b
PA
29;; See `custom.el'.
30
ab678382
RS
31;; No commands should have names starting with `custom-' because
32;; that interferes with completion. Use `customize-' for commands
33;; that the user will run with M-x, and `Custom-' for interactive commands.
34
d543e20b
PA
35;;; Code:
36
37(require 'cus-face)
38(require 'wid-edit)
164cfaeb 39(eval-when-compile
164cfaeb 40 (defvar custom-versions-load-alist)) ; from cus-load
6d528fc5
PA
41
42(condition-case nil
43 (require 'cus-load)
44 (error nil))
d543e20b 45
9097aeb7
PA
46(condition-case nil
47 (require 'cus-start)
48 (error nil))
49
bd042c03 50(put 'custom-define-hook 'custom-type 'hook)
25ac13b5 51(put 'custom-define-hook 'standard-value '(nil))
bd042c03
PA
52(custom-add-to-group 'customize 'custom-define-hook 'custom-variable)
53
d543e20b
PA
54;;; Customization Groups.
55
56(defgroup emacs nil
57 "Customization of the One True Editor."
58 :link '(custom-manual "(emacs)Top"))
59
60;; Most of these groups are stolen from `finder.el',
61(defgroup editing nil
62 "Basic text editing facilities."
63 :group 'emacs)
64
65(defgroup abbrev nil
66 "Abbreviation handling, typing shortcuts, macros."
67 :tag "Abbreviations"
68 :group 'editing)
69
70(defgroup matching nil
71 "Various sorts of searching and matching."
72 :group 'editing)
73
74(defgroup emulations nil
75 "Emulations of other editors."
76 :group 'editing)
77
78(defgroup mouse nil
79 "Mouse support."
80 :group 'editing)
81
82(defgroup outlines nil
83 "Support for hierarchical outlining."
84 :group 'editing)
85
86(defgroup external nil
87 "Interfacing to external utilities."
88 :group 'emacs)
89
90(defgroup bib nil
91 "Code related to the `bib' bibliography processor."
92 :tag "Bibliography"
93 :group 'external)
94
95(defgroup processes nil
96 "Process, subshell, compilation, and job control support."
97 :group 'external
98 :group 'development)
99
4599e8cd
RS
100(defgroup convenience nil
101 "Convenience features for faster editing."
102 :group 'emacs)
103
d543e20b
PA
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
86bd10bc
PA
200(defgroup x nil
201 "The X Window system."
202 :group 'environment)
203
d543e20b
PA
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
482e54f3
RS
212(defgroup files nil
213 "Support editing files."
214 :group 'emacs)
215
d543e20b
PA
216(defgroup wp nil
217 "Word processing."
218 :group 'emacs)
219
220(defgroup tex nil
221 "Code related to the TeX formatter."
222 :group 'wp)
223
224(defgroup faces nil
225 "Support for multiple fonts."
226 :group 'emacs)
227
228(defgroup hypermedia nil
229 "Support for links between text or other media types."
230 :group 'emacs)
231
232(defgroup help nil
233 "Support for on-line help systems."
234 :group 'emacs)
235
d3d4df42
DL
236(defgroup multimedia nil
237 "Non-textual support, specifically images and sound."
238 :group 'emacs)
239
d543e20b
PA
240(defgroup local nil
241 "Code local to your site."
242 :group 'emacs)
243
244(defgroup customize '((widgets custom-group))
245 "Customization of the Customization support."
2a1c4b90 246 :link '(custom-manual "(elisp)Customization")
d3d4df42 247 :link '(url-link :tag "(Old?) Development Page"
d543e20b
PA
248 "http://www.dina.kvl.dk/~abraham/custom/")
249 :prefix "custom-"
bd042c03
PA
250 :group 'help)
251
252(defgroup custom-faces nil
253 "Faces used by customize."
254 :group 'customize
d543e20b
PA
255 :group 'faces)
256
da5ec617
PA
257(defgroup custom-browse nil
258 "Control customize browser."
259 :prefix "custom-"
260 :group 'customize)
261
6aaedd12 262(defgroup custom-buffer nil
da5ec617 263 "Control customize buffers."
6aaedd12
PA
264 :prefix "custom-"
265 :group 'customize)
266
267(defgroup custom-menu nil
da5ec617 268 "Control customize menus."
6aaedd12
PA
269 :prefix "custom-"
270 :group 'customize)
271
bd042c03
PA
272(defgroup abbrev-mode nil
273 "Word abbreviations mode."
274 :group 'abbrev)
275
276(defgroup alloc nil
277 "Storage allocation and gc for GNU Emacs Lisp interpreter."
278 :tag "Storage Allocation"
279 :group 'internal)
280
281(defgroup undo nil
282 "Undoing changes in buffers."
283 :group 'editing)
284
285(defgroup modeline nil
286 "Content of the modeline."
287 :group 'environment)
288
289(defgroup fill nil
290 "Indenting and filling text."
291 :group 'editing)
292
293(defgroup editing-basics nil
294 "Most basic editing facilities."
295 :group 'editing)
296
297(defgroup display nil
298 "How characters are displayed in buffers."
299 :group 'environment)
300
301(defgroup execute nil
302 "Executing external commands."
303 :group 'processes)
304
305(defgroup installation nil
306 "The Emacs installation."
307 :group 'environment)
308
309(defgroup dired nil
310 "Directory editing."
311 :group 'environment)
312
313(defgroup limits nil
314 "Internal Emacs limits."
315 :group 'internal)
316
317(defgroup debug nil
318 "Debugging Emacs itself."
319 :group 'development)
320
321(defgroup minibuffer nil
322 "Controling the behaviour of the minibuffer."
323 :group 'environment)
324
325(defgroup keyboard nil
326 "Input from the keyboard."
327 :group 'environment)
328
329(defgroup mouse nil
330 "Input from the mouse."
331 :group 'environment)
332
333(defgroup menu nil
334 "Input from the menus."
335 :group 'environment)
336
337(defgroup auto-save nil
338 "Preventing accidential loss of data."
482e54f3 339 :group 'files)
bd042c03
PA
340
341(defgroup processes-basics nil
342 "Basic stuff dealing with processes."
343 :group 'processes)
344
25ac13b5
PA
345(defgroup mule nil
346 "MULE Emacs internationalization."
70bc91bc 347 :group 'i18n)
25ac13b5 348
bd042c03
PA
349(defgroup windows nil
350 "Windows within a frame."
86bd10bc 351 :group 'environment)
bd042c03 352
d543e20b
PA
353;;; Utilities.
354
355(defun custom-quote (sexp)
356 "Quote SEXP iff it is not self quoting."
357 (if (or (memq sexp '(t nil))
2365594b 358 (keywordp sexp)
d543e20b
PA
359 (and (listp sexp)
360 (memq (car sexp) '(lambda)))
361 (stringp sexp)
362 (numberp sexp)
d3d4df42
DL
363 (vectorp sexp)
364;;; (and (fboundp 'characterp)
365;;; (characterp sexp))
366 )
d543e20b
PA
367 sexp
368 (list 'quote sexp)))
369
370(defun custom-split-regexp-maybe (regexp)
371 "If REGEXP is a string, split it to a list at `\\|'.
d3d4df42 372You can get the original back with from the result with:
d543e20b
PA
373 (mapconcat 'identity result \"\\|\")
374
375IF REGEXP is not a string, return it unchanged."
376 (if (stringp regexp)
377 (let ((start 0)
378 all)
379 (while (string-match "\\\\|" regexp start)
380 (setq all (cons (substring regexp start (match-beginning 0)) all)
381 start (match-end 0)))
382 (nreverse (cons (substring regexp start) all)))
383 regexp))
384
bd042c03 385(defun custom-variable-prompt ()
bd042c03
PA
386 "Prompt for a variable, defaulting to the variable at point.
387Return a list suitable for use in `interactive'."
388 (let ((v (variable-at-point))
389 (enable-recursive-minibuffers t)
390 val)
d3d4df42 391 (setq val (completing-read
5b5cdd97 392 (if (symbolp v)
64dde95b 393 (format "Customize option: (default %s) " v)
bd042c03 394 "Customize variable: ")
6d528fc5
PA
395 obarray (lambda (symbol)
396 (and (boundp symbol)
397 (or (get symbol 'custom-type)
fa08de96 398 (get symbol 'custom-loads)
01651f07 399 (get symbol 'standard-value)))) t))
bd042c03 400 (list (if (equal val "")
5b5cdd97
RS
401 (if (symbolp v) v nil)
402 (intern val)))))
bd042c03 403
6d528fc5
PA
404(defun custom-menu-filter (menu widget)
405 "Convert MENU to the form used by `widget-choose'.
406MENU should be in the same format as `custom-variable-menu'.
407WIDGET is the widget to apply the filter entries of MENU on."
408 (let ((result nil)
409 current name action filter)
d3d4df42 410 (while menu
6d528fc5
PA
411 (setq current (car menu)
412 name (nth 0 current)
413 action (nth 1 current)
414 filter (nth 2 current)
415 menu (cdr menu))
416 (if (or (null filter) (funcall filter widget))
417 (push (cons name action) result)
418 (push name result)))
419 (nreverse result)))
420
bd042c03
PA
421;;; Unlispify.
422
d543e20b 423(defvar custom-prefix-list nil
2365594b 424 "List of prefixes that should be ignored by `custom-unlispify'.")
d543e20b
PA
425
426(defcustom custom-unlispify-menu-entries t
427 "Display menu entries as words instead of symbols if non nil."
6aaedd12 428 :group 'custom-menu
d543e20b
PA
429 :type 'boolean)
430
cda987f4
RS
431(defcustom custom-unlispify-remove-prefixes nil
432 "Non-nil means remove group prefixes from option names in buffer."
433 :group 'custom-menu
42b4edc5 434 :group 'custom-buffer
cda987f4
RS
435 :type 'boolean)
436
d543e20b 437(defun custom-unlispify-menu-entry (symbol &optional no-suffix)
2365594b 438 "Convert SYMBOL into a menu entry."
d543e20b
PA
439 (cond ((not custom-unlispify-menu-entries)
440 (symbol-name symbol))
441 ((get symbol 'custom-tag)
442 (if no-suffix
443 (get symbol 'custom-tag)
444 (concat (get symbol 'custom-tag) "...")))
445 (t
446 (save-excursion
447 (set-buffer (get-buffer-create " *Custom-Work*"))
448 (erase-buffer)
449 (princ symbol (current-buffer))
450 (goto-char (point-min))
bd042c03
PA
451 (when (and (eq (get symbol 'custom-type) 'boolean)
452 (re-search-forward "-p\\'" nil t))
453 (replace-match "" t t)
454 (goto-char (point-min)))
cda987f4
RS
455 (if custom-unlispify-remove-prefixes
456 (let ((prefixes custom-prefix-list)
457 prefix)
458 (while prefixes
459 (setq prefix (car prefixes))
460 (if (search-forward prefix (+ (point) (length prefix)) t)
d3d4df42 461 (progn
cda987f4
RS
462 (setq prefixes nil)
463 (delete-region (point-min) (point)))
464 (setq prefixes (cdr prefixes))))))
d543e20b
PA
465 (subst-char-in-region (point-min) (point-max) ?- ?\ t)
466 (capitalize-region (point-min) (point-max))
d3d4df42 467 (unless no-suffix
d543e20b
PA
468 (goto-char (point-max))
469 (insert "..."))
470 (buffer-string)))))
471
472(defcustom custom-unlispify-tag-names t
473 "Display tag names as words instead of symbols if non nil."
6aaedd12 474 :group 'custom-buffer
d543e20b
PA
475 :type 'boolean)
476
477(defun custom-unlispify-tag-name (symbol)
2365594b 478 "Convert SYMBOL into a menu entry."
d543e20b
PA
479 (let ((custom-unlispify-menu-entries custom-unlispify-tag-names))
480 (custom-unlispify-menu-entry symbol t)))
481
482(defun custom-prefix-add (symbol prefixes)
2365594b 483 "Add SYMBOL to list of ignored PREFIXES."
d543e20b
PA
484 (cons (or (get symbol 'custom-prefix)
485 (concat (symbol-name symbol) "-"))
486 prefixes))
487
bd042c03
PA
488;;; Guess.
489
490(defcustom custom-guess-name-alist
491 '(("-p\\'" boolean)
492 ("-hook\\'" hook)
493 ("-face\\'" face)
494 ("-file\\'" file)
495 ("-function\\'" function)
496 ("-functions\\'" (repeat function))
497 ("-list\\'" (repeat sexp))
498 ("-alist\\'" (repeat (cons sexp sexp))))
499 "Alist of (MATCH TYPE).
500
d3d4df42 501MATCH should be a regexp matching the name of a symbol, and TYPE should
bd042c03
PA
502be a widget suitable for editing the value of that symbol. The TYPE
503of the first entry where MATCH matches the name of the symbol will be
d3d4df42 504used.
bd042c03
PA
505
506This is used for guessing the type of variables not declared with
507customize."
508 :type '(repeat (group (regexp :tag "Match") (sexp :tag "Type")))
d543e20b
PA
509 :group 'customize)
510
bd042c03
PA
511(defcustom custom-guess-doc-alist
512 '(("\\`\\*?Non-nil " boolean))
513 "Alist of (MATCH TYPE).
d543e20b 514
bd042c03
PA
515MATCH should be a regexp matching a documentation string, and TYPE
516should be a widget suitable for editing the value of a variable with
517that documentation string. The TYPE of the first entry where MATCH
518matches the name of the symbol will be used.
d543e20b 519
bd042c03
PA
520This is used for guessing the type of variables not declared with
521customize."
522 :type '(repeat (group (regexp :tag "Match") (sexp :tag "Type")))
523 :group 'customize)
d543e20b 524
bd042c03
PA
525(defun custom-guess-type (symbol)
526 "Guess a widget suitable for editing the value of SYMBOL.
d3d4df42 527This is done by matching SYMBOL with `custom-guess-name-alist' and
bd042c03
PA
528if that fails, the doc string with `custom-guess-doc-alist'."
529 (let ((name (symbol-name symbol))
530 (names custom-guess-name-alist)
531 current found)
532 (while names
533 (setq current (car names)
534 names (cdr names))
535 (when (string-match (nth 0 current) name)
536 (setq found (nth 1 current)
537 names nil)))
538 (unless found
539 (let ((doc (documentation-property symbol 'variable-documentation))
540 (docs custom-guess-doc-alist))
d3d4df42 541 (when doc
bd042c03
PA
542 (while docs
543 (setq current (car docs)
544 docs (cdr docs))
545 (when (string-match (nth 0 current) doc)
546 (setq found (nth 1 current)
547 docs nil))))))
548 found))
d543e20b 549
25ac13b5
PA
550;;; Sorting.
551
da5ec617
PA
552(defcustom custom-browse-sort-alphabetically nil
553 "If non-nil, sort members of each customization group alphabetically."
554 :type 'boolean
555 :group 'custom-browse)
556
557(defcustom custom-browse-order-groups nil
558 "If non-nil, order group members within each customization group.
559If `first', order groups before non-groups.
560If `last', order groups after non-groups."
561 :type '(choice (const first)
562 (const last)
563 (const :tag "none" nil))
564 :group 'custom-browse)
565
c953515e
PA
566(defcustom custom-browse-only-groups nil
567 "If non-nil, show group members only within each customization group."
568 :type 'boolean
569 :group 'custom-browse)
570
944c91b6 571(defcustom custom-buffer-sort-alphabetically nil
da5ec617 572 "If non-nil, sort members of each customization group alphabetically."
944c91b6 573 :type 'boolean
6aaedd12 574 :group 'custom-buffer)
25ac13b5 575
da5ec617
PA
576(defcustom custom-buffer-order-groups 'last
577 "If non-nil, order group members within each customization group.
578If `first', order groups before non-groups.
579If `last', order groups after non-groups."
580 :type '(choice (const first)
581 (const last)
582 (const :tag "none" nil))
6aaedd12 583 :group 'custom-buffer)
25ac13b5 584
944c91b6 585(defcustom custom-menu-sort-alphabetically nil
da5ec617 586 "If non-nil, sort members of each customization group alphabetically."
944c91b6 587 :type 'boolean
6aaedd12 588 :group 'custom-menu)
25ac13b5 589
da5ec617
PA
590(defcustom custom-menu-order-groups 'first
591 "If non-nil, order group members within each customization group.
592If `first', order groups before non-groups.
593If `last', order groups after non-groups."
594 :type '(choice (const first)
595 (const last)
596 (const :tag "none" nil))
6aaedd12
PA
597 :group 'custom-menu)
598
b4854a23
KH
599;;;###autoload (add-hook 'same-window-regexps "\\`\\*Customiz.*\\*\\'")
600
da5ec617
PA
601(defun custom-sort-items (items sort-alphabetically order-groups)
602 "Return a sorted copy of ITEMS.
603ITEMS should be a `custom-group' property.
604If SORT-ALPHABETICALLY non-nil, sort alphabetically.
605If ORDER-GROUPS is `first' order groups before non-groups, if `last' order
606groups after non-groups, if nil do not order groups at all."
607 (sort (copy-sequence items)
608 (lambda (a b)
609 (let ((typea (nth 1 a)) (typeb (nth 1 b))
359476e0 610 (namea (nth 0 a)) (nameb (nth 0 b)))
da5ec617
PA
611 (cond ((not order-groups)
612 ;; Since we don't care about A and B order, maybe sort.
613 (when sort-alphabetically
614 (string-lessp namea nameb)))
615 ((eq typea 'custom-group)
616 ;; If B is also a group, maybe sort. Otherwise, order A and B.
617 (if (eq typeb 'custom-group)
618 (when sort-alphabetically
619 (string-lessp namea nameb))
620 (eq order-groups 'first)))
621 ((eq typeb 'custom-group)
622 ;; Since A cannot be a group, order A and B.
623 (eq order-groups 'last))
624 (sort-alphabetically
625 ;; Since A and B cannot be groups, sort.
626 (string-lessp namea nameb)))))))
25ac13b5 627
d543e20b
PA
628;;; Custom Mode Commands.
629
bd042c03
PA
630(defvar custom-options nil
631 "Customization widgets in the current buffer.")
632
ab678382 633(defun Custom-set ()
d543e20b
PA
634 "Set changes in all modified options."
635 (interactive)
636 (let ((children custom-options))
fadbdfea
DL
637 (mapc (lambda (child)
638 (when (eq (widget-get child :custom-state) 'modified)
639 (widget-apply child :custom-set)))
d543e20b
PA
640 children)))
641
ab678382 642(defun Custom-save ()
d543e20b
PA
643 "Set all modified group members and save them."
644 (interactive)
645 (let ((children custom-options))
fadbdfea
DL
646 (mapc (lambda (child)
647 (when (memq (widget-get child :custom-state)
648 '(modified set changed rogue))
649 (widget-apply child :custom-save)))
d543e20b
PA
650 children))
651 (custom-save-all))
652
d3d4df42 653(defvar custom-reset-menu
ab678382
RS
654 '(("Current" . Custom-reset-current)
655 ("Saved" . Custom-reset-saved)
19d63704 656 ("Erase Customization (use standard settings)" . Custom-reset-standard))
d543e20b
PA
657 "Alist of actions for the `Reset' button.
658The key is a string containing the name of the action, the value is a
2365594b 659Lisp function taking the widget as an element which will be called
d543e20b
PA
660when the action is chosen.")
661
662(defun custom-reset (event)
663 "Select item from reset menu."
664 (let* ((completion-ignore-case t)
665 (answer (widget-choose "Reset to"
666 custom-reset-menu
667 event)))
668 (if answer
669 (funcall answer))))
670
ab678382 671(defun Custom-reset-current (&rest ignore)
d543e20b
PA
672 "Reset all modified group members to their current value."
673 (interactive)
674 (let ((children custom-options))
fadbdfea
DL
675 (mapc (lambda (widget)
676 (and (default-boundp (widget-value widget))
677 (if (memq (widget-get widget :custom-state)
678 '(modified changed))
679 (widget-apply widget :custom-reset-current))))
d543e20b
PA
680 children)))
681
ab678382 682(defun Custom-reset-saved (&rest ignore)
d543e20b
PA
683 "Reset all modified or set group members to their saved value."
684 (interactive)
685 (let ((children custom-options))
fadbdfea
DL
686 (mapc (lambda (widget)
687 (and (get (widget-value widget) 'saved-value)
688 (if (memq (widget-get widget :custom-state)
689 '(modified set changed rogue))
690 (widget-apply widget :custom-reset-saved))))
d543e20b
PA
691 children)))
692
ab678382 693(defun Custom-reset-standard (&rest ignore)
19d63704
RS
694 "Erase all customization (either current or saved) for the group members.
695The immediate result is to restore them to their standard settings.
696This operation eliminates any saved settings for the group members,
697making them as if they had never been customized at all."
d543e20b
PA
698 (interactive)
699 (let ((children custom-options))
fadbdfea
DL
700 (mapc (lambda (widget)
701 (and (get (widget-value widget) 'standard-value)
702 (if (memq (widget-get widget :custom-state)
703 '(modified set changed saved rogue))
704 (widget-apply widget :custom-reset-standard))))
d543e20b
PA
705 children)))
706
707;;; The Customize Commands
708
d3d4df42 709(defun custom-prompt-variable (prompt-var prompt-val &optional comment)
6d528fc5
PA
710 "Prompt for a variable and a value and return them as a list.
711PROMPT-VAR is the prompt for the variable, and PROMPT-VAL is the
712prompt for the value. The %s escape in PROMPT-VAL is replaced with
713the name of the variable.
714
715If the variable has a `variable-interactive' property, that is used as if
716it were the arg to `interactive' (which see) to interactively read the value.
717
718If the variable has a `custom-type' property, it must be a widget and the
d3d4df42
DL
719`:prompt-value' property of that widget will be used for reading the value.
720
721If optional COMMENT argument is non nil, also prompt for a comment and return
722it as the third element in the list."
6d528fc5 723 (let* ((var (read-variable prompt-var))
d3d4df42
DL
724 (minibuffer-help-form '(describe-variable var))
725 (val
6d528fc5
PA
726 (let ((prop (get var 'variable-interactive))
727 (type (get var 'custom-type))
728 (prompt (format prompt-val var)))
729 (unless (listp type)
730 (setq type (list type)))
731 (cond (prop
732 ;; Use VAR's `variable-interactive' property
733 ;; as an interactive spec for prompting.
734 (call-interactively (list 'lambda '(arg)
735 (list 'interactive prop)
736 'arg)))
737 (type
738 (widget-prompt-value type
739 prompt
740 (if (boundp var)
741 (symbol-value var))
742 (not (boundp var))))
743 (t
d3d4df42
DL
744 (eval-minibuffer prompt))))))
745 (if comment
746 (list var val
747 (read-string "Comment: " (get var 'variable-comment)))
748 (list var val))))
6d528fc5
PA
749
750;;;###autoload
d3d4df42 751(defun customize-set-value (var val &optional comment)
4f9b9060 752 "Set VARIABLE to VALUE, and return VALUE. VALUE is a Lisp object.
6d528fc5
PA
753
754If VARIABLE has a `variable-interactive' property, that is used as if
755it were the arg to `interactive' (which see) to interactively read the value.
756
757If VARIABLE has a `custom-type' property, it must be a widget and the
d3d4df42
DL
758`:prompt-value' property of that widget will be used for reading the value.
759
760If given a prefix (or a COMMENT argument), also prompt for a comment."
6d528fc5 761 (interactive (custom-prompt-variable "Set variable: "
d3d4df42
DL
762 "Set %s to value: "
763 current-prefix-arg))
6d528fc5 764
d3d4df42
DL
765 (cond ((string= comment "")
766 (put var 'variable-comment nil))
767 (comment
4f9b9060
PA
768 (put var 'variable-comment comment)))
769 (set var val))
6d528fc5
PA
770
771;;;###autoload
2365594b 772(defun customize-set-variable (variable value &optional comment)
4f9b9060
PA
773 "Set the default for VARIABLE to VALUE, and return VALUE.
774VALUE is a Lisp object.
6d528fc5
PA
775
776If VARIABLE has a `custom-set' property, that is used for setting
777VARIABLE, otherwise `set-default' is used.
778
779The `customized-value' property of the VARIABLE will be set to a list
780with a quoted VALUE as its sole list member.
781
782If VARIABLE has a `variable-interactive' property, that is used as if
783it were the arg to `interactive' (which see) to interactively read the value.
784
785If VARIABLE has a `custom-type' property, it must be a widget and the
d3d4df42
DL
786`:prompt-value' property of that widget will be used for reading the value.
787
788If given a prefix (or a COMMENT argument), also prompt for a comment."
6d528fc5 789 (interactive (custom-prompt-variable "Set variable: "
d3d4df42
DL
790 "Set customized value for %s to: "
791 current-prefix-arg))
4f9b9060 792 (custom-load-symbol variable)
2365594b
DL
793 (funcall (or (get variable 'custom-set) 'set-default) variable value)
794 (put variable 'customized-value (list (custom-quote value)))
d3d4df42 795 (cond ((string= comment "")
2365594b
DL
796 (put variable 'variable-comment nil)
797 (put variable 'customized-variable-comment nil))
d3d4df42 798 (comment
2365594b 799 (put variable 'variable-comment comment)
4f9b9060
PA
800 (put variable 'customized-variable-comment comment)))
801 value)
6d528fc5 802
4ee1cf9f 803;;;###autoload
2365594b 804(defun customize-save-variable (var value &optional comment)
4ee1cf9f 805 "Set the default for VARIABLE to VALUE, and save it for future sessions.
4f9b9060
PA
806Return VALUE.
807
4ee1cf9f
PA
808If VARIABLE has a `custom-set' property, that is used for setting
809VARIABLE, otherwise `set-default' is used.
810
811The `customized-value' property of the VARIABLE will be set to a list
812with a quoted VALUE as its sole list member.
813
814If VARIABLE has a `variable-interactive' property, that is used as if
815it were the arg to `interactive' (which see) to interactively read the value.
816
817If VARIABLE has a `custom-type' property, it must be a widget and the
d3d4df42
DL
818`:prompt-value' property of that widget will be used for reading the value.
819
820If given a prefix (or a COMMENT argument), also prompt for a comment."
901cd78b 821 (interactive (custom-prompt-variable "Set and save variable: "
d3d4df42
DL
822 "Set and save value for %s as: "
823 current-prefix-arg))
2365594b
DL
824 (funcall (or (get var 'custom-set) 'set-default) var value)
825 (put var 'saved-value (list (custom-quote value)))
d3d4df42
DL
826 (cond ((string= comment "")
827 (put var 'variable-comment nil)
828 (put var 'saved-variable-comment nil))
829 (comment
830 (put var 'variable-comment comment)
831 (put var 'saved-variable-comment comment)))
4f9b9060
PA
832 (custom-save-all)
833 value)
4ee1cf9f 834
d543e20b 835;;;###autoload
5dd0cad0
RS
836(defun customize ()
837 "Select a customization buffer which you can use to set user options.
838User options are structured into \"groups\".
839Initially the top-level group `Emacs' and its immediate subgroups
840are shown; the contents of those subgroups are initially hidden."
841 (interactive)
dc2e979f 842 (customize-group 'emacs))
5dd0cad0
RS
843
844;;;###autoload
845(defun customize-group (group)
846 "Customize GROUP, which must be a customization group."
07e694f8
RS
847 (interactive (list (let ((completion-ignore-case t))
848 (completing-read "Customize group: (default emacs) "
d3d4df42 849 obarray
07e694f8 850 (lambda (symbol)
5aa3f181
RS
851 (or (get symbol 'custom-loads)
852 (get symbol 'custom-group)))
07e694f8 853 t))))
5dd0cad0
RS
854 (when (stringp group)
855 (if (string-equal "" group)
856 (setq group 'emacs)
857 (setq group (intern group))))
5aa3f181
RS
858 (or (get group 'custom-group)
859 (custom-load-symbol group))
241d3080
RS
860 (let ((name (format "*Customize Group: %s*"
861 (custom-unlispify-tag-name group))))
862 (if (get-buffer name)
b4854a23 863 (pop-to-buffer name)
241d3080 864 (custom-buffer-create (list (list group 'custom-group))
3aec85bf
RS
865 name
866 (concat " for group "
867 (custom-unlispify-tag-name group))))))
d543e20b 868
6d528fc5 869;;;###autoload
fd88fe73
RS
870(defun customize-group-other-window (group)
871 "Customize GROUP, which must be a customization group."
872 (interactive (list (let ((completion-ignore-case t))
873 (completing-read "Customize group: (default emacs) "
d3d4df42 874 obarray
fd88fe73
RS
875 (lambda (symbol)
876 (or (get symbol 'custom-loads)
877 (get symbol 'custom-group)))
878 t))))
879 (when (stringp group)
880 (if (string-equal "" group)
881 (setq group 'emacs)
882 (setq group (intern group))))
883 (or (get group 'custom-group)
884 (custom-load-symbol group))
885 (let ((name (format "*Customize Group: %s*"
886 (custom-unlispify-tag-name group))))
887 (if (get-buffer name)
888 (let ((window (selected-window)))
09da6520 889 (pop-to-buffer name)
fd88fe73
RS
890 (select-window window))
891 (custom-buffer-create-other-window
892 (list (list group 'custom-group))
893 name
894 (concat " for group "
895 (custom-unlispify-tag-name group))))))
6d528fc5 896
9097aeb7
PA
897;;;###autoload
898(defalias 'customize-variable 'customize-option)
38d58078 899
d543e20b 900;;;###autoload
38d58078
RS
901(defun customize-option (symbol)
902 "Customize SYMBOL, which must be a user option variable."
bd042c03 903 (interactive (custom-variable-prompt))
41b3e67c
KH
904 ;; If we don't have SYMBOL's real definition loaded,
905 ;; try to load it.
906 (unless (get symbol 'custom-type)
907 (let ((loaddefs-file (locate-library "loaddefs.el" t))
908 file)
909 ;; See if it is autoloaded from some library.
910 (when loaddefs-file
911 (with-temp-buffer
912 (insert-file-contents loaddefs-file)
913 (when (re-search-forward (concat "^(defvar " (symbol-name symbol))
914 nil t)
915 (search-backward "\n;;; Generated autoloads from ")
916 (goto-char (match-end 0))
917 (setq file (buffer-substring (point)
918 (progn (end-of-line) (point)))))))
919 ;; If it is, load that library.
920 (when file
921 (when (string-match "\\.el\\'" file)
922 (setq file (substring file 0 (match-beginning 0))))
923 (load file))))
924 (unless (get symbol 'custom-type)
925 (error "Variable %s cannot be customized" symbol))
86bd10bc 926 (custom-buffer-create (list (list symbol 'custom-variable))
38d58078 927 (format "*Customize Option: %s*"
86bd10bc 928 (custom-unlispify-tag-name symbol))))
d543e20b 929
e418be26
KH
930(defvar customize-changed-options-previous-release "20.2"
931 "Version for `customize-changed-options' to refer back to by default.")
932
c32de15e 933;;;###autoload
f50dc5d2 934(defun customize-changed-options (since-version)
e418be26
KH
935 "Customize all user option variables changed in Emacs itself.
936This includes new user option variables and faces, and new
937customization groups, as well as older options and faces whose default
938values have changed since the previous major Emacs release.
939
940With argument SINCE-VERSION (a string), customize all user option
941variables that were added (or their meanings were changed) since that
942version."
943
f50dc5d2
KH
944 (interactive "sCustomize options changed, since version (default all versions): ")
945 (if (equal since-version "")
26c67de8
DL
946 (setq since-version nil)
947 (unless (condition-case nil
948 (numberp (read since-version))
949 (error nil))
950 (signal 'wrong-type-argument (list 'numberp since-version))))
e418be26
KH
951 (unless since-version
952 (setq since-version customize-changed-options-previous-release))
1e484d64
DN
953 (let ((found nil)
954 (versions nil))
f50dc5d2 955 (mapatoms (lambda (symbol)
a55d9b3b 956 (and (or (boundp symbol)
1e484d64
DN
957 ;; For variables not yet loaded.
958 (get symbol 'standard-value)
a55d9b3b
DN
959 ;; For groups the previous test fails, this one
960 ;; could be used to determine if symbol is a
961 ;; group. Is there a better way for this?
962 (get symbol 'group-documentation))
f50dc5d2
KH
963 (let ((version (get symbol 'custom-version)))
964 (and version
965 (or (null since-version)
1e484d64 966 (customize-version-lessp since-version version))
d3d4df42 967 (if (member version versions)
1e484d64
DN
968 t
969 ;;; Collect all versions that we use.
970 (push version versions))))
f50dc5d2 971 (setq found
a55d9b3b
DN
972 ;; We have to set the right thing here,
973 ;; depending if we have a group or a
d3d4df42 974 ;; variable.
a55d9b3b
DN
975 (if (get symbol 'group-documentation)
976 (cons (list symbol 'custom-group) found)
977 (cons (list symbol 'custom-variable) found))))))
f50dc5d2 978 (if (not found)
e418be26
KH
979 (error "No user option defaults have been changed since Emacs %s"
980 since-version)
65f64d00
RS
981 (let ((flist nil))
982 (while versions
d3d4df42 983 (push (copy-sequence
65f64d00
RS
984 (cdr (assoc (car versions) custom-versions-load-alist)))
985 flist)
986 (setq versions (cdr versions)))
d3d4df42 987 (put 'custom-versions-load-alist 'custom-loads
65f64d00
RS
988 ;; Get all the files that correspond to element from the
989 ;; VERSIONS list. This could use some simplification.
1e484d64
DN
990 (apply 'nconc flist)))
991 ;; Because we set all the files needed to be loaded as a
992 ;; `custom-loads' property to `custom-versions-load-alist' this
993 ;; call will actually load them.
994 (custom-load-symbol 'custom-versions-load-alist)
995 ;; Clean up
996 (put 'custom-versions-load-alist 'custom-loads nil)
997 (custom-buffer-create (custom-sort-items found t 'first)
f50dc5d2
KH
998 "*Customize Changed Options*"))))
999
1000(defun customize-version-lessp (version1 version2)
26c67de8
DL
1001 ;; Why are the versions strings, and given that they are, why aren't
1002 ;; they converted to numbers and compared as such here? -- fx
1003
e418be26
KH
1004 ;; In case someone made a mistake and left out the quotes
1005 ;; in the :version value.
1006 (if (numberp version2)
1007 (setq version2 (prin1-to-string version2)))
f50dc5d2 1008 (let (major1 major2 minor1 minor2)
26c67de8
DL
1009 (string-match "\\([0-9]+\\)\\(\\.\\([0-9]+\\)\\)?" version1)
1010 (setq major1 (read (or (match-string 1 version1)
1011 "0")))
1012 (setq minor1 (read (or (match-string 3 version1)
1013 "0")))
1014 (string-match "\\([0-9]+\\)\\(\\.\\([0-9]+\\)\\)?" version2)
1015 (setq major2 (read (or (match-string 1 version2)
1016 "0")))
1017 (setq minor2 (read (or (match-string 3 version2)
1018 "0")))
f50dc5d2
KH
1019 (or (< major1 major2)
1020 (and (= major1 major2)
1021 (< minor1 minor2)))))
1022
1023;;;###autoload
c32de15e
PA
1024(defalias 'customize-variable-other-window 'customize-option-other-window)
1025
bd042c03 1026;;;###autoload
38d58078
RS
1027(defun customize-option-other-window (symbol)
1028 "Customize SYMBOL, which must be a user option variable.
bd042c03
PA
1029Show the buffer in another window, but don't select it."
1030 (interactive (custom-variable-prompt))
86bd10bc
PA
1031 (custom-buffer-create-other-window
1032 (list (list symbol 'custom-variable))
38d58078 1033 (format "*Customize Option: %s*" (custom-unlispify-tag-name symbol))))
bd042c03 1034
d543e20b 1035;;;###autoload
cdd50dea 1036(defun customize-face (&optional face)
d543e20b 1037 "Customize SYMBOL, which should be a face name or nil.
d29a536a
GM
1038If SYMBOL is nil, customize all faces.
1039
1040Interactively, when point is on text which has a face specified,
1041suggest to customized that face, if it's customizable."
1042 (interactive
cdd50dea
RS
1043 (list (read-face-name "Customize face" "all faces" t)))
1044 (if (member face '(nil ""))
1045 (setq face (face-list)))
1046 (if (and (listp face) (null (cdr face)))
1047 (setq face (car face)))
1048 (if (listp face)
da5ec617 1049 (custom-buffer-create (custom-sort-items
cdd50dea
RS
1050 (mapcar (lambda (s)
1051 (list s 'custom-face))
1052 face)
da5ec617
PA
1053 t nil)
1054 "*Customize Faces*")
cdd50dea
RS
1055 (unless (facep face)
1056 (error "Invalid face %S"))
1057 (custom-buffer-create (list (list face 'custom-face))
86bd10bc 1058 (format "*Customize Face: %s*"
cdd50dea 1059 (custom-unlispify-tag-name face)))))
d543e20b 1060
bd042c03 1061;;;###autoload
701f49d2 1062(defun customize-face-other-window (&optional face)
d29a536a
GM
1063 "Show customization buffer for face SYMBOL in other window.
1064
1065Interactively, when point is on text which has a face specified,
1066suggest to customized that face, if it's customizable."
1067 (interactive
501d8923
RS
1068 (list (read-face-name "Customize face" "all faces" t)))
1069 (if (member face '(nil ""))
1070 (setq face (face-list)))
1071 (if (and (listp face) (null (cdr face)))
1072 (setq face (car face)))
1073 (if (listp face)
1074 (custom-buffer-create-other-window
1075 (custom-sort-items
1076 (mapcar (lambda (s)
1077 (list s 'custom-face))
1078 face)
1079 t nil)
1080 "*Customize Faces*")
1081 (unless (facep face)
1082 (error "Invalid face %S"))
d3d4df42 1083 (custom-buffer-create-other-window
501d8923
RS
1084 (list (list face 'custom-face))
1085 (format "*Customize Face: %s*"
1086 (custom-unlispify-tag-name face)))))
bd042c03 1087
d543e20b
PA
1088;;;###autoload
1089(defun customize-customized ()
6d528fc5
PA
1090 "Customize all user options set since the last save in this session."
1091 (interactive)
1092 (let ((found nil))
1093 (mapatoms (lambda (symbol)
d3d4df42
DL
1094 (and (or (get symbol 'customized-face)
1095 (get symbol 'customized-face-comment))
6d528fc5 1096 (custom-facep symbol)
a1a4fa22 1097 (push (list symbol 'custom-face) found))
d3d4df42
DL
1098 (and (or (get symbol 'customized-value)
1099 (get symbol 'customized-variable-comment))
6d528fc5 1100 (boundp symbol)
a1a4fa22 1101 (push (list symbol 'custom-variable) found))))
da5ec617
PA
1102 (if (not found)
1103 (error "No customized user options")
1104 (custom-buffer-create (custom-sort-items found t nil)
1105 "*Customize Customized*"))))
6d528fc5
PA
1106
1107;;;###autoload
1108(defun customize-saved ()
1109 "Customize all already saved user options."
d543e20b
PA
1110 (interactive)
1111 (let ((found nil))
1112 (mapatoms (lambda (symbol)
d3d4df42
DL
1113 (and (or (get symbol 'saved-face)
1114 (get symbol 'saved-face-comment))
d543e20b 1115 (custom-facep symbol)
a1a4fa22 1116 (push (list symbol 'custom-face) found))
d3d4df42
DL
1117 (and (or (get symbol 'saved-value)
1118 (get symbol 'saved-variable-comment))
d543e20b 1119 (boundp symbol)
a1a4fa22 1120 (push (list symbol 'custom-variable) found))))
da5ec617
PA
1121 (if (not found )
1122 (error "No saved user options")
1123 (custom-buffer-create (custom-sort-items found t nil)
1124 "*Customize Saved*"))))
d543e20b
PA
1125
1126;;;###autoload
1127(defun customize-apropos (regexp &optional all)
1128 "Customize all user options matching REGEXP.
a1a4fa22
PA
1129If ALL is `options', include only options.
1130If ALL is `faces', include only faces.
1131If ALL is `groups', include only groups.
1132If ALL is t (interactively, with prefix arg), include options which are not
1133user-settable, as well as faces and groups."
d543e20b
PA
1134 (interactive "sCustomize regexp: \nP")
1135 (let ((found nil))
1136 (mapatoms (lambda (symbol)
1137 (when (string-match regexp (symbol-name symbol))
a1a4fa22
PA
1138 (when (and (not (memq all '(faces options)))
1139 (get symbol 'custom-group))
1140 (push (list symbol 'custom-group) found))
1141 (when (and (not (memq all '(options groups)))
1142 (custom-facep symbol))
1143 (push (list symbol 'custom-face) found))
1144 (when (and (not (memq all '(groups faces)))
1145 (boundp symbol)
d543e20b 1146 (or (get symbol 'saved-value)
25ac13b5 1147 (get symbol 'standard-value)
a1a4fa22
PA
1148 (if (memq all '(nil options))
1149 (user-variable-p symbol)
1150 (get symbol 'variable-documentation))))
1151 (push (list symbol 'custom-variable) found)))))
1152 (if (not found)
1153 (error "No matches")
da5ec617
PA
1154 (custom-buffer-create (custom-sort-items found t
1155 custom-buffer-order-groups)
1156 "*Customize Apropos*"))))
a1a4fa22
PA
1157
1158;;;###autoload
1159(defun customize-apropos-options (regexp &optional arg)
1160 "Customize all user options matching REGEXP.
1161With prefix arg, include options which are not user-settable."
1162 (interactive "sCustomize regexp: \nP")
1163 (customize-apropos regexp (or arg 'options)))
1164
1165;;;###autoload
1166(defun customize-apropos-faces (regexp)
1167 "Customize all user faces matching REGEXP."
1168 (interactive "sCustomize regexp: \n")
1169 (customize-apropos regexp 'faces))
1170
1171;;;###autoload
1172(defun customize-apropos-groups (regexp)
1173 "Customize all user groups matching REGEXP."
1174 (interactive "sCustomize regexp: \n")
1175 (customize-apropos regexp 'groups))
d543e20b 1176
6d528fc5
PA
1177;;; Buffer.
1178
944c91b6
PA
1179(defcustom custom-buffer-style 'links
1180 "Control the presentation style for customization buffers.
1181The value should be a symbol, one of:
1182
1183brackets: groups nest within each other with big horizontal brackets.
1184links: groups have links to subgroups."
1185 :type '(radio (const brackets)
1186 (const links))
1187 :group 'custom-buffer)
1188
8d8ca350
DL
1189;; If we pass BUFFER to `bury-buffer', the buffer isn't removed from
1190;; the window.
62633a5f
DL
1191(defun custom-bury-buffer (buffer)
1192 (bury-buffer))
1193
8d8ca350 1194(defcustom custom-buffer-done-function 'custom-bury-buffer
d3d4df42
DL
1195 "*Function called to remove a Custom buffer when the user is done with it.
1196Called with one argument, the buffer to remove."
8d8ca350
DL
1197 :type '(choice (function-item :tag "Bury buffer" custom-bury-buffer)
1198 (function-item :tag "Kill buffer" kill-buffer)
d3d4df42
DL
1199 (function :tag "Other"))
1200 :version "21.1"
1201 :group 'custom-buffer)
1202
944c91b6
PA
1203(defcustom custom-buffer-indent 3
1204 "Number of spaces to indent nested groups."
1205 :type 'integer
1206 :group 'custom-buffer)
1207
d543e20b 1208;;;###autoload
3aec85bf 1209(defun custom-buffer-create (options &optional name description)
d543e20b 1210 "Create a buffer containing OPTIONS.
86bd10bc 1211Optional NAME is the name of the buffer.
d543e20b
PA
1212OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
1213SYMBOL is a customization option, and WIDGET is a widget for editing
1214that option."
86bd10bc
PA
1215 (unless name (setq name "*Customization*"))
1216 (kill-buffer (get-buffer-create name))
b4854a23 1217 (pop-to-buffer (get-buffer-create name))
3aec85bf 1218 (custom-buffer-create-internal options description))
bd042c03 1219
6d528fc5 1220;;;###autoload
3aec85bf 1221(defun custom-buffer-create-other-window (options &optional name description)
bd042c03 1222 "Create a buffer containing OPTIONS.
86bd10bc 1223Optional NAME is the name of the buffer.
bd042c03
PA
1224OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
1225SYMBOL is a customization option, and WIDGET is a widget for editing
1226that option."
86bd10bc
PA
1227 (unless name (setq name "*Customization*"))
1228 (kill-buffer (get-buffer-create name))
b4854a23
KH
1229 (let ((window (selected-window))
1230 (pop-up-windows t)
1231 (special-display-buffer-names nil)
1232 (special-display-regexps nil)
1233 (same-window-buffer-names nil)
1234 (same-window-regexps nil))
1235 (pop-to-buffer (get-buffer-create name))
3aec85bf 1236 (custom-buffer-create-internal options description)
bd042c03 1237 (select-window window)))
9097aeb7
PA
1238
1239(defcustom custom-reset-button-menu nil
1240 "If non-nil, only show a single reset button in customize buffers.
1241This button will have a menu with all three reset operations."
1242 :type 'boolean
6aaedd12 1243 :group 'custom-buffer)
bd042c03 1244
c42c5c7c
KS
1245(defcustom custom-buffer-verbose-help t
1246 "If non-nil, include explanatory text in the customization buffer."
1247 :type 'boolean
1248 :group 'custom-buffer)
1249
d3d4df42
DL
1250(defun Custom-buffer-done (&rest ignore)
1251 "Remove current buffer by calling `custom-buffer-done-function'."
1252 (interactive)
1253 (funcall custom-buffer-done-function (current-buffer)))
1254
1255(defcustom custom-raised-buttons (not (equal (face-valid-attribute-values :box)
1256 '(("unspecified" . unspecified))))
1257 "If non-nil, indicate active buttons in a `raised-button' style.
1258Otherwise use brackets."
1259 :type 'boolean
1260 :version "21.1"
1261 :group 'custom-buffer)
1262
3aec85bf 1263(defun custom-buffer-create-internal (options &optional description)
bd042c03 1264 (message "Creating customization buffer...")
d543e20b 1265 (custom-mode)
c42c5c7c
KS
1266 (if custom-buffer-verbose-help
1267 (progn
1268 (widget-insert "This is a customization buffer")
1269 (if description
1270 (widget-insert description))
1271 (widget-insert (format ".
d3d4df42 1272%s show active fields; type RET or click mouse-1
3aec85bf
RS
1273on an active field to invoke its action. Editing an option value
1274changes the text in the buffer; invoke the State button and
1275choose the Set operation to set the option value.
d3d4df42
DL
1276Invoke " (if custom-raised-buttons
1277 "`Raised' buttons"
1278 "Square brackets")))
c42c5c7c
KS
1279 (widget-create 'info-link
1280 :tag "Help"
1281 :help-echo "Read the online help."
1282 "(emacs)Easy Customization")
1283 (widget-insert " for more information.\n\n")
1284 (message "Creating customization buttons...")
1285 (widget-insert "Operate on everything in this buffer:\n "))
1286 (widget-insert " "))
25ac13b5 1287 (widget-create 'push-button
0f3335c0 1288 :tag "Set for Current Session"
b62c92bb
RS
1289 :help-echo "\
1290Make your editing in this buffer take effect for this session."
25ac13b5 1291 :action (lambda (widget &optional event)
ab678382 1292 (Custom-set)))
25ac13b5
PA
1293 (widget-insert " ")
1294 (widget-create 'push-button
0f3335c0 1295 :tag "Save for Future Sessions"
25ac13b5 1296 :help-echo "\
b62c92bb 1297Make your editing in this buffer take effect for future Emacs sessions."
25ac13b5 1298 :action (lambda (widget &optional event)
ab678382 1299 (Custom-save)))
9097aeb7 1300 (if custom-reset-button-menu
0f3335c0
RS
1301 (progn
1302 (widget-insert " ")
1303 (widget-create 'push-button
1304 :tag "Reset"
1305 :help-echo "Show a menu with reset operations."
1306 :mouse-down-action (lambda (&rest junk) t)
1307 :action (lambda (widget &optional event)
1308 (custom-reset event))))
1309 (widget-insert "\n ")
9097aeb7
PA
1310 (widget-create 'push-button
1311 :tag "Reset"
c32de15e 1312 :help-echo "\
b62c92bb 1313Reset all edited text in this buffer to reflect current values."
ab678382 1314 :action 'Custom-reset-current)
9097aeb7
PA
1315 (widget-insert " ")
1316 (widget-create 'push-button
1317 :tag "Reset to Saved"
c32de15e 1318 :help-echo "\
b62c92bb 1319Reset all values in this buffer to their saved settings."
ab678382 1320 :action 'Custom-reset-saved)
9097aeb7
PA
1321 (widget-insert " ")
1322 (widget-create 'push-button
19d63704 1323 :tag "Erase Customization"
c32de15e 1324 :help-echo "\
19d63704 1325Un-customize all values in this buffer. They get their standard settings."
ab678382 1326 :action 'Custom-reset-standard))
c42c5c7c
KS
1327 (if (not custom-buffer-verbose-help)
1328 (progn
1329 (widget-insert " ")
1330 (widget-create 'info-link
1331 :tag "Help"
1332 :help-echo "Read the online help."
1333 "(emacs)Easy Customization")))
0eef62d5 1334 (widget-insert " ")
25ac13b5 1335 (widget-create 'push-button
d3d4df42 1336 :tag "Finish"
91a38db1
DL
1337 :help-echo
1338 (lambda (&rest ignore)
6b292312
DL
1339 (cond
1340 ((eq custom-buffer-done-function
1341 'custom-bury-buffer)
1342 "Bury this buffer")
1343 ((eq custom-buffer-done-function 'kill-buffer)
1344 "Kill this buffer")
1345 (t "Finish with this buffer")))
d3d4df42 1346 :action #'Custom-buffer-done)
25ac13b5
PA
1347 (widget-insert "\n\n")
1348 (message "Creating customization items...")
fadbdfea 1349 (buffer-disable-undo)
d3d4df42 1350 (setq custom-options
d543e20b
PA
1351 (if (= (length options) 1)
1352 (mapcar (lambda (entry)
1353 (widget-create (nth 1 entry)
c32de15e 1354 :documentation-shown t
d543e20b
PA
1355 :custom-state 'unknown
1356 :tag (custom-unlispify-tag-name
1357 (nth 0 entry))
1358 :value (nth 0 entry)))
1359 options)
1360 (let ((count 0)
1361 (length (length options)))
1362 (mapcar (lambda (entry)
6b292312
DL
1363 (prog2
1364 (message "Creating customization items ...%2d%%"
1365 (/ (* 100.0 count) length))
1366 (widget-create (nth 1 entry)
d543e20b
PA
1367 :tag (custom-unlispify-tag-name
1368 (nth 0 entry))
1369 :value (nth 0 entry))
6b292312
DL
1370 (setq count (1+ count))
1371 (unless (eq (preceding-char) ?\n)
1372 (widget-insert "\n"))
1373 (widget-insert "\n")))
1374 options))))
d543e20b
PA
1375 (unless (eq (preceding-char) ?\n)
1376 (widget-insert "\n"))
a9bcbf3f 1377 (message "Creating customization items ...done")
944c91b6 1378 (unless (eq custom-buffer-style 'tree)
fadbdfea 1379 (mapc 'custom-magic-reset custom-options))
d543e20b
PA
1380 (message "Creating customization setup...")
1381 (widget-setup)
fadbdfea 1382 (buffer-enable-undo)
d543e20b
PA
1383 (goto-char (point-min))
1384 (message "Creating customization buffer...done"))
1385
944c91b6
PA
1386;;; The Tree Browser.
1387
1388;;;###autoload
4ee1cf9f 1389(defun customize-browse (&optional group)
944c91b6 1390 "Create a tree browser for the customize hierarchy."
cda987f4 1391 (interactive)
4ee1cf9f
PA
1392 (unless group
1393 (setq group 'emacs))
1394 (let ((name "*Customize Browser*"))
1395 (kill-buffer (get-buffer-create name))
b4854a23 1396 (pop-to-buffer (get-buffer-create name)))
4ee1cf9f
PA
1397 (custom-mode)
1398 (widget-insert "\
cda987f4
RS
1399Square brackets show active fields; type RET or click mouse-1
1400on an active field to invoke its action.
df816618 1401Invoke [+] below to expand a group, and [-] to collapse an expanded group.\n")
4ee1cf9f
PA
1402 (if custom-browse-only-groups
1403 (widget-insert "\
c953515e 1404Invoke the [Group] button below to edit that item in another window.\n\n")
d3d4df42
DL
1405 (widget-insert "Invoke the ")
1406 (widget-create 'item
4ee1cf9f
PA
1407 :format "%t"
1408 :tag "[Group]"
1409 :tag-glyph "folder")
1410 (widget-insert ", ")
d3d4df42 1411 (widget-create 'item
4ee1cf9f
PA
1412 :format "%t"
1413 :tag "[Face]"
1414 :tag-glyph "face")
1415 (widget-insert ", and ")
d3d4df42 1416 (widget-create 'item
4ee1cf9f
PA
1417 :format "%t"
1418 :tag "[Option]"
1419 :tag-glyph "option")
1420 (widget-insert " buttons below to edit that
c953515e 1421item in another window.\n\n"))
4ee1cf9f 1422 (let ((custom-buffer-style 'tree))
d3d4df42 1423 (widget-create 'custom-group
4ee1cf9f
PA
1424 :custom-last t
1425 :custom-state 'unknown
1426 :tag (custom-unlispify-tag-name group)
1427 :value group))
1428 (goto-char (point-min)))
944c91b6 1429
c953515e 1430(define-widget 'custom-browse-visibility 'item
1edec9cf 1431 "Control visibility of items in the customize tree browser."
da5ec617 1432 :format "%[[%t]%]"
c953515e 1433 :action 'custom-browse-visibility-action)
944c91b6 1434
c953515e 1435(defun custom-browse-visibility-action (widget &rest ignore)
944c91b6
PA
1436 (let ((custom-buffer-style 'tree))
1437 (custom-toggle-parent widget)))
1438
c953515e 1439(define-widget 'custom-browse-group-tag 'push-button
944c91b6 1440 "Show parent in other window when activated."
cd6c0940 1441 :tag "Group"
da5ec617 1442 :tag-glyph "folder"
c953515e 1443 :action 'custom-browse-group-tag-action)
944c91b6 1444
c953515e 1445(defun custom-browse-group-tag-action (widget &rest ignore)
944c91b6
PA
1446 (let ((parent (widget-get widget :parent)))
1447 (customize-group-other-window (widget-value parent))))
1448
c953515e 1449(define-widget 'custom-browse-variable-tag 'push-button
944c91b6 1450 "Show parent in other window when activated."
cd6c0940 1451 :tag "Option"
da5ec617 1452 :tag-glyph "option"
c953515e 1453 :action 'custom-browse-variable-tag-action)
944c91b6 1454
c953515e 1455(defun custom-browse-variable-tag-action (widget &rest ignore)
944c91b6
PA
1456 (let ((parent (widget-get widget :parent)))
1457 (customize-variable-other-window (widget-value parent))))
1458
c953515e 1459(define-widget 'custom-browse-face-tag 'push-button
944c91b6 1460 "Show parent in other window when activated."
cd6c0940 1461 :tag "Face"
da5ec617 1462 :tag-glyph "face"
c953515e 1463 :action 'custom-browse-face-tag-action)
944c91b6 1464
c953515e 1465(defun custom-browse-face-tag-action (widget &rest ignore)
944c91b6
PA
1466 (let ((parent (widget-get widget :parent)))
1467 (customize-face-other-window (widget-value parent))))
1468
c953515e 1469(defconst custom-browse-alist '((" " "space")
da5ec617
PA
1470 (" | " "vertical")
1471 ("-\\ " "top")
1472 (" |-" "middle")
1473 (" `-" "bottom")))
1474
c953515e 1475(defun custom-browse-insert-prefix (prefix)
da5ec617 1476 "Insert PREFIX. On XEmacs convert it to line graphics."
d3d4df42 1477 ;; Fixme: do graphics.
da5ec617 1478 (if nil ; (string-match "XEmacs" emacs-version)
d3d4df42 1479 (progn
da5ec617
PA
1480 (insert "*")
1481 (while (not (string-equal prefix ""))
1482 (let ((entry (substring prefix 0 3)))
1483 (setq prefix (substring prefix 3))
1484 (let ((overlay (make-overlay (1- (point)) (point) nil t nil))
c953515e 1485 (name (nth 1 (assoc entry custom-browse-alist))))
da5ec617
PA
1486 (overlay-put overlay 'end-glyph (widget-glyph-find name entry))
1487 (overlay-put overlay 'start-open t)
1488 (overlay-put overlay 'end-open t)))))
1489 (insert prefix)))
1490
d543e20b
PA
1491;;; Modification of Basic Widgets.
1492;;
1493;; We add extra properties to the basic widgets needed here. This is
1494;; fine, as long as we are careful to stay within out own namespace.
1495;;
1496;; We want simple widgets to be displayed by default, but complex
1497;; widgets to be hidden.
1498
1499(widget-put (get 'item 'widget-type) :custom-show t)
1500(widget-put (get 'editable-field 'widget-type)
1501 :custom-show (lambda (widget value)
1502 (let ((pp (pp-to-string value)))
1503 (cond ((string-match "\n" pp)
1504 nil)
1505 ((> (length pp) 40)
1506 nil)
1507 (t t)))))
1508(widget-put (get 'menu-choice 'widget-type) :custom-show t)
1509
1510;;; The `custom-manual' Widget.
1511
1512(define-widget 'custom-manual 'info-link
1513 "Link to the manual entry for this customization option."
1514 :help-echo "Read the manual entry for this option."
1515 :tag "Manual")
1516
1517;;; The `custom-magic' Widget.
1518
6aaedd12
PA
1519(defgroup custom-magic-faces nil
1520 "Faces used by the magic button."
1521 :group 'custom-faces
1522 :group 'custom-buffer)
1523
d543e20b
PA
1524(defface custom-invalid-face '((((class color))
1525 (:foreground "yellow" :background "red"))
1526 (t
b5555381 1527 (:weight bold :slant italic :underline t)))
6aaedd12
PA
1528 "Face used when the customize item is invalid."
1529 :group 'custom-magic-faces)
d543e20b
PA
1530
1531(defface custom-rogue-face '((((class color))
1532 (:foreground "pink" :background "black"))
1533 (t
1534 (:underline t)))
6aaedd12
PA
1535 "Face used when the customize item is not defined for customization."
1536 :group 'custom-magic-faces)
d543e20b 1537
d3d4df42 1538(defface custom-modified-face '((((class color))
d543e20b
PA
1539 (:foreground "white" :background "blue"))
1540 (t
b5555381 1541 (:slant italic :bold)))
6aaedd12
PA
1542 "Face used when the customize item has been modified."
1543 :group 'custom-magic-faces)
d543e20b 1544
d3d4df42 1545(defface custom-set-face '((((class color))
d543e20b
PA
1546 (:foreground "blue" :background "white"))
1547 (t
b5555381 1548 (:slant italic)))
6aaedd12
PA
1549 "Face used when the customize item has been set."
1550 :group 'custom-magic-faces)
d543e20b 1551
d3d4df42 1552(defface custom-changed-face '((((class color))
d543e20b
PA
1553 (:foreground "white" :background "blue"))
1554 (t
b5555381 1555 (:slant italic)))
6aaedd12
PA
1556 "Face used when the customize item has been changed."
1557 :group 'custom-magic-faces)
d543e20b
PA
1558
1559(defface custom-saved-face '((t (:underline t)))
6aaedd12
PA
1560 "Face used when the customize item has been saved."
1561 :group 'custom-magic-faces)
d543e20b 1562
25ac13b5 1563(defconst custom-magic-alist '((nil "#" underline "\
d543e20b 1564uninitialized, you should not see this.")
25ac13b5 1565 (unknown "?" italic "\
d543e20b 1566unknown, you should not see this.")
25ac13b5 1567 (hidden "-" default "\
cbc7d892
RS
1568hidden, invoke \"Show\" in the previous line to show." "\
1569group now hidden, invoke \"Show\", above, to show contents.")
25ac13b5 1570 (invalid "x" custom-invalid-face "\
9097aeb7 1571the value displayed for this %c is invalid and cannot be set.")
25ac13b5 1572 (modified "*" custom-modified-face "\
1e850936
RS
1573you have edited the value as text, but you have not set the %c." "\
1574you have edited something in this group, but not set it.")
25ac13b5 1575 (set "+" custom-set-face "\
1e850936
RS
1576you have set this %c, but not saved it for future sessions." "\
1577something in this group has been set, but not saved.")
25ac13b5 1578 (changed ":" custom-changed-face "\
9097aeb7 1579this %c has been changed outside the customize buffer." "\
25ac13b5
PA
1580something in this group has been changed outside customize.")
1581 (saved "!" custom-saved-face "\
9097aeb7 1582this %c has been set and saved." "\
5dd0cad0 1583something in this group has been set and saved.")
25ac13b5 1584 (rogue "@" custom-rogue-face "\
9097aeb7 1585this %c has not been changed with customize." "\
25ac13b5
PA
1586something in this group is not prepared for customization.")
1587 (standard " " nil "\
9097aeb7 1588this %c is unchanged from its standard setting." "\
c32de15e 1589visible group members are all at standard settings."))
d543e20b 1590 "Alist of customize option states.
d3d4df42 1591Each entry is of the form (STATE MAGIC FACE ITEM-DESC [ GROUP-DESC ]), where
d543e20b
PA
1592
1593STATE is one of the following symbols:
1594
1595`nil'
1596 For internal use, should never occur.
1597`unknown'
1598 For internal use, should never occur.
1599`hidden'
d3d4df42 1600 This item is not being displayed.
d543e20b
PA
1601`invalid'
1602 This item is modified, but has an invalid form.
1603`modified'
1604 This item is modified, and has a valid form.
1605`set'
1606 This item has been set but not saved.
1607`changed'
1608 The current value of this item has been changed temporarily.
1609`saved'
1610 This item is marked for saving.
1611`rogue'
1612 This item has no customization information.
25ac13b5 1613`standard'
5dd0cad0 1614 This item is unchanged from the standard setting.
d543e20b
PA
1615
1616MAGIC is a string used to present that state.
1617
1618FACE is a face used to present the state.
1619
25ac13b5
PA
1620ITEM-DESC is a string describing the state for options.
1621
1622GROUP-DESC is a string describing the state for groups. If this is
1623left out, ITEM-DESC will be used.
1624
9097aeb7
PA
1625The string %c in either description will be replaced with the
1626category of the item. These are `group'. `option', and `face'.
1627
25ac13b5 1628The list should be sorted most significant first.")
d543e20b
PA
1629
1630(defcustom custom-magic-show 'long
3acab5ef 1631 "If non-nil, show textual description of the state.
b62c92bb 1632If `long', show a full-line description, not just one word."
d543e20b 1633 :type '(choice (const :tag "no" nil)
c992338c
AS
1634 (const long)
1635 (other :tag "short" short))
6aaedd12 1636 :group 'custom-buffer)
d543e20b 1637
9097aeb7 1638(defcustom custom-magic-show-hidden '(option face)
b62c92bb
RS
1639 "Control whether the State button is shown for hidden items.
1640The value should be a list with the custom categories where the State
9097aeb7
PA
1641button should be visible. Possible categories are `group', `option',
1642and `face'."
1643 :type '(set (const group) (const option) (const face))
6aaedd12 1644 :group 'custom-buffer)
3acab5ef 1645
25ac13b5 1646(defcustom custom-magic-show-button nil
b62c92bb 1647 "Show a \"magic\" button indicating the state of each customization option."
d543e20b 1648 :type 'boolean
6aaedd12 1649 :group 'custom-buffer)
d543e20b
PA
1650
1651(define-widget 'custom-magic 'default
1652 "Show and manipulate state for a customization option."
1653 :format "%v"
86bd10bc 1654 :action 'widget-parent-action
6d528fc5 1655 :notify 'ignore
d543e20b
PA
1656 :value-get 'ignore
1657 :value-create 'custom-magic-value-create
1658 :value-delete 'widget-children-value-delete)
1659
86bd10bc
PA
1660(defun widget-magic-mouse-down-action (widget &optional event)
1661 ;; Non-nil unless hidden.
d3d4df42 1662 (not (eq (widget-get (widget-get (widget-get widget :parent) :parent)
86bd10bc
PA
1663 :custom-state)
1664 'hidden)))
1665
d543e20b 1666(defun custom-magic-value-create (widget)
2365594b 1667 "Create compact status report for WIDGET."
d543e20b
PA
1668 (let* ((parent (widget-get widget :parent))
1669 (state (widget-get parent :custom-state))
3acab5ef 1670 (hidden (eq state 'hidden))
25ac13b5 1671 (entry (assq state custom-magic-alist))
d543e20b
PA
1672 (magic (nth 1 entry))
1673 (face (nth 2 entry))
9097aeb7
PA
1674 (category (widget-get parent :custom-category))
1675 (text (or (and (eq category 'group)
25ac13b5
PA
1676 (nth 4 entry))
1677 (nth 3 entry)))
f985c5f7 1678 (form (widget-get parent :custom-form))
d543e20b 1679 children)
9097aeb7 1680 (while (string-match "\\`\\(.*\\)%c\\(.*\\)\\'" text)
d3d4df42 1681 (setq text (concat (match-string 1 text)
9097aeb7
PA
1682 (symbol-name category)
1683 (match-string 2 text))))
3acab5ef 1684 (when (and custom-magic-show
9097aeb7
PA
1685 (or (not hidden)
1686 (memq category custom-magic-show-hidden)))
25ac13b5 1687 (insert " ")
26c7b3ef
RS
1688 (when (and (eq category 'group)
1689 (not (and (eq custom-buffer-style 'links)
1690 (> (widget-get parent :custom-level) 1))))
944c91b6
PA
1691 (insert-char ?\ (* custom-buffer-indent
1692 (widget-get parent :custom-level))))
d3d4df42
DL
1693 (push (widget-create-child-and-convert
1694 widget 'choice-item
d5c42d02 1695 :help-echo "Change the state of this item."
3acab5ef 1696 :format (if hidden "%t" "%[%t%]")
25ac13b5
PA
1697 :button-prefix 'widget-push-button-prefix
1698 :button-suffix 'widget-push-button-suffix
86bd10bc
PA
1699 :mouse-down-action 'widget-magic-mouse-down-action
1700 :tag "State")
d543e20b
PA
1701 children)
1702 (insert ": ")
b62c92bb
RS
1703 (let ((start (point)))
1704 (if (eq custom-magic-show 'long)
1705 (insert text)
1706 (insert (symbol-name state)))
f985c5f7
PA
1707 (cond ((eq form 'lisp)
1708 (insert " (lisp)"))
1709 ((eq form 'mismatch)
1710 (insert " (mismatch)")))
b62c92bb 1711 (put-text-property start (point) 'face 'custom-state-face))
d543e20b 1712 (insert "\n"))
26c7b3ef
RS
1713 (when (and (eq category 'group)
1714 (not (and (eq custom-buffer-style 'links)
1715 (> (widget-get parent :custom-level) 1))))
944c91b6
PA
1716 (insert-char ?\ (* custom-buffer-indent
1717 (widget-get parent :custom-level))))
d543e20b
PA
1718 (when custom-magic-show-button
1719 (when custom-magic-show
1720 (let ((indent (widget-get parent :indent)))
1721 (when indent
1722 (insert-char ? indent))))
d3d4df42
DL
1723 (push (widget-create-child-and-convert
1724 widget 'choice-item
86bd10bc
PA
1725 :mouse-down-action 'widget-magic-mouse-down-action
1726 :button-face face
3acab5ef
PA
1727 :button-prefix ""
1728 :button-suffix ""
86bd10bc 1729 :help-echo "Change the state."
3acab5ef 1730 :format (if hidden "%t" "%[%t%]")
f985c5f7 1731 :tag (if (memq form '(lisp mismatch))
86bd10bc
PA
1732 (concat "(" magic ")")
1733 (concat "[" magic "]")))
d543e20b
PA
1734 children)
1735 (insert " "))
1736 (widget-put widget :children children)))
1737
1738(defun custom-magic-reset (widget)
1739 "Redraw the :custom-magic property of WIDGET."
1740 (let ((magic (widget-get widget :custom-magic)))
1741 (widget-value-set magic (widget-value magic))))
1742
d543e20b
PA
1743;;; The `custom' Widget.
1744
d3d4df42 1745(defface custom-button-face
3060662f 1746 '((((type x w32 mac) (class color)) ; Like default modeline
1a578e9b
AC
1747 (:box (:line-width 2 :style released-button)
1748 :background "lightgrey" :foreground "black"))
d3d4df42
DL
1749 (t
1750 nil))
b62c92bb 1751 "Face used for buttons in customization buffers."
d3d4df42
DL
1752 :version "21.1"
1753 :group 'custom-faces)
1754
1755(defface custom-button-pressed-face
3060662f 1756 '((((type x w32 mac) (class color))
1a578e9b
AC
1757 (:box (:line-width 2 :style pressed-button)
1758 :background "lightgrey" :foreground "black"))
d3d4df42
DL
1759 (t
1760 (:inverse-video t)))
1761 "Face used for buttons in customization buffers."
1762 :version "21.1"
b62c92bb
RS
1763 :group 'custom-faces)
1764
1765(defface custom-documentation-face nil
1766 "Face used for documentation strings in customization buffers."
1767 :group 'custom-faces)
1768
1769(defface custom-state-face '((((class color)
1770 (background dark))
1771 (:foreground "lime green"))
1772 (((class color)
1773 (background light))
1774 (:foreground "dark green"))
1775 (t nil))
1776 "Face used for State descriptions in the customize buffer."
1777 :group 'custom-faces)
1778
d543e20b
PA
1779(define-widget 'custom 'default
1780 "Customize a user option."
944c91b6 1781 :format "%v"
d543e20b 1782 :convert-widget 'custom-convert-widget
d543e20b 1783 :notify 'custom-notify
944c91b6 1784 :custom-prefix ""
d543e20b
PA
1785 :custom-level 1
1786 :custom-state 'hidden
1787 :documentation-property 'widget-subclass-responsibility
1788 :value-create 'widget-subclass-responsibility
1789 :value-delete 'widget-children-value-delete
86bd10bc
PA
1790 :value-get 'widget-value-value-get
1791 :validate 'widget-children-validate
d543e20b
PA
1792 :match (lambda (widget value) (symbolp value)))
1793
1794(defun custom-convert-widget (widget)
2365594b 1795 "Initialize :value and :tag from :args in WIDGET."
d543e20b 1796 (let ((args (widget-get widget :args)))
d3d4df42 1797 (when args
d543e20b
PA
1798 (widget-put widget :value (widget-apply widget
1799 :value-to-internal (car args)))
1800 (widget-put widget :tag (custom-unlispify-tag-name (car args)))
1801 (widget-put widget :args nil)))
1802 widget)
1803
d543e20b
PA
1804(defun custom-notify (widget &rest args)
1805 "Keep track of changes."
0a3a0b56
PA
1806 (let ((state (widget-get widget :custom-state)))
1807 (unless (eq state 'modified)
1808 (unless (memq state '(nil unknown hidden))
1809 (widget-put widget :custom-state 'modified))
1810 (custom-magic-reset widget)
1811 (apply 'widget-default-notify widget args))))
d543e20b
PA
1812
1813(defun custom-redraw (widget)
1814 "Redraw WIDGET with current settings."
6d528fc5
PA
1815 (let ((line (count-lines (point-min) (point)))
1816 (column (current-column))
1817 (pos (point))
d543e20b
PA
1818 (from (marker-position (widget-get widget :from)))
1819 (to (marker-position (widget-get widget :to))))
1820 (save-excursion
1821 (widget-value-set widget (widget-value widget))
1822 (custom-redraw-magic widget))
1823 (when (and (>= pos from) (<= pos to))
6d528fc5 1824 (condition-case nil
d3d4df42 1825 (progn
86bd10bc
PA
1826 (if (> column 0)
1827 (goto-line line)
1828 (goto-line (1+ line)))
6d528fc5
PA
1829 (move-to-column column))
1830 (error nil)))))
d543e20b
PA
1831
1832(defun custom-redraw-magic (widget)
1833 "Redraw WIDGET state with current settings."
d3d4df42 1834 (while widget
d543e20b 1835 (let ((magic (widget-get widget :custom-magic)))
d3d4df42 1836 (cond (magic
944c91b6
PA
1837 (widget-value-set magic (widget-value magic))
1838 (when (setq widget (widget-get widget :group))
1839 (custom-group-state-update widget)))
1840 (t
1841 (setq widget nil)))))
d543e20b
PA
1842 (widget-setup))
1843
1844(defun custom-show (widget value)
1845 "Non-nil if WIDGET should be shown with VALUE by default."
1846 (let ((show (widget-get widget :custom-show)))
1847 (cond ((null show)
1848 nil)
1849 ((eq t show)
1850 t)
1851 (t
1852 (funcall show widget value)))))
1853
d543e20b
PA
1854(defun custom-load-widget (widget)
1855 "Load all dependencies for WIDGET."
1856 (custom-load-symbol (widget-value widget)))
1857
c953515e
PA
1858(defun custom-unloaded-symbol-p (symbol)
1859 "Return non-nil if the dependencies of SYMBOL has not yet been loaded."
1860 (let ((found nil)
1861 (loads (get symbol 'custom-loads))
1862 load)
1863 (while loads
1864 (setq load (car loads)
1865 loads (cdr loads))
1866 (cond ((symbolp load)
1867 (unless (featurep load)
1868 (setq found t)))
1869 ((assoc load load-history))
1870 ((assoc (locate-library load) load-history)
1871 (message nil))
1872 (t
1873 (setq found t))))
1874 found))
1875
1876(defun custom-unloaded-widget-p (widget)
1877 "Return non-nil if the dependencies of WIDGET has not yet been loaded."
1878 (custom-unloaded-symbol-p (widget-value widget)))
1879
6d528fc5
PA
1880(defun custom-toggle-hide (widget)
1881 "Toggle visibility of WIDGET."
c953515e 1882 (custom-load-widget widget)
6d528fc5
PA
1883 (let ((state (widget-get widget :custom-state)))
1884 (cond ((memq state '(invalid modified))
1885 (error "There are unset changes"))
1886 ((eq state 'hidden)
1887 (widget-put widget :custom-state 'unknown))
d3d4df42 1888 (t
3acab5ef 1889 (widget-put widget :documentation-shown nil)
6d528fc5 1890 (widget-put widget :custom-state 'hidden)))
8697863a
PA
1891 (custom-redraw widget)
1892 (widget-setup)))
6d528fc5 1893
3acab5ef 1894(defun custom-toggle-parent (widget &rest ignore)
b62c92bb 1895 "Toggle visibility of parent of WIDGET."
3acab5ef
PA
1896 (custom-toggle-hide (widget-get widget :parent)))
1897
944c91b6
PA
1898(defun custom-add-see-also (widget &optional prefix)
1899 "Add `See also ...' to WIDGET if there are any links.
1900Insert PREFIX first if non-nil."
1901 (let* ((symbol (widget-get widget :value))
1902 (links (get symbol 'custom-links))
1903 (many (> (length links) 2))
1904 (buttons (widget-get widget :buttons))
1905 (indent (widget-get widget :indent)))
1906 (when links
1907 (when indent
1908 (insert-char ?\ indent))
1909 (when prefix
1910 (insert prefix))
1911 (insert "See also ")
1912 (while links
1913 (push (widget-create-child-and-convert widget (car links))
1914 buttons)
1915 (setq links (cdr links))
1916 (cond ((null links)
1917 (insert ".\n"))
1918 ((null (cdr links))
1919 (if many
1920 (insert ", and ")
1921 (insert " and ")))
d3d4df42 1922 (t
944c91b6
PA
1923 (insert ", "))))
1924 (widget-put widget :buttons buttons))))
1925
cd6c0940
RS
1926(defun custom-add-parent-links (widget &optional initial-string)
1927 "Add \"Parent groups: ...\" to WIDGET if the group has parents.
1928The value if non-nil if any parents were found.
1929If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
944c91b6
PA
1930 (let ((name (widget-value widget))
1931 (type (widget-type widget))
1932 (buttons (widget-get widget :buttons))
d377bee9 1933 (start (point))
944c91b6 1934 found)
cd6c0940 1935 (insert (or initial-string "Parent groups:"))
944c91b6 1936 (mapatoms (lambda (symbol)
da5ec617
PA
1937 (let ((entry (assq name (get symbol 'custom-group))))
1938 (when (eq (nth 1 entry) type)
1939 (insert " ")
d3d4df42
DL
1940 (push (widget-create-child-and-convert
1941 widget 'custom-group-link
da5ec617
PA
1942 :tag (custom-unlispify-tag-name symbol)
1943 symbol)
1944 buttons)
1945 (setq found t)))))
944c91b6 1946 (widget-put widget :buttons buttons)
d377bee9
RS
1947 (if found
1948 (insert "\n")
1949 (delete-region start (point)))
1950 found))
944c91b6 1951
d3d4df42
DL
1952;;; The `custom-comment' Widget.
1953
1954;; like the editable field
1955(defface custom-comment-face '((((class grayscale color)
1956 (background light))
1957 (:background "gray85"))
1958 (((class grayscale color)
1959 (background dark))
1960 (:background "dim gray"))
1961 (t
b5555381 1962 (:slant italic)))
d3d4df42
DL
1963 "Face used for comments on variables or faces"
1964 :version "21.1"
1965 :group 'custom-faces)
1966
1967;; like font-lock-comment-face
1968(defface custom-comment-tag-face
1969 '((((class color) (background dark)) (:foreground "gray80"))
1970 (((class color) (background light)) (:foreground "blue4"))
1971 (((class grayscale) (background light))
b5555381 1972 (:foreground "DimGray" :weight bold :slant italic))
d3d4df42 1973 (((class grayscale) (background dark))
b5555381
RS
1974 (:foreground "LightGray" :weight bold :slant italic))
1975 (t (:weight bold)))
d3d4df42
DL
1976 "Face used for variables or faces comment tags"
1977 :group 'custom-faces)
1978
1979(define-widget 'custom-comment 'string
164cfaeb 1980 "User comment."
d3d4df42 1981 :tag "Comment"
164cfaeb 1982 :help-echo "Edit a comment here."
d3d4df42
DL
1983 :sample-face 'custom-comment-tag-face
1984 :value-face 'custom-comment-face
164cfaeb
DL
1985 :shown nil
1986 :create 'custom-comment-create)
d3d4df42
DL
1987
1988(defun custom-comment-create (widget)
164cfaeb 1989 (let* ((null-comment (equal "" (widget-value widget))))
6171a945
DL
1990 (if (or (widget-get (widget-get widget :parent) :comment-shown)
1991 (not null-comment))
1992 (widget-default-create widget)
1993 ;; `widget-default-delete' expects markers in these slots --
1994 ;; maybe it shouldn't.
1995 (widget-put widget :from (point-marker))
1996 (widget-put widget :to (point-marker)))))
164cfaeb
DL
1997
1998(defun custom-comment-hide (widget)
1999 (widget-put (widget-get widget :parent) :comment-shown nil))
d3d4df42
DL
2000
2001;; Those functions are for the menu. WIDGET is NOT the comment widget. It's
2002;; the global custom one
2003(defun custom-comment-show (widget)
164cfaeb
DL
2004 (widget-put widget :comment-shown t)
2005 (custom-redraw widget)
2006 (widget-setup))
d3d4df42
DL
2007
2008(defun custom-comment-invisible-p (widget)
164cfaeb
DL
2009 (let ((val (widget-value (widget-get widget :comment-widget))))
2010 (and (equal "" val)
2011 (not (widget-get widget :comment-shown)))))
d3d4df42 2012
d543e20b
PA
2013;;; The `custom-variable' Widget.
2014
2365594b
DL
2015;; When this was underlined blue, users confused it with a
2016;; Mosaic-style hyperlink...
16b20ed9
GM
2017(defface custom-variable-tag-face
2018 `((((class color)
2019 (background dark))
b5555381 2020 (:foreground "light blue" :weight bold :height 1.2 :inherit variable-pitch))
16b20ed9
GM
2021 (((class color)
2022 (background light))
b5555381
RS
2023 (:foreground "blue" :weight bold :height 1.2 :inherit variable-pitch))
2024 (t (:weight bold)))
d543e20b 2025 "Face used for unpushable variable tags."
bd042c03 2026 :group 'custom-faces)
d543e20b 2027
b5555381 2028(defface custom-variable-button-face '((t (:underline t :weight bold)))
d543e20b 2029 "Face used for pushable variable tags."
bd042c03 2030 :group 'custom-faces)
d543e20b 2031
d64478da
KH
2032(defcustom custom-variable-default-form 'edit
2033 "Default form of displaying variable values."
2034 :type '(choice (const edit)
2035 (const lisp))
cd32a7ba
DN
2036 :group 'custom-buffer
2037 :version "20.3")
d64478da 2038
d543e20b
PA
2039(define-widget 'custom-variable 'custom
2040 "Customize variable."
944c91b6 2041 :format "%v"
d543e20b
PA
2042 :help-echo "Set or reset this variable."
2043 :documentation-property 'variable-documentation
9097aeb7 2044 :custom-category 'option
d543e20b
PA
2045 :custom-state nil
2046 :custom-menu 'custom-variable-menu-create
d64478da 2047 :custom-form nil ; defaults to value of `custom-variable-default-form'
d543e20b
PA
2048 :value-create 'custom-variable-value-create
2049 :action 'custom-variable-action
2050 :custom-set 'custom-variable-set
2051 :custom-save 'custom-variable-save
2052 :custom-reset-current 'custom-redraw
2053 :custom-reset-saved 'custom-variable-reset-saved
25ac13b5 2054 :custom-reset-standard 'custom-variable-reset-standard)
d543e20b 2055
bd042c03
PA
2056(defun custom-variable-type (symbol)
2057 "Return a widget suitable for editing the value of SYMBOL.
d3d4df42 2058If SYMBOL has a `custom-type' property, use that.
bd042c03
PA
2059Otherwise, look up symbol in `custom-guess-type-alist'."
2060 (let* ((type (or (get symbol 'custom-type)
25ac13b5 2061 (and (not (get symbol 'standard-value))
bd042c03
PA
2062 (custom-guess-type symbol))
2063 'sexp))
2064 (options (get symbol 'custom-options))
2065 (tmp (if (listp type)
46fa5a83 2066 (copy-sequence type)
bd042c03
PA
2067 (list type))))
2068 (when options
2069 (widget-put tmp :options options))
2070 tmp))
2071
d543e20b 2072(defun custom-variable-value-create (widget)
164cfaeb 2073 "Here is where you edit the variable's value."
d543e20b 2074 (custom-load-widget widget)
d64478da
KH
2075 (unless (widget-get widget :custom-form)
2076 (widget-put widget :custom-form custom-variable-default-form))
d543e20b
PA
2077 (let* ((buttons (widget-get widget :buttons))
2078 (children (widget-get widget :children))
2079 (form (widget-get widget :custom-form))
2080 (state (widget-get widget :custom-state))
2081 (symbol (widget-get widget :value))
d543e20b 2082 (tag (widget-get widget :tag))
bd042c03 2083 (type (custom-variable-type symbol))
d543e20b 2084 (conv (widget-convert type))
6d528fc5 2085 (get (or (get symbol 'custom-get) 'default-value))
944c91b6
PA
2086 (prefix (widget-get widget :custom-prefix))
2087 (last (widget-get widget :custom-last))
d543e20b 2088 (value (if (default-boundp symbol)
6d528fc5 2089 (funcall get symbol)
d543e20b 2090 (widget-get conv :value))))
164cfaeb 2091 ;; If the widget is new, the child determines whether it is hidden.
d543e20b
PA
2092 (cond (state)
2093 ((custom-show type value)
2094 (setq state 'unknown))
2095 (t
2096 (setq state 'hidden)))
2097 ;; If we don't know the state, see if we need to edit it in lisp form.
2098 (when (eq state 'unknown)
2099 (unless (widget-apply conv :match value)
2100 ;; (widget-apply (widget-convert type) :match value)
f985c5f7 2101 (setq form 'mismatch)))
d543e20b 2102 ;; Now we can create the child widget.
944c91b6 2103 (cond ((eq custom-buffer-style 'tree)
da5ec617 2104 (insert prefix (if last " `--- " " |--- "))
944c91b6 2105 (push (widget-create-child-and-convert
c953515e 2106 widget 'custom-browse-variable-tag)
944c91b6
PA
2107 buttons)
2108 (insert " " tag "\n")
2109 (widget-put widget :buttons buttons))
2110 ((eq state 'hidden)
d543e20b 2111 ;; Indicate hidden value.
d3d4df42 2112 (push (widget-create-child-and-convert
d543e20b 2113 widget 'item
3acab5ef 2114 :format "%{%t%}: "
b62c92bb 2115 :sample-face 'custom-variable-tag-face
d543e20b
PA
2116 :tag tag
2117 :parent widget)
3acab5ef 2118 buttons)
d3d4df42 2119 (push (widget-create-child-and-convert
3acab5ef 2120 widget 'visibility
8697863a 2121 :help-echo "Show the value of this option."
3acab5ef
PA
2122 :action 'custom-toggle-parent
2123 nil)
2124 buttons))
f985c5f7 2125 ((memq form '(lisp mismatch))
d543e20b
PA
2126 ;; In lisp mode edit the saved value when possible.
2127 (let* ((value (cond ((get symbol 'saved-value)
2128 (car (get symbol 'saved-value)))
25ac13b5
PA
2129 ((get symbol 'standard-value)
2130 (car (get symbol 'standard-value)))
d543e20b 2131 ((default-boundp symbol)
6d528fc5 2132 (custom-quote (funcall get symbol)))
d543e20b
PA
2133 (t
2134 (custom-quote (widget-get conv :value))))))
3acab5ef 2135 (insert (symbol-name symbol) ": ")
d3d4df42 2136 (push (widget-create-child-and-convert
944c91b6
PA
2137 widget 'visibility
2138 :help-echo "Hide the value of this option."
2139 :action 'custom-toggle-parent
2140 t)
2141 buttons)
3acab5ef 2142 (insert " ")
d3d4df42
DL
2143 (push (widget-create-child-and-convert
2144 widget 'sexp
d543e20b 2145 :button-face 'custom-variable-button-face
3acab5ef 2146 :format "%v"
d543e20b
PA
2147 :tag (symbol-name symbol)
2148 :parent widget
2149 :value value)
2150 children)))
2151 (t
2152 ;; Edit mode.
3acab5ef
PA
2153 (let* ((format (widget-get type :format))
2154 tag-format value-format)
2155 (unless (string-match ":" format)
896a6a5d 2156 (error "Bad format"))
3acab5ef
PA
2157 (setq tag-format (substring format 0 (match-end 0)))
2158 (setq value-format (substring format (match-end 0)))
2159 (push (widget-create-child-and-convert
d3d4df42 2160 widget 'item
3acab5ef
PA
2161 :format tag-format
2162 :action 'custom-tag-action
8697863a 2163 :help-echo "Change value of this option."
3acab5ef
PA
2164 :mouse-down-action 'custom-tag-mouse-down-action
2165 :button-face 'custom-variable-button-face
b62c92bb 2166 :sample-face 'custom-variable-tag-face
3acab5ef
PA
2167 tag)
2168 buttons)
2169 (insert " ")
d3d4df42 2170 (push (widget-create-child-and-convert
164cfaeb
DL
2171 widget 'visibility
2172 :help-echo "Hide the value of this option."
2173 :action 'custom-toggle-parent
2174 t)
2175 buttons)
3acab5ef 2176 (push (widget-create-child-and-convert
d3d4df42 2177 widget type
3acab5ef
PA
2178 :format value-format
2179 :value value)
2180 children))))
944c91b6 2181 (unless (eq custom-buffer-style 'tree)
944c91b6
PA
2182 (unless (eq (preceding-char) ?\n)
2183 (widget-insert "\n"))
944c91b6
PA
2184 ;; Create the magic button.
2185 (let ((magic (widget-create-child-and-convert
2186 widget 'custom-magic nil)))
2187 (widget-put widget :custom-magic magic)
2188 (push magic buttons))
164cfaeb 2189 ;; ### NOTE: this is ugly!!!! I need to update the :buttons property
d3d4df42
DL
2190 ;; before the call to `widget-default-format-handler'. Otherwise, I
2191 ;; loose my current `buttons'. This function shouldn't be called like
2192 ;; this anyway. The doc string widget should be added like the others.
2193 ;; --dv
944c91b6 2194 (widget-put widget :buttons buttons)
944c91b6
PA
2195 ;; Insert documentation.
2196 (widget-default-format-handler widget ?h)
d3d4df42
DL
2197
2198 ;; The comment field
2199 (unless (eq state 'hidden)
2200 (let* ((comment (get symbol 'variable-comment))
2201 (comment-widget
2202 (widget-create-child-and-convert
2203 widget 'custom-comment
2204 :parent widget
2205 :value (or comment ""))))
2206 (widget-put widget :comment-widget comment-widget)
2207 ;; Don't push it !!! Custom assumes that the first child is the
2208 ;; value one.
2209 (setq children (append children (list comment-widget)))))
2210 ;; Update the rest of the properties properties.
2211 (widget-put widget :custom-form form)
2212 (widget-put widget :children children)
2213 ;; Now update the state.
2214 (if (eq state 'hidden)
2215 (widget-put widget :custom-state state)
2216 (custom-variable-state-set widget))
944c91b6
PA
2217 ;; See also.
2218 (unless (eq state 'hidden)
2219 (when (eq (widget-get widget :custom-level) 1)
2220 (custom-add-parent-links widget))
2221 (custom-add-see-also widget)))))
d543e20b 2222
3acab5ef
PA
2223(defun custom-tag-action (widget &rest args)
2224 "Pass :action to first child of WIDGET's parent."
2225 (apply 'widget-apply (car (widget-get (widget-get widget :parent) :children))
2226 :action args))
2227
2228(defun custom-tag-mouse-down-action (widget &rest args)
2229 "Pass :mouse-down-action to first child of WIDGET's parent."
2230 (apply 'widget-apply (car (widget-get (widget-get widget :parent) :children))
2231 :mouse-down-action args))
2232
d543e20b
PA
2233(defun custom-variable-state-set (widget)
2234 "Set the state of WIDGET."
2235 (let* ((symbol (widget-value widget))
6d528fc5 2236 (get (or (get symbol 'custom-get) 'default-value))
d543e20b 2237 (value (if (default-boundp symbol)
6d528fc5 2238 (funcall get symbol)
d543e20b 2239 (widget-get widget :value)))
d3d4df42 2240 (comment (get symbol 'variable-comment))
d543e20b 2241 tmp
d3d4df42
DL
2242 temp
2243 (state (cond ((progn (setq tmp (get symbol 'customized-value))
2244 (setq temp
2245 (get symbol 'customized-variable-comment))
2246 (or tmp temp))
d543e20b 2247 (if (condition-case nil
d3d4df42
DL
2248 (and (equal value (eval (car tmp)))
2249 (equal comment temp))
d543e20b
PA
2250 (error nil))
2251 'set
2252 'changed))
d3d4df42
DL
2253 ((progn (setq tmp (get symbol 'saved-value))
2254 (setq temp (get symbol 'saved-variable-comment))
2255 (or tmp temp))
d543e20b 2256 (if (condition-case nil
d3d4df42
DL
2257 (and (equal value (eval (car tmp)))
2258 (equal comment temp))
d543e20b
PA
2259 (error nil))
2260 'saved
2261 'changed))
25ac13b5 2262 ((setq tmp (get symbol 'standard-value))
d543e20b 2263 (if (condition-case nil
d3d4df42
DL
2264 (and (equal value (eval (car tmp)))
2265 (equal comment nil))
d543e20b 2266 (error nil))
25ac13b5 2267 'standard
d543e20b
PA
2268 'changed))
2269 (t 'rogue))))
2270 (widget-put widget :custom-state state)))
2271
d3d4df42 2272(defvar custom-variable-menu
0f3335c0 2273 '(("Set for Current Session" custom-variable-set
6d528fc5
PA
2274 (lambda (widget)
2275 (eq (widget-get widget :custom-state) 'modified)))
0f3335c0 2276 ("Save for Future Sessions" custom-variable-save
6d528fc5
PA
2277 (lambda (widget)
2278 (memq (widget-get widget :custom-state) '(modified set changed rogue))))
2279 ("Reset to Current" custom-redraw
2280 (lambda (widget)
2281 (and (default-boundp (widget-value widget))
86bd10bc 2282 (memq (widget-get widget :custom-state) '(modified changed)))))
6d528fc5
PA
2283 ("Reset to Saved" custom-variable-reset-saved
2284 (lambda (widget)
d3d4df42
DL
2285 (and (or (get (widget-value widget) 'saved-value)
2286 (get (widget-value widget) 'saved-variable-comment))
6d528fc5
PA
2287 (memq (widget-get widget :custom-state)
2288 '(modified set changed rogue)))))
19d63704 2289 ("Erase Customization" custom-variable-reset-standard
6d528fc5 2290 (lambda (widget)
25ac13b5 2291 (and (get (widget-value widget) 'standard-value)
6d528fc5 2292 (memq (widget-get widget :custom-state)
8697863a
PA
2293 '(modified set changed saved rogue)))))
2294 ("---" ignore ignore)
d3d4df42
DL
2295 ("Add Comment" custom-comment-show custom-comment-invisible-p)
2296 ("---" ignore ignore)
2297 ("Don't show as Lisp expression" custom-variable-edit
8697863a 2298 (lambda (widget)
f985c5f7 2299 (eq (widget-get widget :custom-form) 'lisp)))
0db1ff23 2300 ("Show initial Lisp expression" custom-variable-edit-lisp
8697863a 2301 (lambda (widget)
f985c5f7 2302 (eq (widget-get widget :custom-form) 'edit))))
d543e20b 2303 "Alist of actions for the `custom-variable' widget.
6d528fc5
PA
2304Each entry has the form (NAME ACTION FILTER) where NAME is the name of
2305the menu entry, ACTION is the function to call on the widget when the
2306menu is selected, and FILTER is a predicate which takes a `custom-variable'
2307widget as an argument, and returns non-nil if ACTION is valid on that
19d63704 2308widget. If FILTER is nil, ACTION is always valid.")
d543e20b
PA
2309
2310(defun custom-variable-action (widget &optional event)
2311 "Show the menu for `custom-variable' WIDGET.
2312Optional EVENT is the location for the menu."
2313 (if (eq (widget-get widget :custom-state) 'hidden)
6d528fc5 2314 (custom-toggle-hide widget)
86bd10bc
PA
2315 (unless (eq (widget-get widget :custom-state) 'modified)
2316 (custom-variable-state-set widget))
2317 (custom-redraw-magic widget)
d543e20b 2318 (let* ((completion-ignore-case t)
25ac13b5
PA
2319 (answer (widget-choose (concat "Operation on "
2320 (custom-unlispify-tag-name
2321 (widget-get widget :value)))
6d528fc5
PA
2322 (custom-menu-filter custom-variable-menu
2323 widget)
d543e20b
PA
2324 event)))
2325 (if answer
2326 (funcall answer widget)))))
2327
2328(defun custom-variable-edit (widget)
2329 "Edit value of WIDGET."
2330 (widget-put widget :custom-state 'unknown)
2331 (widget-put widget :custom-form 'edit)
2332 (custom-redraw widget))
2333
2334(defun custom-variable-edit-lisp (widget)
2365594b 2335 "Edit the Lisp representation of the value of WIDGET."
d543e20b
PA
2336 (widget-put widget :custom-state 'unknown)
2337 (widget-put widget :custom-form 'lisp)
2338 (custom-redraw widget))
2339
2340(defun custom-variable-set (widget)
2341 "Set the current value for the variable being edited by WIDGET."
6d528fc5
PA
2342 (let* ((form (widget-get widget :custom-form))
2343 (state (widget-get widget :custom-state))
2344 (child (car (widget-get widget :children)))
2345 (symbol (widget-value widget))
2346 (set (or (get symbol 'custom-set) 'set-default))
d3d4df42
DL
2347 (comment-widget (widget-get widget :comment-widget))
2348 (comment (widget-value comment-widget))
2349 val)
d543e20b 2350 (cond ((eq state 'hidden)
896a6a5d 2351 (error "Cannot set hidden variable"))
d543e20b
PA
2352 ((setq val (widget-apply child :validate))
2353 (goto-char (widget-get val :from))
2354 (error "%s" (widget-get val :error)))
f985c5f7 2355 ((memq form '(lisp mismatch))
d3d4df42
DL
2356 (when (equal comment "")
2357 (setq comment nil)
2358 ;; Make the comment invisible by hand if it's empty
164cfaeb 2359 (custom-comment-hide comment-widget))
6d528fc5 2360 (funcall set symbol (eval (setq val (widget-value child))))
d3d4df42
DL
2361 (put symbol 'customized-value (list val))
2362 (put symbol 'variable-comment comment)
2363 (put symbol 'customized-variable-comment comment))
d543e20b 2364 (t
d3d4df42
DL
2365 (when (equal comment "")
2366 (setq comment nil)
2367 ;; Make the comment invisible by hand if it's empty
164cfaeb 2368 (custom-comment-hide comment-widget))
6d528fc5 2369 (funcall set symbol (setq val (widget-value child)))
d3d4df42
DL
2370 (put symbol 'customized-value (list (custom-quote val)))
2371 (put symbol 'variable-comment comment)
2372 (put symbol 'customized-variable-comment comment)))
d543e20b
PA
2373 (custom-variable-state-set widget)
2374 (custom-redraw-magic widget)))
2375
2376(defun custom-variable-save (widget)
0db1ff23 2377 "Set and save the value for the variable being edited by WIDGET."
6d528fc5
PA
2378 (let* ((form (widget-get widget :custom-form))
2379 (state (widget-get widget :custom-state))
2380 (child (car (widget-get widget :children)))
2381 (symbol (widget-value widget))
2382 (set (or (get symbol 'custom-set) 'set-default))
d3d4df42
DL
2383 (comment-widget (widget-get widget :comment-widget))
2384 (comment (widget-value comment-widget))
6d528fc5 2385 val)
d543e20b 2386 (cond ((eq state 'hidden)
896a6a5d 2387 (error "Cannot set hidden variable"))
d543e20b
PA
2388 ((setq val (widget-apply child :validate))
2389 (goto-char (widget-get val :from))
6b292312 2390 (error "Saving %s: %s" symbol (widget-get val :error)))
f985c5f7 2391 ((memq form '(lisp mismatch))
d3d4df42
DL
2392 (when (equal comment "")
2393 (setq comment nil)
2394 ;; Make the comment invisible by hand if it's empty
164cfaeb 2395 (custom-comment-hide comment-widget))
d543e20b 2396 (put symbol 'saved-value (list (widget-value child)))
d3d4df42
DL
2397 (funcall set symbol (eval (widget-value child)))
2398 (put symbol 'variable-comment comment)
2399 (put symbol 'saved-variable-comment comment))
d543e20b 2400 (t
d3d4df42
DL
2401 (when (equal comment "")
2402 (setq comment nil)
2403 ;; Make the comment invisible by hand if it's empty
164cfaeb 2404 (custom-comment-hide comment-widget))
d3d4df42
DL
2405 (put symbol 'saved-value
2406 (list (custom-quote (widget-value child))))
2407 (funcall set symbol (widget-value child))
2408 (put symbol 'variable-comment comment)
2409 (put symbol 'saved-variable-comment comment)))
d543e20b 2410 (put symbol 'customized-value nil)
d3d4df42 2411 (put symbol 'customized-variable-comment nil)
d543e20b
PA
2412 (custom-save-all)
2413 (custom-variable-state-set widget)
2414 (custom-redraw-magic widget)))
2415
2416(defun custom-variable-reset-saved (widget)
2417 "Restore the saved value for the variable being edited by WIDGET."
6d528fc5 2418 (let* ((symbol (widget-value widget))
d3d4df42 2419 (set (or (get symbol 'custom-set) 'set-default))
d3d4df42
DL
2420 (value (get symbol 'saved-value))
2421 (comment (get symbol 'saved-variable-comment)))
2422 (cond ((or value comment)
2423 (put symbol 'variable-comment comment)
2424 (condition-case nil
2425 (funcall set symbol (eval (car value)))
2426 (error nil)))
2427 (t
2428 (error "No saved value for %s" symbol)))
d543e20b 2429 (put symbol 'customized-value nil)
d3d4df42 2430 (put symbol 'customized-variable-comment nil)
d543e20b 2431 (widget-put widget :custom-state 'unknown)
d3d4df42 2432 ;; This call will possibly make the comment invisible
d543e20b
PA
2433 (custom-redraw widget)))
2434
25ac13b5 2435(defun custom-variable-reset-standard (widget)
19d63704
RS
2436 "Restore the standard setting for the variable being edited by WIDGET.
2437This operation eliminates any saved setting for the variable,
2438restoring it to the state of a variable that has never been customized."
6d528fc5 2439 (let* ((symbol (widget-value widget))
ba8cb52d 2440 (set (or (get symbol 'custom-set) 'set-default)))
25ac13b5
PA
2441 (if (get symbol 'standard-value)
2442 (funcall set symbol (eval (car (get symbol 'standard-value))))
5dd0cad0 2443 (error "No standard setting known for %S" symbol))
164cfaeb 2444 (put symbol 'variable-comment nil)
d543e20b 2445 (put symbol 'customized-value nil)
d3d4df42
DL
2446 (put symbol 'customized-variable-comment nil)
2447 (when (or (get symbol 'saved-value) (get symbol 'saved-variable-comment))
d543e20b 2448 (put symbol 'saved-value nil)
d3d4df42 2449 (put symbol 'saved-variable-comment nil)
d543e20b
PA
2450 (custom-save-all))
2451 (widget-put widget :custom-state 'unknown)
d3d4df42 2452 ;; This call will possibly make the comment invisible
d543e20b
PA
2453 (custom-redraw widget)))
2454
2455;;; The `custom-face-edit' Widget.
2456
2457(define-widget 'custom-face-edit 'checklist
2458 "Edit face attributes."
2459 :format "%t: %v"
2460 :tag "Attributes"
2461 :extra-offset 12
d3d4df42 2462 :button-args '(:help-echo "Control whether this attribute has any effect.")
d75fa08f
RS
2463 :value-to-internal 'custom-face-edit-fix-value
2464 :match (lambda (widget value)
2465 (widget-checklist-match widget
2466 (custom-face-edit-fix-value widget value)))
3ea051cb 2467 :convert-widget 'custom-face-edit-convert-widget
d543e20b 2468 :args (mapcar (lambda (att)
d3d4df42 2469 (list 'group
d543e20b
PA
2470 :inline t
2471 :sibling-args (widget-get (nth 1 att) :sibling-args)
d3d4df42 2472 (list 'const :format "" :value (nth 0 att))
d543e20b
PA
2473 (nth 1 att)))
2474 custom-face-attributes))
2475
d75fa08f
RS
2476(defun custom-face-edit-fix-value (widget value)
2477 "Ignoring WIDGET, convert :bold and :italic in VALUE to new form."
2478 (let (result)
2479 (while value
d75fa08f
RS
2480 (let ((key (car value))
2481 (val (car (cdr value))))
2482 (cond ((eq key :italic)
2483 (push :slant result)
2484 (push (if val 'italic 'normal) result))
2485 ((eq key :bold)
2486 (push :weight result)
2487 (push (if val 'bold 'normal) result))
2488 (t
2489 (push key result)
2490 (push val result))))
2491 (setq value (cdr (cdr value))))
2492 (setq result (nreverse result))
2493 result))
2494
3ea051cb
MB
2495(defun custom-face-edit-convert-widget (widget)
2496 "Convert :args as widget types in WIDGET."
2497 (widget-put
2498 widget
2499 :args (mapcar (lambda (arg)
2500 (widget-convert arg
2501 :deactivate 'custom-face-edit-deactivate
2502 :activate 'custom-face-edit-activate
2503 :delete 'custom-face-edit-delete))
2504 (widget-get widget :args)))
2505 widget)
2506
2507(defun custom-face-edit-deactivate (widget)
2508 "Make face widget WIDGET inactive for user modifications."
2509 (unless (widget-get widget :inactive)
2510 (let ((tag (custom-face-edit-attribute-tag widget))
2511 (from (copy-marker (widget-get widget :from)))
2512 (to (widget-get widget :to))
2513 (value (widget-value widget))
2514 (inhibit-read-only t)
2515 (inhibit-modification-hooks t))
2516 (save-excursion
2517 (goto-char from)
2518 (widget-default-delete widget)
2519 (insert tag ": *\n")
2520 (widget-put widget :inactive
2521 (cons value (cons from (- (point) from))))))))
2522
2523(defun custom-face-edit-activate (widget)
2524 "Make face widget WIDGET inactive for user modifications."
2525 (let ((inactive (widget-get widget :inactive))
2526 (inhibit-read-only t)
2527 (inhibit-modification-hooks t))
2528 (when (consp inactive)
2529 (save-excursion
2530 (goto-char (car (cdr inactive)))
2531 (delete-region (point) (+ (point) (cdr (cdr inactive))))
2532 (widget-put widget :inactive nil)
2533 (widget-apply widget :create)
2534 (widget-value-set widget (car inactive))
2535 (widget-setup)))))
2536
2537(defun custom-face-edit-delete (widget)
2538 "Remove widget from the buffer."
2539 (let ((inactive (widget-get widget :inactive))
2540 (inhibit-read-only t)
2541 (inhibit-modification-hooks t))
2542 (if (not inactive)
2543 ;; Widget is alive, we don't have to do anything special
2544 (widget-default-delete widget)
2545 ;; WIDGET is already deleted because we did so to inactivate it;
2546 ;; now just get rid of the label we put in its place.
2547 (delete-region (car (cdr inactive))
2548 (+ (car (cdr inactive)) (cdr (cdr inactive))))
2549 (widget-put widget :inactive nil))))
2550
2551
2552(defun custom-face-edit-attribute-tag (widget)
2553 "Returns the first :tag property in WIDGET or one of its children."
2554 (let ((tag (widget-get widget :tag)))
2555 (or (and (not (equal tag "")) tag)
2556 (let ((children (widget-get widget :children)))
2557 (while (and (null tag) children)
2558 (setq tag (custom-face-edit-attribute-tag (pop children))))
2559 tag))))
2560
d543e20b
PA
2561;;; The `custom-display' Widget.
2562
2563(define-widget 'custom-display 'menu-choice
2564 "Select a display type."
2565 :tag "Display"
2566 :value t
2567 :help-echo "Specify frames where the face attributes should be used."
2568 :args '((const :tag "all" t)
2569 (checklist
2570 :offset 0
2571 :extra-offset 9
2572 :args ((group :sibling-args (:help-echo "\
2573Only match the specified window systems.")
2574 (const :format "Type: "
2575 type)
2576 (checklist :inline t
2577 :offset 0
2578 (const :format "X "
2579 :sibling-args (:help-echo "\
2580The X11 Window System.")
2581 x)
2582 (const :format "PM "
2583 :sibling-args (:help-echo "\
2584OS/2 Presentation Manager.")
2585 pm)
b97aca27 2586 (const :format "W32 "
d543e20b 2587 :sibling-args (:help-echo "\
b97aca27
GV
2588Windows NT/9X.")
2589 w32)
d543e20b
PA
2590 (const :format "DOS "
2591 :sibling-args (:help-echo "\
2592Plain MS-DOS.")
2593 pc)
2594 (const :format "TTY%n"
2595 :sibling-args (:help-echo "\
2596Plain text terminals.")
2597 tty)))
2598 (group :sibling-args (:help-echo "\
2599Only match the frames with the specified color support.")
2600 (const :format "Class: "
2601 class)
2602 (checklist :inline t
2603 :offset 0
2604 (const :format "Color "
2605 :sibling-args (:help-echo "\
2606Match color frames.")
2607 color)
2608 (const :format "Grayscale "
2609 :sibling-args (:help-echo "\
2610Match grayscale frames.")
2611 grayscale)
2612 (const :format "Monochrome%n"
2613 :sibling-args (:help-echo "\
2614Match frames with no color support.")
2615 mono)))
2616 (group :sibling-args (:help-echo "\
2617Only match frames with the specified intensity.")
2618 (const :format "\
2619Background brightness: "
2620 background)
2621 (checklist :inline t
2622 :offset 0
2623 (const :format "Light "
2624 :sibling-args (:help-echo "\
2625Match frames with light backgrounds.")
2626 light)
2627 (const :format "Dark\n"
2628 :sibling-args (:help-echo "\
2629Match frames with dark backgrounds.")
2630 dark)))))))
2631
2632;;; The `custom-face' Widget.
2633
16b20ed9 2634(defface custom-face-tag-face
b5555381 2635 `((t (:weight bold :height 1.2 :inherit variable-pitch)))
d543e20b 2636 "Face used for face tags."
bd042c03 2637 :group 'custom-faces)
d543e20b 2638
d64478da
KH
2639(defcustom custom-face-default-form 'selected
2640 "Default form of displaying face definition."
2641 :type '(choice (const all)
2642 (const selected)
2643 (const lisp))
cd32a7ba
DN
2644 :group 'custom-buffer
2645 :version "20.3")
d64478da 2646
d543e20b
PA
2647(define-widget 'custom-face 'custom
2648 "Customize face."
d543e20b
PA
2649 :sample-face 'custom-face-tag-face
2650 :help-echo "Set or reset this face."
23c0fb21 2651 :documentation-property #'face-doc-string
d543e20b
PA
2652 :value-create 'custom-face-value-create
2653 :action 'custom-face-action
9097aeb7 2654 :custom-category 'face
d64478da 2655 :custom-form nil ; defaults to value of `custom-face-default-form'
d543e20b
PA
2656 :custom-set 'custom-face-set
2657 :custom-save 'custom-face-save
2658 :custom-reset-current 'custom-redraw
2659 :custom-reset-saved 'custom-face-reset-saved
25ac13b5 2660 :custom-reset-standard 'custom-face-reset-standard
d543e20b
PA
2661 :custom-menu 'custom-face-menu-create)
2662
d3d4df42 2663(define-widget 'custom-face-all 'editable-list
d543e20b
PA
2664 "An editable list of display specifications and attributes."
2665 :entry-format "%i %d %v"
2666 :insert-button-args '(:help-echo "Insert new display specification here.")
2667 :append-button-args '(:help-echo "Append new display specification here.")
2668 :delete-button-args '(:help-echo "Delete this display specification.")
2669 :args '((group :format "%v" custom-display custom-face-edit)))
2670
2671(defconst custom-face-all (widget-convert 'custom-face-all)
2672 "Converted version of the `custom-face-all' widget.")
2673
2674(define-widget 'custom-display-unselected 'item
2675 "A display specification that doesn't match the selected display."
2676 :match 'custom-display-unselected-match)
2677
2678(defun custom-display-unselected-match (widget value)
2679 "Non-nil if VALUE is an unselected display specification."
86bd10bc 2680 (not (face-spec-set-match-display value (selected-frame))))
d543e20b 2681
d3d4df42 2682(define-widget 'custom-face-selected 'group
d543e20b
PA
2683 "Edit the attributes of the selected display in a face specification."
2684 :args '((repeat :format ""
2685 :inline t
2686 (group custom-display-unselected sexp))
2687 (group (sexp :format "") custom-face-edit)
2688 (repeat :format ""
2689 :inline t
2690 sexp)))
2691
2692(defconst custom-face-selected (widget-convert 'custom-face-selected)
2693 "Converted version of the `custom-face-selected' widget.")
2694
3ea051cb 2695(defun custom-filter-face-spec (spec filter-index &optional default-filter)
f5b50baa
MB
2696 "Return a canonicalized version of SPEC using.
2697FILTER-INDEX is the index in the entry for each attribute in
2698`custom-face-attributes' at which the appropriate filter function can be
2699found, and DEFAULT-FILTER is the filter to apply for attributes that
2700don't specify one."
2701 (mapcar (lambda (entry)
2702 ;; Filter a single face-spec entry
2703 (let ((tests (car entry))
2704 (unfiltered-attrs
2705 ;; Handle both old- and new-style attribute syntax
2706 (if (listp (car (cdr entry)))
2707 (car (cdr entry))
2708 (cdr entry)))
2709 (filtered-attrs nil))
2710 ;; Filter each face attribute
2711 (while unfiltered-attrs
2712 (let* ((attr (pop unfiltered-attrs))
2713 (pre-filtered-value (pop unfiltered-attrs))
2714 (filter
2715 (or (nth filter-index (assq attr custom-face-attributes))
2716 default-filter))
2717 (filtered-value
2718 (if filter
2719 (funcall filter pre-filtered-value)
2720 pre-filtered-value)))
2721 (push filtered-value filtered-attrs)
2722 (push attr filtered-attrs)))
2723 ;;
2724 (list tests filtered-attrs)))
2725 spec))
2726
2727(defun custom-pre-filter-face-spec (spec)
2728 "Return SPEC changed as necessary for editing by the face customization widget.
2729SPEC must be a full face spec."
3ea051cb 2730 (custom-filter-face-spec spec 2))
f5b50baa
MB
2731
2732(defun custom-post-filter-face-spec (spec)
2733 "Return the customized SPEC in a form suitable for setting the face."
3ea051cb 2734 (custom-filter-face-spec spec 3))
f5b50baa 2735
d543e20b 2736(defun custom-face-value-create (widget)
944c91b6
PA
2737 "Create a list of the display specifications for WIDGET."
2738 (let ((buttons (widget-get widget :buttons))
d3d4df42 2739 children
944c91b6
PA
2740 (symbol (widget-get widget :value))
2741 (tag (widget-get widget :tag))
2742 (state (widget-get widget :custom-state))
2743 (begin (point))
2744 (is-last (widget-get widget :custom-last))
2745 (prefix (widget-get widget :custom-prefix)))
2746 (unless tag
2747 (setq tag (prin1-to-string symbol)))
2748 (cond ((eq custom-buffer-style 'tree)
da5ec617 2749 (insert prefix (if is-last " `--- " " |--- "))
944c91b6 2750 (push (widget-create-child-and-convert
c953515e 2751 widget 'custom-browse-face-tag)
944c91b6
PA
2752 buttons)
2753 (insert " " tag "\n")
2754 (widget-put widget :buttons buttons))
2755 (t
2756 ;; Create tag.
2757 (insert tag)
c069a9d3 2758 (widget-specify-sample widget begin (point))
944c91b6
PA
2759 (if (eq custom-buffer-style 'face)
2760 (insert " ")
a62ebc52
MB
2761 (if (string-match "face\\'" tag)
2762 (insert ":")
2763 (insert " face: ")))
944c91b6 2764 ;; Sample.
944c91b6
PA
2765 (push (widget-create-child-and-convert widget 'item
2766 :format "(%{%t%})"
2767 :sample-face symbol
2768 :tag "sample")
2769 buttons)
2770 ;; Visibility.
2771 (insert " ")
d3d4df42 2772 (push (widget-create-child-and-convert
944c91b6
PA
2773 widget 'visibility
2774 :help-echo "Hide or show this face."
2775 :action 'custom-toggle-parent
2776 (not (eq state 'hidden)))
2777 buttons)
2778 ;; Magic.
2779 (insert "\n")
2780 (let ((magic (widget-create-child-and-convert
2781 widget 'custom-magic nil)))
2782 (widget-put widget :custom-magic magic)
2783 (push magic buttons))
2784 ;; Update buttons.
2785 (widget-put widget :buttons buttons)
2786 ;; Insert documentation.
2787 (widget-default-format-handler widget ?h)
d3d4df42
DL
2788 ;; The comment field
2789 (unless (eq state 'hidden)
2790 (let* ((comment (get symbol 'face-comment))
2791 (comment-widget
2792 (widget-create-child-and-convert
2793 widget 'custom-comment
2794 :parent widget
2795 :value (or comment ""))))
2796 (widget-put widget :comment-widget comment-widget)
2797 (push comment-widget children)))
944c91b6
PA
2798 ;; See also.
2799 (unless (eq state 'hidden)
2800 (when (eq (widget-get widget :custom-level) 1)
2801 (custom-add-parent-links widget))
2802 (custom-add-see-also widget))
2803 ;; Editor.
2804 (unless (eq (preceding-char) ?\n)
2805 (insert "\n"))
2806 (unless (eq state 'hidden)
2807 (message "Creating face editor...")
2808 (custom-load-widget widget)
d64478da
KH
2809 (unless (widget-get widget :custom-form)
2810 (widget-put widget :custom-form custom-face-default-form))
944c91b6 2811 (let* ((symbol (widget-value widget))
61763509
PA
2812 (spec (or (get symbol 'customized-face)
2813 (get symbol 'saved-face)
944c91b6
PA
2814 (get symbol 'face-defface-spec)
2815 ;; Attempt to construct it.
d3d4df42 2816 (list (list t (custom-face-attributes-get
944c91b6
PA
2817 symbol (selected-frame))))))
2818 (form (widget-get widget :custom-form))
2819 (indent (widget-get widget :indent))
fa0b3d46
RS
2820 edit)
2821 ;; If the user has changed this face in some other way,
2822 ;; edit it as the user has specified it.
2823 (if (not (face-spec-match-p symbol spec (selected-frame)))
2824 (setq spec (list (list t (face-attr-construct symbol (selected-frame))))))
f5b50baa 2825 (setq spec (custom-pre-filter-face-spec spec))
fa0b3d46 2826 (setq edit (widget-create-child-and-convert
944c91b6
PA
2827 widget
2828 (cond ((and (eq form 'selected)
d3d4df42 2829 (widget-apply custom-face-selected
944c91b6
PA
2830 :match spec))
2831 (when indent (insert-char ?\ indent))
2832 'custom-face-selected)
2833 ((and (not (eq form 'lisp))
2834 (widget-apply custom-face-all
2835 :match spec))
2836 'custom-face-all)
d3d4df42 2837 (t
944c91b6
PA
2838 (when indent (insert-char ?\ indent))
2839 'sexp))
fa0b3d46 2840 :value spec))
944c91b6 2841 (custom-face-state-set widget)
d3d4df42
DL
2842 (push edit children)
2843 (widget-put widget :children children))
944c91b6 2844 (message "Creating face editor...done"))))))
d543e20b 2845
d3d4df42 2846(defvar custom-face-menu
3aec85bf 2847 '(("Set for Current Session" custom-face-set)
896a6a5d 2848 ("Save for Future Sessions" custom-face-save-command)
6d528fc5
PA
2849 ("Reset to Saved" custom-face-reset-saved
2850 (lambda (widget)
d3d4df42
DL
2851 (or (get (widget-value widget) 'saved-face)
2852 (get (widget-value widget) 'saved-face-comment))))
19d63704 2853 ("Erase Customization" custom-face-reset-standard
6d528fc5 2854 (lambda (widget)
8697863a
PA
2855 (get (widget-value widget) 'face-defface-spec)))
2856 ("---" ignore ignore)
d3d4df42
DL
2857 ("Add Comment" custom-comment-show custom-comment-invisible-p)
2858 ("---" ignore ignore)
8697863a
PA
2859 ("Show all display specs" custom-face-edit-all
2860 (lambda (widget)
2861 (not (eq (widget-get widget :custom-form) 'all))))
2862 ("Just current attributes" custom-face-edit-selected
2863 (lambda (widget)
2864 (not (eq (widget-get widget :custom-form) 'selected))))
a84ff57a 2865 ("Show as Lisp expression" custom-face-edit-lisp
8697863a
PA
2866 (lambda (widget)
2867 (not (eq (widget-get widget :custom-form) 'lisp)))))
d543e20b 2868 "Alist of actions for the `custom-face' widget.
6d528fc5
PA
2869Each entry has the form (NAME ACTION FILTER) where NAME is the name of
2870the menu entry, ACTION is the function to call on the widget when the
2871menu is selected, and FILTER is a predicate which takes a `custom-face'
2872widget as an argument, and returns non-nil if ACTION is valid on that
19d63704 2873widget. If FILTER is nil, ACTION is always valid.")
d543e20b
PA
2874
2875(defun custom-face-edit-selected (widget)
2876 "Edit selected attributes of the value of WIDGET."
2877 (widget-put widget :custom-state 'unknown)
2878 (widget-put widget :custom-form 'selected)
2879 (custom-redraw widget))
2880
2881(defun custom-face-edit-all (widget)
2882 "Edit all attributes of the value of WIDGET."
2883 (widget-put widget :custom-state 'unknown)
2884 (widget-put widget :custom-form 'all)
2885 (custom-redraw widget))
2886
2887(defun custom-face-edit-lisp (widget)
2365594b 2888 "Edit the Lisp representation of the value of WIDGET."
d543e20b
PA
2889 (widget-put widget :custom-state 'unknown)
2890 (widget-put widget :custom-form 'lisp)
2891 (custom-redraw widget))
2892
2893(defun custom-face-state-set (widget)
2894 "Set the state of WIDGET."
d3d4df42
DL
2895 (let* ((symbol (widget-value widget))
2896 (comment (get symbol 'face-comment))
2897 tmp temp)
2898 (widget-put widget :custom-state
2899 (cond ((progn
2900 (setq tmp (get symbol 'customized-face))
2901 (setq temp (get symbol 'customized-face-comment))
2902 (or tmp temp))
2903 (if (equal temp comment)
2904 'set
2905 'changed))
2906 ((progn
2907 (setq tmp (get symbol 'saved-face))
2908 (setq temp (get symbol 'saved-face-comment))
2909 (or tmp temp))
2910 (if (equal temp comment)
2911 'saved
2912 'changed))
2913 ((get symbol 'face-defface-spec)
2914 (if (equal comment nil)
2915 'standard
2916 'changed))
2917 (t
2918 'rogue)))))
d543e20b
PA
2919
2920(defun custom-face-action (widget &optional event)
2921 "Show the menu for `custom-face' WIDGET.
2922Optional EVENT is the location for the menu."
2923 (if (eq (widget-get widget :custom-state) 'hidden)
6d528fc5 2924 (custom-toggle-hide widget)
d543e20b
PA
2925 (let* ((completion-ignore-case t)
2926 (symbol (widget-get widget :value))
25ac13b5
PA
2927 (answer (widget-choose (concat "Operation on "
2928 (custom-unlispify-tag-name symbol))
6d528fc5
PA
2929 (custom-menu-filter custom-face-menu
2930 widget)
2931 event)))
d543e20b
PA
2932 (if answer
2933 (funcall answer widget)))))
2934
2935(defun custom-face-set (widget)
2936 "Make the face attributes in WIDGET take effect."
2937 (let* ((symbol (widget-value widget))
2938 (child (car (widget-get widget :children)))
f5b50baa 2939 (value (custom-post-filter-face-spec (widget-value child)))
d3d4df42
DL
2940 (comment-widget (widget-get widget :comment-widget))
2941 (comment (widget-value comment-widget)))
2942 (when (equal comment "")
2943 (setq comment nil)
2944 ;; Make the comment invisible by hand if it's empty
164cfaeb 2945 (custom-comment-hide comment-widget))
d543e20b 2946 (put symbol 'customized-face value)
f5b50baa
MB
2947 (if (face-spec-choose value)
2948 (face-spec-set symbol value)
2949 ;; face-set-spec ignores empty attribute lists, so just give it
2950 ;; something harmless instead.
2951 (face-spec-set symbol '((t :foreground unspecified))))
d3d4df42
DL
2952 (put symbol 'customized-face-comment comment)
2953 (put symbol 'face-comment comment)
d543e20b
PA
2954 (custom-face-state-set widget)
2955 (custom-redraw-magic widget)))
2956
896a6a5d
RS
2957(defun custom-face-save-command (widget)
2958 "Save in `.emacs' the face attributes in WIDGET."
2959 (custom-face-save widget)
2960 (custom-save-all))
2961
d543e20b 2962(defun custom-face-save (widget)
896a6a5d 2963 "Prepare for saving WIDGET's face attributes, but don't write `.emacs'."
d543e20b
PA
2964 (let* ((symbol (widget-value widget))
2965 (child (car (widget-get widget :children)))
e475612a 2966 (value (custom-post-filter-face-spec (widget-value child)))
d3d4df42
DL
2967 (comment-widget (widget-get widget :comment-widget))
2968 (comment (widget-value comment-widget)))
2969 (when (equal comment "")
2970 (setq comment nil)
2971 ;; Make the comment invisible by hand if it's empty
164cfaeb 2972 (custom-comment-hide comment-widget))
e475612a
MB
2973 (if (face-spec-choose value)
2974 (face-spec-set symbol value)
2975 ;; face-set-spec ignores empty attribute lists, so just give it
2976 ;; something harmless instead.
2977 (face-spec-set symbol '((t :foreground unspecified))))
d543e20b
PA
2978 (put symbol 'saved-face value)
2979 (put symbol 'customized-face nil)
d3d4df42
DL
2980 (put symbol 'face-comment comment)
2981 (put symbol 'customized-face-comment nil)
2982 (put symbol 'saved-face-comment comment)
6321bddd 2983 (custom-save-all)
d543e20b
PA
2984 (custom-face-state-set widget)
2985 (custom-redraw-magic widget)))
2986
2987(defun custom-face-reset-saved (widget)
2988 "Restore WIDGET to the face's default attributes."
2989 (let* ((symbol (widget-value widget))
2990 (child (car (widget-get widget :children)))
d3d4df42
DL
2991 (value (get symbol 'saved-face))
2992 (comment (get symbol 'saved-face-comment))
2993 (comment-widget (widget-get widget :comment-widget)))
2994 (unless (or value comment)
d543e20b
PA
2995 (error "No saved value for this face"))
2996 (put symbol 'customized-face nil)
d3d4df42 2997 (put symbol 'customized-face-comment nil)
25ac13b5 2998 (face-spec-set symbol value)
d3d4df42 2999 (put symbol 'face-comment comment)
d543e20b 3000 (widget-value-set child value)
d3d4df42
DL
3001 ;; This call manages the comment visibility
3002 (widget-value-set comment-widget (or comment ""))
d543e20b
PA
3003 (custom-face-state-set widget)
3004 (custom-redraw-magic widget)))
3005
25ac13b5 3006(defun custom-face-reset-standard (widget)
19d63704
RS
3007 "Restore WIDGET to the face's standard settings.
3008This operation eliminates any saved setting for the face,
3009restoring it to the state of a face that has never been customized."
d543e20b
PA
3010 (let* ((symbol (widget-value widget))
3011 (child (car (widget-get widget :children)))
d3d4df42
DL
3012 (value (get symbol 'face-defface-spec))
3013 (comment-widget (widget-get widget :comment-widget)))
d543e20b 3014 (unless value
5dd0cad0 3015 (error "No standard setting for this face"))
d543e20b 3016 (put symbol 'customized-face nil)
d3d4df42
DL
3017 (put symbol 'customized-face-comment nil)
3018 (when (or (get symbol 'saved-face) (get symbol 'saved-face-comment))
d543e20b 3019 (put symbol 'saved-face nil)
d3d4df42 3020 (put symbol 'saved-face-comment nil)
d543e20b 3021 (custom-save-all))
25ac13b5 3022 (face-spec-set symbol value)
d3d4df42 3023 (put symbol 'face-comment nil)
d543e20b 3024 (widget-value-set child value)
d3d4df42
DL
3025 ;; This call manages the comment visibility
3026 (widget-value-set comment-widget "")
d543e20b
PA
3027 (custom-face-state-set widget)
3028 (custom-redraw-magic widget)))
3029
3030;;; The `face' Widget.
3031
3032(define-widget 'face 'default
3033 "Select and customize a face."
86bd10bc 3034 :convert-widget 'widget-value-convert-widget
944c91b6
PA
3035 :button-prefix 'widget-push-button-prefix
3036 :button-suffix 'widget-push-button-suffix
9c6a4107 3037 :format "%{%t%}: %[select face%] %v"
d543e20b
PA
3038 :tag "Face"
3039 :value 'default
3040 :value-create 'widget-face-value-create
3041 :value-delete 'widget-face-value-delete
86bd10bc
PA
3042 :value-get 'widget-value-value-get
3043 :validate 'widget-children-validate
d543e20b 3044 :action 'widget-face-action
8cfd634f 3045 :match (lambda (widget value) (symbolp value)))
d543e20b
PA
3046
3047(defun widget-face-value-create (widget)
2365594b 3048 "Create a `custom-face' child."
d543e20b 3049 (let* ((symbol (widget-value widget))
944c91b6 3050 (custom-buffer-style 'face)
d543e20b
PA
3051 (child (widget-create-child-and-convert
3052 widget 'custom-face
d543e20b
PA
3053 :custom-level nil
3054 :value symbol)))
3055 (custom-magic-reset child)
3056 (setq custom-options (cons child custom-options))
3057 (widget-put widget :children (list child))))
3058
3059(defun widget-face-value-delete (widget)
2365594b 3060 "Remove the child from the options."
d543e20b
PA
3061 (let ((child (car (widget-get widget :children))))
3062 (setq custom-options (delq child custom-options))
3063 (widget-children-value-delete widget)))
3064
3065(defvar face-history nil
3066 "History of entered face names.")
3067
3068(defun widget-face-action (widget &optional event)
3069 "Prompt for a face."
3070 (let ((answer (completing-read "Face: "
3071 (mapcar (lambda (face)
3072 (list (symbol-name face)))
3073 (face-list))
d3d4df42 3074 nil nil nil
d543e20b
PA
3075 'face-history)))
3076 (unless (zerop (length answer))
3077 (widget-value-set widget (intern answer))
3078 (widget-apply widget :notify widget event)
3079 (widget-setup))))
3080
3081;;; The `hook' Widget.
3082
3083(define-widget 'hook 'list
3084 "A emacs lisp hook"
f985c5f7 3085 :value-to-internal (lambda (widget value)
5aa3f181 3086 (if (and value (symbolp value))
f985c5f7
PA
3087 (list value)
3088 value))
3089 :match (lambda (widget value)
3090 (or (symbolp value)
4743fc91 3091 (widget-group-match widget value)))
2365594b
DL
3092 ;; Avoid adding undefined functions to the hook, especially for
3093 ;; things like `find-file-hook' or even more basic ones, to avoid
3094 ;; chaos.
3095 :set (lambda (symbol value)
d4881668
SM
3096 (dolist (elt value)
3097 (if (fboundp elt)
3098 (add-hook symbol elt))))
d543e20b
PA
3099 :convert-widget 'custom-hook-convert-widget
3100 :tag "Hook")
3101
3102(defun custom-hook-convert-widget (widget)
3c708e98 3103 ;; Handle `:options'.
d543e20b 3104 (let* ((options (widget-get widget :options))
d3d4df42 3105 (other `(editable-list :inline t
d543e20b
PA
3106 :entry-format "%i %d%v"
3107 (function :format " %v")))
3108 (args (if options
3109 (list `(checklist :inline t
3110 ,@(mapcar (lambda (entry)
3111 `(function-item ,entry))
3112 options))
3113 other)
3114 (list other))))
3115 (widget-put widget :args args)
3116 widget))
3117
944c91b6
PA
3118;;; The `custom-group-link' Widget.
3119
3120(define-widget 'custom-group-link 'link
3121 "Show parent in other window when activated."
b62c92bb 3122 :help-echo "Create customization buffer for this group."
944c91b6
PA
3123 :action 'custom-group-link-action)
3124
3125(defun custom-group-link-action (widget &rest ignore)
3126 (customize-group (widget-value widget)))
3127
d543e20b
PA
3128;;; The `custom-group' Widget.
3129
b62c92bb 3130(defcustom custom-group-tag-faces nil
d543e20b 3131 ;; In XEmacs, this ought to play games with font size.
d3d4df42 3132 ;; Fixme: make it do so in Emacs.
d543e20b
PA
3133 "Face used for group tags.
3134The first member is used for level 1 groups, the second for level 2,
3135and so forth. The remaining group tags are shown with
3136`custom-group-tag-face'."
3137 :type '(repeat face)
bd042c03 3138 :group 'custom-faces)
d543e20b 3139
16b20ed9
GM
3140(defface custom-group-tag-face-1
3141 `((((class color)
3142 (background dark))
b5555381 3143 (:foreground "pink" :weight bold :height 1.2 :inherit variable-pitch))
16b20ed9
GM
3144 (((class color)
3145 (background light))
b5555381
RS
3146 (:foreground "red" :weight bold :height 1.2 :inherit variable-pitch))
3147 (t (:weight bold)))
16b20ed9
GM
3148 "Face used for group tags."
3149 :group 'custom-faces)
3150
3151(defface custom-group-tag-face
3152 `((((class color)
3153 (background dark))
b5555381 3154 (:foreground "light blue" :weight bold :height 1.2))
16b20ed9
GM
3155 (((class color)
3156 (background light))
b5555381
RS
3157 (:foreground "blue" :weight bold :height 1.2))
3158 (t (:weight bold)))
d543e20b 3159 "Face used for low level group tags."
bd042c03 3160 :group 'custom-faces)
d543e20b
PA
3161
3162(define-widget 'custom-group 'custom
3163 "Customize group."
944c91b6 3164 :format "%v"
d543e20b
PA
3165 :sample-face-get 'custom-group-sample-face-get
3166 :documentation-property 'group-documentation
3167 :help-echo "Set or reset all members of this group."
3168 :value-create 'custom-group-value-create
3169 :action 'custom-group-action
9097aeb7 3170 :custom-category 'group
d543e20b
PA
3171 :custom-set 'custom-group-set
3172 :custom-save 'custom-group-save
3173 :custom-reset-current 'custom-group-reset-current
3174 :custom-reset-saved 'custom-group-reset-saved
25ac13b5 3175 :custom-reset-standard 'custom-group-reset-standard
d543e20b
PA
3176 :custom-menu 'custom-group-menu-create)
3177
3178(defun custom-group-sample-face-get (widget)
3179 ;; Use :sample-face.
3180 (or (nth (1- (widget-get widget :custom-level)) custom-group-tag-faces)
3181 'custom-group-tag-face))
3182
8691cfa7
RS
3183(define-widget 'custom-group-visibility 'visibility
3184 "An indicator and manipulator for hidden group contents."
3185 :create 'custom-group-visibility-create)
3186
3187(defun custom-group-visibility-create (widget)
3188 (let ((visible (widget-value widget)))
3189 (if visible
3190 (insert "--------")))
3191 (widget-default-create widget))
3192
4ee1cf9f
PA
3193(defun custom-group-members (symbol groups-only)
3194 "Return SYMBOL's custom group members.
3195If GROUPS-ONLY non-nil, return only those members that are groups."
3196 (if (not groups-only)
3197 (get symbol 'custom-group)
3198 (let (members)
3199 (dolist (entry (get symbol 'custom-group))
3200 (when (eq (nth 1 entry) 'custom-group)
3201 (push entry members)))
3202 (nreverse members))))
3203
d543e20b 3204(defun custom-group-value-create (widget)
944c91b6 3205 "Insert a customize group for WIDGET in the current buffer."
4ee1cf9f
PA
3206 (let* ((state (widget-get widget :custom-state))
3207 (level (widget-get widget :custom-level))
f985c5f7 3208 ;; (indent (widget-get widget :indent))
4ee1cf9f
PA
3209 (prefix (widget-get widget :custom-prefix))
3210 (buttons (widget-get widget :buttons))
3211 (tag (widget-get widget :tag))
3212 (symbol (widget-value widget))
3213 (members (custom-group-members symbol
3214 (and (eq custom-buffer-style 'tree)
3215 custom-browse-only-groups))))
944c91b6 3216 (cond ((and (eq custom-buffer-style 'tree)
c953515e 3217 (eq state 'hidden)
4ee1cf9f 3218 (or members (custom-unloaded-widget-p widget)))
c953515e 3219 (custom-browse-insert-prefix prefix)
944c91b6 3220 (push (widget-create-child-and-convert
d3d4df42 3221 widget 'custom-browse-visibility
da5ec617 3222 ;; :tag-glyph "plus"
df816618 3223 :tag "+")
944c91b6
PA
3224 buttons)
3225 (insert "-- ")
da5ec617 3226 ;; (widget-glyph-insert nil "-- " "horizontal")
944c91b6 3227 (push (widget-create-child-and-convert
c953515e 3228 widget 'custom-browse-group-tag)
944c91b6
PA
3229 buttons)
3230 (insert " " tag "\n")
3231 (widget-put widget :buttons buttons))
3232 ((and (eq custom-buffer-style 'tree)
4ee1cf9f 3233 (zerop (length members)))
c953515e 3234 (custom-browse-insert-prefix prefix)
da5ec617
PA
3235 (insert "[ ]-- ")
3236 ;; (widget-glyph-insert nil "[ ]" "empty")
3237 ;; (widget-glyph-insert nil "-- " "horizontal")
d3d4df42 3238 (push (widget-create-child-and-convert
c953515e 3239 widget 'custom-browse-group-tag)
944c91b6
PA
3240 buttons)
3241 (insert " " tag "\n")
3242 (widget-put widget :buttons buttons))
3243 ((eq custom-buffer-style 'tree)
c953515e 3244 (custom-browse-insert-prefix prefix)
944c91b6 3245 (custom-load-widget widget)
4ee1cf9f 3246 (if (zerop (length members))
d3d4df42 3247 (progn
c953515e 3248 (custom-browse-insert-prefix prefix)
da5ec617
PA
3249 (insert "[ ]-- ")
3250 ;; (widget-glyph-insert nil "[ ]" "empty")
3251 ;; (widget-glyph-insert nil "-- " "horizontal")
d3d4df42 3252 (push (widget-create-child-and-convert
c953515e 3253 widget 'custom-browse-group-tag)
944c91b6
PA
3254 buttons)
3255 (insert " " tag "\n")
3256 (widget-put widget :buttons buttons))
d3d4df42
DL
3257 (push (widget-create-child-and-convert
3258 widget 'custom-browse-visibility
da5ec617
PA
3259 ;; :tag-glyph "minus"
3260 :tag "-")
944c91b6 3261 buttons)
da5ec617
PA
3262 (insert "-\\ ")
3263 ;; (widget-glyph-insert nil "-\\ " "top")
d3d4df42 3264 (push (widget-create-child-and-convert
c953515e 3265 widget 'custom-browse-group-tag)
944c91b6
PA
3266 buttons)
3267 (insert " " tag "\n")
3268 (widget-put widget :buttons buttons)
3269 (message "Creating group...")
4ee1cf9f 3270 (let* ((members (custom-sort-items members
da5ec617
PA
3271 custom-browse-sort-alphabetically
3272 custom-browse-order-groups))
944c91b6
PA
3273 (prefixes (widget-get widget :custom-prefixes))
3274 (custom-prefix-list (custom-prefix-add symbol prefixes))
944c91b6
PA
3275 (extra-prefix (if (widget-get widget :custom-last)
3276 " "
3277 " | "))
3278 (prefix (concat prefix extra-prefix))
3279 children entry)
3280 (while members
3281 (setq entry (car members)
3282 members (cdr members))
4ee1cf9f
PA
3283 (push (widget-create-child-and-convert
3284 widget (nth 1 entry)
3285 :group widget
3286 :tag (custom-unlispify-tag-name (nth 0 entry))
3287 :custom-prefixes custom-prefix-list
3288 :custom-level (1+ level)
3289 :custom-last (null members)
3290 :value (nth 0 entry)
3291 :custom-prefix prefix)
3292 children))
944c91b6
PA
3293 (widget-put widget :children (reverse children)))
3294 (message "Creating group...done")))
3295 ;; Nested style.
3296 ((eq state 'hidden)
3297 ;; Create level indicator.
26c7b3ef
RS
3298 (unless (eq custom-buffer-style 'links)
3299 (insert-char ?\ (* custom-buffer-indent (1- level)))
3300 (insert "-- "))
944c91b6
PA
3301 ;; Create tag.
3302 (let ((begin (point)))
3303 (insert tag)
3304 (widget-specify-sample widget begin (point)))
3305 (insert " group: ")
3306 ;; Create link/visibility indicator.
3307 (if (eq custom-buffer-style 'links)
3308 (push (widget-create-child-and-convert
d3d4df42 3309 widget 'custom-group-link
b62c92bb 3310 :tag "Go to Group"
944c91b6
PA
3311 symbol)
3312 buttons)
d3d4df42 3313 (push (widget-create-child-and-convert
98d5aafe 3314 widget 'custom-group-visibility
944c91b6
PA
3315 :help-echo "Show members of this group."
3316 :action 'custom-toggle-parent
3317 (not (eq state 'hidden)))
3318 buttons))
3319 (insert " \n")
3320 ;; Create magic button.
3321 (let ((magic (widget-create-child-and-convert
3322 widget 'custom-magic nil)))
3323 (widget-put widget :custom-magic magic)
3324 (push magic buttons))
3325 ;; Update buttons.
3326 (widget-put widget :buttons buttons)
3327 ;; Insert documentation.
26c7b3ef
RS
3328 (if (and (eq custom-buffer-style 'links) (> level 1))
3329 (widget-put widget :documentation-indent 0))
944c91b6
PA
3330 (widget-default-format-handler widget ?h))
3331 ;; Nested style.
3332 (t ;Visible.
d377bee9
RS
3333 ;; Add parent groups references above the group.
3334 (if t ;;; This should test that the buffer
3335 ;;; was made to display a group.
3336 (when (eq level 1)
cd6c0940
RS
3337 (if (custom-add-parent-links widget
3338 "Go to parent group:")
d377bee9 3339 (insert "\n"))))
944c91b6
PA
3340 ;; Create level indicator.
3341 (insert-char ?\ (* custom-buffer-indent (1- level)))
3342 (insert "/- ")
3343 ;; Create tag.
3344 (let ((start (point)))
3345 (insert tag)
3346 (widget-specify-sample widget start (point)))
3347 (insert " group: ")
3348 ;; Create visibility indicator.
3349 (unless (eq custom-buffer-style 'links)
3350 (insert "--------")
d3d4df42 3351 (push (widget-create-child-and-convert
944c91b6
PA
3352 widget 'visibility
3353 :help-echo "Hide members of this group."
3354 :action 'custom-toggle-parent
3355 (not (eq state 'hidden)))
3356 buttons)
3357 (insert " "))
3358 ;; Create more dashes.
3359 ;; Use 76 instead of 75 to compensate for the temporary "<"
d3d4df42 3360 ;; added by `widget-insert'.
944c91b6
PA
3361 (insert-char ?- (- 76 (current-column)
3362 (* custom-buffer-indent level)))
3363 (insert "\\\n")
3364 ;; Create magic button.
3365 (let ((magic (widget-create-child-and-convert
d3d4df42 3366 widget 'custom-magic
944c91b6
PA
3367 :indent 0
3368 nil)))
3369 (widget-put widget :custom-magic magic)
3370 (push magic buttons))
3371 ;; Update buttons.
3372 (widget-put widget :buttons buttons)
3373 ;; Insert documentation.
3374 (widget-default-format-handler widget ?h)
d377bee9
RS
3375 ;; Parent groups.
3376 (if nil ;;; This should test that the buffer
3377 ;;; was not made to display a group.
3378 (when (eq level 1)
3379 (insert-char ?\ custom-buffer-indent)
3380 (custom-add-parent-links widget)))
d3d4df42 3381 (custom-add-see-also widget
944c91b6
PA
3382 (make-string (* custom-buffer-indent level)
3383 ?\ ))
3384 ;; Members.
3385 (message "Creating group...")
3386 (custom-load-widget widget)
4ee1cf9f 3387 (let* ((members (custom-sort-items members
da5ec617
PA
3388 custom-buffer-sort-alphabetically
3389 custom-buffer-order-groups))
944c91b6
PA
3390 (prefixes (widget-get widget :custom-prefixes))
3391 (custom-prefix-list (custom-prefix-add symbol prefixes))
3392 (length (length members))
3393 (count 0)
3394 (children (mapcar (lambda (entry)
3395 (widget-insert "\n")
3396 (message "\
3397Creating group members... %2d%%"
3398 (/ (* 100.0 count) length))
3399 (setq count (1+ count))
3400 (prog1
3401 (widget-create-child-and-convert
3402 widget (nth 1 entry)
3403 :group widget
3404 :tag (custom-unlispify-tag-name
3405 (nth 0 entry))
3406 :custom-prefixes custom-prefix-list
3407 :custom-level (1+ level)
3408 :value (nth 0 entry))
3409 (unless (eq (preceding-char) ?\n)
3410 (widget-insert "\n"))))
3411 members)))
3412 (message "Creating group magic...")
fadbdfea 3413 (mapc 'custom-magic-reset children)
944c91b6
PA
3414 (message "Creating group state...")
3415 (widget-put widget :children children)
3416 (custom-group-state-update widget)
3417 (message "Creating group... done"))
3418 ;; End line
3419 (insert "\n")
3420 (insert-char ?\ (* custom-buffer-indent (1- level)))
3421 (insert "\\- " (widget-get widget :tag) " group end ")
3422 (insert-char ?- (- 75 (current-column) (* custom-buffer-indent level)))
3423 (insert "/\n")))))
d543e20b 3424
d3d4df42 3425(defvar custom-group-menu
3aec85bf 3426 '(("Set for Current Session" custom-group-set
6d528fc5
PA
3427 (lambda (widget)
3428 (eq (widget-get widget :custom-state) 'modified)))
3aec85bf 3429 ("Save for Future Sessions" custom-group-save
6d528fc5
PA
3430 (lambda (widget)
3431 (memq (widget-get widget :custom-state) '(modified set))))
3432 ("Reset to Current" custom-group-reset-current
3433 (lambda (widget)
86bd10bc 3434 (memq (widget-get widget :custom-state) '(modified))))
6d528fc5
PA
3435 ("Reset to Saved" custom-group-reset-saved
3436 (lambda (widget)
86bd10bc 3437 (memq (widget-get widget :custom-state) '(modified set))))
25ac13b5 3438 ("Reset to standard setting" custom-group-reset-standard
6d528fc5 3439 (lambda (widget)
86bd10bc 3440 (memq (widget-get widget :custom-state) '(modified set saved)))))
d543e20b 3441 "Alist of actions for the `custom-group' widget.
6d528fc5
PA
3442Each entry has the form (NAME ACTION FILTER) where NAME is the name of
3443the menu entry, ACTION is the function to call on the widget when the
3444menu is selected, and FILTER is a predicate which takes a `custom-group'
3445widget as an argument, and returns non-nil if ACTION is valid on that
d3d4df42 3446widget. If FILTER is nil, ACTION is always valid.")
d543e20b
PA
3447
3448(defun custom-group-action (widget &optional event)
3449 "Show the menu for `custom-group' WIDGET.
3450Optional EVENT is the location for the menu."
3451 (if (eq (widget-get widget :custom-state) 'hidden)
6d528fc5 3452 (custom-toggle-hide widget)
d543e20b 3453 (let* ((completion-ignore-case t)
25ac13b5
PA
3454 (answer (widget-choose (concat "Operation on "
3455 (custom-unlispify-tag-name
3456 (widget-get widget :value)))
6d528fc5
PA
3457 (custom-menu-filter custom-group-menu
3458 widget)
d543e20b
PA
3459 event)))
3460 (if answer
3461 (funcall answer widget)))))
3462
3463(defun custom-group-set (widget)
3464 "Set changes in all modified group members."
3465 (let ((children (widget-get widget :children)))
fadbdfea
DL
3466 (mapc (lambda (child)
3467 (when (eq (widget-get child :custom-state) 'modified)
3468 (widget-apply child :custom-set)))
d543e20b
PA
3469 children )))
3470
3471(defun custom-group-save (widget)
3472 "Save all modified group members."
3473 (let ((children (widget-get widget :children)))
fadbdfea
DL
3474 (mapc (lambda (child)
3475 (when (memq (widget-get child :custom-state) '(modified set))
3476 (widget-apply child :custom-save)))
d543e20b
PA
3477 children )))
3478
3479(defun custom-group-reset-current (widget)
3480 "Reset all modified group members."
3481 (let ((children (widget-get widget :children)))
fadbdfea
DL
3482 (mapc (lambda (child)
3483 (when (eq (widget-get child :custom-state) 'modified)
3484 (widget-apply child :custom-reset-current)))
d543e20b
PA
3485 children )))
3486
3487(defun custom-group-reset-saved (widget)
3488 "Reset all modified or set group members."
3489 (let ((children (widget-get widget :children)))
fadbdfea
DL
3490 (mapc (lambda (child)
3491 (when (memq (widget-get child :custom-state) '(modified set))
3492 (widget-apply child :custom-reset-saved)))
d543e20b
PA
3493 children )))
3494
25ac13b5 3495(defun custom-group-reset-standard (widget)
d543e20b
PA
3496 "Reset all modified, set, or saved group members."
3497 (let ((children (widget-get widget :children)))
fadbdfea
DL
3498 (mapc (lambda (child)
3499 (when (memq (widget-get child :custom-state)
3500 '(modified set saved))
3501 (widget-apply child :custom-reset-standard)))
d543e20b
PA
3502 children )))
3503
3504(defun custom-group-state-update (widget)
3505 "Update magic."
3506 (unless (eq (widget-get widget :custom-state) 'hidden)
3507 (let* ((children (widget-get widget :children))
3508 (states (mapcar (lambda (child)
3509 (widget-get child :custom-state))
3510 children))
25ac13b5
PA
3511 (magics custom-magic-alist)
3512 (found 'standard))
d543e20b
PA
3513 (while magics
3514 (let ((magic (car (car magics))))
3515 (if (and (not (eq magic 'hidden))
3516 (memq magic states))
3517 (setq found magic
3518 magics nil)
3519 (setq magics (cdr magics)))))
3520 (widget-put widget :custom-state found)))
3521 (custom-magic-reset widget))
3522
3523;;; The `custom-save-all' Function.
a1a4fa22 3524;;;###autoload
1e4ed6df 3525(defcustom custom-file nil
d543e20b 3526 "File used for storing customization information.
1e4ed6df
PA
3527The default is nil, which means to use your init file
3528as specified by `user-init-file'. If you specify some other file,
a34511a1
RS
3529you need to explicitly load that file for the settings to take effect.
3530
3531When you change this variable, look in the previous custom file
3532\(usually your init file) for the forms `(custom-set-variables ...)'
3533and `(custom-set-faces ...)', and copy them (whichever ones you find)
3534to the new custom file. This will preserve your existing customizations."
1e4ed6df 3535 :type '(choice (const :tag "Your Emacs init file" nil) file)
d543e20b
PA
3536 :group 'customize)
3537
176eb8cb
KH
3538(defun custom-file ()
3539 "Return the file name for saving customizations."
558771ba
EZ
3540 (setq custom-file
3541 (or custom-file
3542 (let ((user-init-file user-init-file)
3543 (default-init-file
3544 (if (eq system-type 'ms-dos) "~/_emacs" "~/.emacs")))
3545 (when (null user-init-file)
3546 (if (or (file-exists-p default-init-file)
3547 (and (eq system-type 'windows-nt)
3548 (file-exists-p "~/_emacs")))
3549 ;; Started with -q, i.e. the file containing
3550 ;; Custom settings hasn't been read. Saving
3551 ;; settings there would overwrite other settings.
3552 (error "Saving settings from \"emacs -q\" would overwrite existing customizations"))
3553 (setq user-init-file default-init-file))
3554 user-init-file))))
176eb8cb 3555
d543e20b 3556(defun custom-save-delete (symbol)
a34511a1
RS
3557 "Visit `custom-file' and delete all calls to SYMBOL from it.
3558Leave point at the old location of the first such call,
3559or (if there were none) at the end of the buffer."
fc4d62fe 3560 (let ((default-major-mode))
176eb8cb 3561 (set-buffer (find-file-noselect (custom-file))))
d543e20b 3562 (goto-char (point-min))
cbe8bb8e
KH
3563 ;; Skip all whitespace and comments.
3564 (while (forward-comment 1))
3565 (or (eobp)
3566 (save-excursion (forward-sexp (buffer-size)))) ; Test for scan errors.
a34511a1
RS
3567 (let (first)
3568 (catch 'found
3569 (while t ;; We exit this loop only via throw.
3570 ;; Skip all whitespace and comments.
3571 (while (forward-comment 1))
3572 (let ((start (point))
3573 (sexp (condition-case nil
3574 (read (current-buffer))
3575 (end-of-file (throw 'found nil)))))
3576 (when (and (listp sexp)
3577 (eq (car sexp) symbol))
3578 (delete-region start (point))
3579 (unless first
3580 (setq first (point)))))))
3581 (if first
3582 (goto-char first)
189638d5
GM
3583 ;; Move in front of local variables, otherwise long Custom
3584 ;; entries would make them ineffective.
3585 (let ((pos (point-max))
3586 (case-fold-search t))
3587 (save-excursion
3588 (goto-char (point-max))
3589 (search-backward "\n\^L" (max (- (point-max) 3000) (point-min))
3590 'move)
3591 (when (search-forward "Local Variables:" nil t)
3592 (setq pos (line-beginning-position))))
3593 (goto-char pos)))))
d543e20b
PA
3594
3595(defun custom-save-variables ()
3596 "Save all customized variables in `custom-file'."
3597 (save-excursion
3598 (custom-save-delete 'custom-set-variables)
d151422c
MR
3599 (let ((standard-output (current-buffer))
3600 (saved-list (make-list 1 0))
3601 sort-fold-case)
3602 ;; First create a sorted list of saved variables.
3603 (mapatoms
3604 (lambda (symbol)
3605 (if (get symbol 'saved-value)
3606 (nconc saved-list (list symbol)))))
3607 (setq saved-list (sort (cdr saved-list) 'string<))
d543e20b
PA
3608 (unless (bolp)
3609 (princ "\n"))
aec2bb63
DL
3610 (princ "(custom-set-variables
3611 ;; custom-set-variables was added by Custom -- don't edit or cut/paste it!
fda514f7 3612 ;; Your init file should contain only one such instance.\n")
d151422c
MR
3613 (mapcar
3614 (lambda (symbol)
3615 (let ((value (get symbol 'saved-value))
3616 (requests (get symbol 'custom-requests))
3617 (now (not (or (get symbol 'standard-value)
3618 (and (not (boundp symbol))
d3d4df42
DL
3619 (not (get symbol 'force-value))))))
3620 (comment (get symbol 'saved-variable-comment))
3621 sep)
3622 (when (or value comment)
a34511a1
RS
3623 (unless (bolp)
3624 (princ "\n"))
3625 (princ " '(")
d3d4df42
DL
3626 (prin1 symbol)
3627 (princ " ")
3628 (prin1 (car value))
3629 (cond ((or now requests comment)
3630 (princ " ")
3631 (if now
3632 (princ "t")
3633 (princ "nil"))
3634 (cond ((or requests comment)
3635 (princ " ")
3636 (if requests
3637 (prin1 requests)
3638 (princ "nil"))
3639 (cond (comment
3640 (princ " ")
3641 (prin1 comment)
3642 (princ ")"))
3643 (t
3644 (princ ")"))))
3645 (t
3646 (princ ")"))))
3647 (t
3648 (princ ")"))))))
d151422c 3649 saved-list)
a34511a1
RS
3650 (if (bolp)
3651 (princ " "))
d543e20b
PA
3652 (princ ")")
3653 (unless (looking-at "\n")
3654 (princ "\n")))))
3655
3656(defun custom-save-faces ()
3657 "Save all customized faces in `custom-file'."
3658 (save-excursion
3659 (custom-save-delete 'custom-set-faces)
d151422c
MR
3660 (let ((standard-output (current-buffer))
3661 (saved-list (make-list 1 0))
3662 sort-fold-case)
3663 ;; First create a sorted list of saved faces.
3664 (mapatoms
3665 (lambda (symbol)
3666 (if (get symbol 'saved-face)
3667 (nconc saved-list (list symbol)))))
3668 (setq saved-list (sort (cdr saved-list) 'string<))
3669 ;; The default face must be first, since it affects the others.
3670 (if (memq 'default saved-list)
3671 (setq saved-list (cons 'default (delq 'default saved-list))))
d543e20b
PA
3672 (unless (bolp)
3673 (princ "\n"))
aec2bb63
DL
3674 (princ "(custom-set-faces
3675 ;; custom-set-faces was added by Custom -- don't edit or cut/paste it!
fda514f7 3676 ;; Your init file should contain only one such instance.\n")
d151422c
MR
3677 (mapcar
3678 (lambda (symbol)
d3d4df42
DL
3679 (let ((value (get symbol 'saved-face))
3680 (now (not (or (get 'default 'face-defface-spec)
3681 (and (not (custom-facep 'default))
3682 (not (get 'default 'force-face))))))
3683 (comment (get 'default 'saved-face-comment)))
d151422c
MR
3684 (unless (eq symbol 'default))
3685 ;; Don't print default face here.
a34511a1
RS
3686 (unless (bolp)
3687 (princ "\n"))
3688 (princ " '(")
d3d4df42 3689 (prin1 symbol)
d151422c
MR
3690 (princ " ")
3691 (prin1 value)
d3d4df42
DL
3692 (cond ((or now comment)
3693 (princ " ")
3694 (if now
3695 (princ "t")
3696 (princ "nil"))
3697 (cond (comment
3698 (princ " ")
3699 (prin1 comment)
3700 (princ ")"))
3701 (t
3702 (princ ")"))))
3703 (t
3704 (princ ")")))))
d151422c 3705 saved-list)
a34511a1
RS
3706 (if (bolp)
3707 (princ " "))
d543e20b
PA
3708 (princ ")")
3709 (unless (looking-at "\n")
3710 (princ "\n")))))
3711
6d528fc5 3712;;;###autoload
f9dd586e 3713(defun customize-save-customized ()
6d528fc5
PA
3714 "Save all user options which have been set in this session."
3715 (interactive)
3716 (mapatoms (lambda (symbol)
3717 (let ((face (get symbol 'customized-face))
d3d4df42
DL
3718 (value (get symbol 'customized-value))
3719 (face-comment (get symbol 'customized-face-comment))
3720 (variable-comment
3721 (get symbol 'customized-variable-comment)))
3722 (when face
6d528fc5
PA
3723 (put symbol 'saved-face face)
3724 (put symbol 'customized-face nil))
d3d4df42 3725 (when value
6d528fc5 3726 (put symbol 'saved-value value)
d3d4df42
DL
3727 (put symbol 'customized-value nil))
3728 (when variable-comment
3729 (put symbol 'saved-variable-comment variable-comment)
3730 (put symbol 'customized-variable-comment nil))
3731 (when face-comment
3732 (put symbol 'saved-face-comment face-comment)
3733 (put symbol 'customized-face-comment nil)))))
6d528fc5
PA
3734 ;; We really should update all custom buffers here.
3735 (custom-save-all))
3736
d543e20b
PA
3737;;;###autoload
3738(defun custom-save-all ()
3739 "Save all customizations in `custom-file'."
4ee1cf9f
PA
3740 (let ((inhibit-read-only t))
3741 (custom-save-variables)
3742 (custom-save-faces)
3743 (save-excursion
fc4d62fe 3744 (let ((default-major-mode nil))
176eb8cb 3745 (set-buffer (find-file-noselect (custom-file))))
82d3d694
RS
3746 (let ((file-precious-flag t))
3747 (save-buffer)))))
d543e20b
PA
3748
3749;;; The Customize Menu.
3750
bd042c03
PA
3751;;; Menu support
3752
25ac13b5
PA
3753(defcustom custom-menu-nesting 2
3754 "Maximum nesting in custom menus."
3755 :type 'integer
6aaedd12 3756 :group 'custom-menu)
d543e20b
PA
3757
3758(defun custom-face-menu-create (widget symbol)
3759 "Ignoring WIDGET, create a menu entry for customization face SYMBOL."
3760 (vector (custom-unlispify-menu-entry symbol)
86bd10bc 3761 `(customize-face ',symbol)
d543e20b
PA
3762 t))
3763
3764(defun custom-variable-menu-create (widget symbol)
3765 "Ignoring WIDGET, create a menu entry for customization variable SYMBOL."
3766 (let ((type (get symbol 'custom-type)))
3767 (unless (listp type)
3768 (setq type (list type)))
3769 (if (and type (widget-get type :custom-menu))
3770 (widget-apply type :custom-menu symbol)
3771 (vector (custom-unlispify-menu-entry symbol)
86bd10bc 3772 `(customize-variable ',symbol)
d543e20b
PA
3773 t))))
3774
bd042c03 3775;; Add checkboxes to boolean variable entries.
d543e20b
PA
3776(widget-put (get 'boolean 'widget-type)
3777 :custom-menu (lambda (widget symbol)
3778 (vector (custom-unlispify-menu-entry symbol)
86bd10bc 3779 `(customize-variable ',symbol)
d543e20b
PA
3780 ':style 'toggle
3781 ':selected symbol)))
3782
d04a3972
DL
3783(defun custom-group-menu-create (widget symbol)
3784 "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
3785 `( ,(custom-unlispify-menu-entry symbol t)
3786 :filter (lambda (&rest junk)
3787 (cdr (custom-menu-create ',symbol)))))
d543e20b 3788
bd042c03
PA
3789;;;###autoload
3790(defun custom-menu-create (symbol)
d543e20b 3791 "Create menu for customization group SYMBOL.
d543e20b 3792The menu is in a format applicable to `easy-menu-define'."
bd042c03 3793 (let* ((item (vector (custom-unlispify-menu-entry symbol)
86bd10bc 3794 `(customize-group ',symbol)
bd042c03
PA
3795 t)))
3796 (if (and (or (not (boundp 'custom-menu-nesting))
3797 (>= custom-menu-nesting 0))
d543e20b
PA
3798 (< (length (get symbol 'custom-group)) widget-menu-max-size))
3799 (let ((custom-prefix-list (custom-prefix-add symbol
25ac13b5 3800 custom-prefix-list))
da5ec617
PA
3801 (members (custom-sort-items (get symbol 'custom-group)
3802 custom-menu-sort-alphabetically
3803 custom-menu-order-groups)))
d543e20b
PA
3804 (custom-load-symbol symbol)
3805 `(,(custom-unlispify-menu-entry symbol t)
3806 ,item
3807 "--"
3808 ,@(mapcar (lambda (entry)
3809 (widget-apply (if (listp (nth 1 entry))
3810 (nth 1 entry)
3811 (list (nth 1 entry)))
3812 :custom-menu (nth 0 entry)))
25ac13b5 3813 members)))
d543e20b
PA
3814 item)))
3815
3816;;;###autoload
bd042c03
PA
3817(defun customize-menu-create (symbol &optional name)
3818 "Return a customize menu for customization group SYMBOL.
d3d4df42 3819If optional NAME is given, use that as the name of the menu.
bd042c03
PA
3820Otherwise the menu will be named `Customize'.
3821The format is suitable for use with `easy-menu-define'."
3822 (unless name
3823 (setq name "Customize"))
d04a3972
DL
3824 `(,name
3825 :filter (lambda (&rest junk)
1751c874 3826 (custom-menu-create ',symbol))))
d543e20b 3827
bd042c03
PA
3828;;; The Custom Mode.
3829
3830(defvar custom-mode-map nil
3831 "Keymap for `custom-mode'.")
b62c92bb 3832
bd042c03 3833(unless custom-mode-map
b92aaee0
SM
3834 ;; This keymap should be dense, but a dense keymap would prevent inheriting
3835 ;; "\r" bindings from the parent map.
3836 (setq custom-mode-map (make-sparse-keymap))
bd042c03 3837 (set-keymap-parent custom-mode-map widget-keymap)
c32de15e 3838 (suppress-keymap custom-mode-map)
b62c92bb
RS
3839 (define-key custom-mode-map " " 'scroll-up)
3840 (define-key custom-mode-map "\177" 'scroll-down)
d3d4df42 3841 (define-key custom-mode-map "q" 'Custom-buffer-done)
0f3335c0 3842 (define-key custom-mode-map "u" 'Custom-goto-parent)
766e15c6
RS
3843 (define-key custom-mode-map "n" 'widget-forward)
3844 (define-key custom-mode-map "p" 'widget-backward)
0f3335c0
RS
3845 (define-key custom-mode-map [mouse-1] 'Custom-move-and-invoke))
3846
3847(defun Custom-move-and-invoke (event)
3848 "Move to where you click, and if it is an active field, invoke it."
3849 (interactive "e")
3850 (mouse-set-point event)
3851 (if (widget-event-point event)
3852 (let* ((pos (widget-event-point event))
3853 (button (get-char-property pos 'button)))
3854 (if button
3855 (widget-button-click event)))))
bd042c03 3856
d3d4df42 3857(easy-menu-define Custom-mode-menu
bd042c03
PA
3858 custom-mode-map
3859 "Menu used in customization buffers."
3860 `("Custom"
944c91b6 3861 ,(customize-menu-create 'customize)
ab678382
RS
3862 ["Set" Custom-set t]
3863 ["Save" Custom-save t]
3864 ["Reset to Current" Custom-reset-current t]
3865 ["Reset to Saved" Custom-reset-saved t]
3866 ["Reset to Standard Settings" Custom-reset-standard t]
2a1c4b90 3867 ["Info" (Info-goto-node "(emacs)Easy Customization") t]))
bd042c03 3868
b62c92bb
RS
3869(defun Custom-goto-parent ()
3870 "Go to the parent group listed at the top of this buffer.
3871If several parents are listed, go to the first of them."
3872 (interactive)
3873 (save-excursion
3874 (goto-char (point-min))
3875 (if (search-forward "\nGo to parent group: " nil t)
3876 (let* ((button (get-char-property (point) 'button))
3877 (parent (downcase (widget-get button :tag))))
3878 (customize-group parent)))))
3879
bd042c03 3880(defcustom custom-mode-hook nil
d3d4df42 3881 "Hook called when entering Custom mode."
bd042c03 3882 :type 'hook
6aaedd12 3883 :group 'custom-buffer )
bd042c03 3884
b62c92bb
RS
3885(defun custom-state-buffer-message (widget)
3886 (if (eq (widget-get (widget-get widget :parent) :custom-state) 'modified)
3887 (message "To install your edits, invoke [State] and choose the Set operation")))
8691cfa7 3888
bd042c03
PA
3889(defun custom-mode ()
3890 "Major mode for editing customization buffers.
3891
3892The following commands are available:
3893
3894Move to next button or editable field. \\[widget-forward]
3895Move to previous button or editable field. \\[widget-backward]
4ee1cf9f
PA
3896\\<widget-field-keymap>\
3897Complete content of editable text field. \\[widget-complete]
3898\\<custom-mode-map>\
0f3335c0 3899Invoke button under the mouse pointer. \\[Custom-move-and-invoke]
25ac13b5 3900Invoke button under point. \\[widget-button-press]
ab678382
RS
3901Set all modifications. \\[Custom-set]
3902Make all modifications default. \\[Custom-save]
3903Reset all modified options. \\[Custom-reset-current]
3904Reset all modified or set options. \\[Custom-reset-saved]
3905Reset all options. \\[Custom-reset-standard]
bd042c03
PA
3906
3907Entry to this mode calls the value of `custom-mode-hook'
3908if that value is non-nil."
3909 (kill-all-local-variables)
3910 (setq major-mode 'custom-mode
3911 mode-name "Custom")
3912 (use-local-map custom-mode-map)
ab678382 3913 (easy-menu-add Custom-mode-menu)
bd042c03 3914 (make-local-variable 'custom-options)
b62c92bb
RS
3915 (make-local-variable 'widget-documentation-face)
3916 (setq widget-documentation-face 'custom-documentation-face)
3aec85bf
RS
3917 (make-local-variable 'widget-button-face)
3918 (setq widget-button-face 'custom-button-face)
d3d4df42
DL
3919 (set (make-local-variable 'widget-button-pressed-face)
3920 'custom-button-pressed-face)
3921 (set (make-local-variable 'widget-mouse-face)
3922 'custom-button-pressed-face) ; buttons `depress' when moused
3923 ;; When possible, use relief for buttons, not bracketing. This test
3924 ;; may not be optimal.
3925 (when custom-raised-buttons
3926 (set (make-local-variable 'widget-push-button-prefix) "")
3927 (set (make-local-variable 'widget-push-button-suffix) "")
3928 (set (make-local-variable 'widget-link-prefix) "")
3929 (set (make-local-variable 'widget-link-suffix) ""))
b62c92bb 3930 (add-hook 'widget-edit-functions 'custom-state-buffer-message nil t)
bd042c03 3931 (run-hooks 'custom-mode-hook))
d543e20b 3932
7f352f86
DL
3933(put 'custom-mode 'mode-class 'special)
3934
2365594b
DL
3935(add-to-list
3936 'debug-ignored-errors
3937 "^No user options have changed defaults in recent Emacs versions$")
3938
d543e20b
PA
3939;;; The End.
3940
3941(provide 'cus-edit)
3942
d3d4df42 3943;;; cus-edit.el ends here