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