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