1 ;;; custom.el --- User friendly customization support.
3 ;; Copyright (C) 1995, 1996 Free Software Foundation, Inc.
5 ;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
9 ;; This file is part of GNU Emacs.
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)
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.
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.
28 ;; WARNING: This package is still under construction and not all of
29 ;; the features below are implemented.
31 ;; This package provides a framework for adding user friendly
32 ;; customization support to Emacs. Having to do customization by
33 ;; editing a text file in some arcane syntax is user hostile in the
34 ;; extreme, and to most users emacs lisp definitely count as arcane.
36 ;; The intent is that authors of emacs lisp packages declare the
37 ;; variables intended for user customization with `custom-declare'.
38 ;; Custom can then automatically generate a customization buffer with
39 ;; `custom-buffer-create' where the user can edit the package
40 ;; variables in a simple and intuitive way, as well as a menu with
41 ;; `custom-menu-create' where he can set the more commonly used
42 ;; variables interactively.
44 ;; It is also possible to use custom for modifying the properties of
45 ;; other objects than the package itself, by specifying extra optional
46 ;; arguments to `custom-buffer-create'.
48 ;; Custom is inspired by OPEN LOOK property windows.
52 ;; - Toggle documentation in three states `none', `one-line', `full'.
53 ;; - Function to generate an XEmacs menu from a CUSTOM.
54 ;; - Write TeXinfo documentation.
55 ;; - Make it possible to hide sections by clicking at the level.
56 ;; - Declare AUC TeX variables.
57 ;; - Declare (ding) Gnus variables.
58 ;; - Declare Emacs variables.
59 ;; - Implement remaining types.
61 ;; - Allow `URL', `info', and internal hypertext buttons.
62 ;; - Support meta-variables and goal directed customization.
63 ;; - Make it easy to declare custom types independently.
64 ;; - Make it possible to declare default value and type for a single
65 ;; variable, storing the data in a symbol property.
66 ;; - Syntactic sugar for CUSTOM declarations.
67 ;; - Use W3 for variable documentation.
76 (defun custom-xmas-add-text-properties (start end props &optional object)
77 (add-text-properties start end props object)
78 (put-text-property start end 'start-open t object)
79 (put-text-property start end 'end-open t object))
81 (defun custom-xmas-put-text-property (start end prop value &optional object)
82 (put-text-property start end prop value object)
83 (put-text-property start end 'start-open t object)
84 (put-text-property start end 'end-open t object))
86 (defun custom-xmas-extent-start-open ()
87 (map-extents (lambda (extent arg)
88 (set-extent-property extent 'start-open t))
89 nil (point) (min (1+ (point)) (point-max))))
91 (if (string-match "XEmacs\\|Lucid" emacs-version)
93 (fset 'custom-add-text-properties 'custom-xmas-add-text-properties)
94 (fset 'custom-put-text-property 'custom-xmas-put-text-property)
95 (fset 'custom-extent-start-open 'custom-xmas-extent-start-open)
96 (fset 'custom-set-text-properties
97 (if (fboundp 'set-text-properties)
98 'set-text-properties))
99 (fset 'custom-buffer-substring-no-properties
100 (if (fboundp 'buffer-substring-no-properties)
101 'buffer-substring-no-properties
102 'custom-xmas-buffer-substring-no-properties)))
103 (fset 'custom-add-text-properties 'add-text-properties)
104 (fset 'custom-put-text-property 'put-text-property)
105 (fset 'custom-extent-start-open 'ignore)
106 (fset 'custom-set-text-properties 'set-text-properties)
107 (fset 'custom-buffer-substring-no-properties
108 'buffer-substring-no-properties))
110 (defun custom-xmas-buffer-substring-no-properties (beg end)
111 "Return the text from BEG to END, without text properties, as a string."
112 (let ((string (buffer-substring beg end)))
113 (custom-set-text-properties 0 (length string) nil string)
116 (or (fboundp 'add-to-list)
117 ;; Introduced in Emacs 19.29.
118 (defun add-to-list (list-var element)
119 "Add to the value of LIST-VAR the element ELEMENT if it isn't there yet.
120 If you want to use `add-to-list' on a variable that is not defined
121 until a certain package is loaded, you should put the call to `add-to-list'
122 into a hook function that will be run only after loading the package.
123 `eval-after-load' provides one way to do this. In some cases
124 other hooks, such as major mode hooks, can do the job."
125 (or (member element (symbol-value list-var))
126 (set list-var (cons element (symbol-value list-var))))))
128 (or (fboundp 'plist-get)
129 ;; Introduced in Emacs 19.29.
130 (defun plist-get (plist prop)
131 "Extract a value from a property list.
132 PLIST is a property list, which is a list of the form
133 \(PROP1 VALUE1 PROP2 VALUE2...). This function returns the value
134 corresponding to the given PROP, or nil if PROP is not
135 one of the properties on the list."
138 (if (eq (car plist) prop)
139 (setq result (car (cdr plist))
141 (set plist (cdr (cdr plist)))))
144 (or (fboundp 'plist-put)
145 ;; Introduced in Emacs 19.29.
146 (defun plist-put (plist prop val)
147 "Change value in PLIST of PROP to VAL.
148 PLIST is a property list, which is a list of the form
149 \(PROP1 VALUE1 PROP2 VALUE2 ...). PROP is a symbol and VAL is any object.
150 If PROP is already a property on the list, its value is set to VAL,
151 otherwise the new PROP VAL pair is added. The new plist is returned;
152 use `(setq x (plist-put x prop val))' to be sure to use the new value.
153 The PLIST is modified by side effects."
156 (let ((current plist))
158 (cond ((eq (car current) prop)
159 (setcar (cdr current) val)
161 ((null (cdr (cdr current)))
162 (setcdr (cdr current) (list prop val))
165 (setq current (cdr (cdr current)))))))
168 (or (fboundp 'match-string)
169 ;; Introduced in Emacs 19.29.
170 (defun match-string (num &optional string)
171 "Return string of text matched by last search.
172 NUM specifies which parenthesized expression in the last regexp.
173 Value is nil if NUMth pair didn't match, or there were less than NUM pairs.
174 Zero means the entire text matched by the whole regexp or whole string.
175 STRING should be given if the last search was by `string-match' on STRING."
176 (if (match-beginning num)
178 (substring string (match-beginning num) (match-end num))
179 (buffer-substring (match-beginning num) (match-end num))))))
182 ;; Introduced in Emacs 19.29.
184 "Return t if X is a face name or an internal face vector."
185 (and (or (and (fboundp 'internal-facep) (internal-facep x))
188 (assq x (and (boundp 'global-face-data) global-face-data))))
191 ;; XEmacs and Emacs 19.29 facep does different things.
192 (if (fboundp 'find-face)
193 (fset 'custom-facep 'find-face)
194 (fset 'custom-facep 'facep))
196 (if (custom-facep 'underline)
198 ;; No underline face in XEmacs 19.12.
199 (and (fboundp 'make-face)
200 (funcall (intern "make-face") 'underline))
201 ;; Must avoid calling set-face-underline-p directly, because it
202 ;; is a defsubst in emacs19, and will make the .elc files non
204 (or (and (fboundp 'face-differs-from-default-p)
205 (face-differs-from-default-p 'underline))
206 (and (fboundp 'set-face-underline-p)
207 (funcall 'set-face-underline-p 'underline t))))
209 (defun custom-xmas-set-text-properties (start end props &optional buffer)
213 (custom-put-text-property
214 start end (car props) (nth 1 props) buffer)
215 (setq props (nthcdr 2 props)))
216 (remove-text-properties start end ()))))
218 (or (fboundp 'event-point)
219 ;; Missing in Emacs 19.29.
220 (defun event-point (event)
221 "Return the character position of the given mouse-motion, button-press,
222 or button-release event. If the event did not occur over a window, or did
223 not occur over text, then this returns nil. Otherwise, it returns an index
224 into the buffer visible in the event's window."
225 (posn-point (event-start event))))
228 (defvar x-colors nil)
229 (defvar custom-button-face nil)
230 (defvar custom-field-uninitialized-face nil)
231 (defvar custom-field-invalid-face nil)
232 (defvar custom-field-modified-face nil)
233 (defvar custom-field-face nil)
234 (defvar custom-mouse-face nil)
235 (defvar custom-field-active-face nil))
237 ;; We can't easily check for a working intangible.
238 (defconst intangible (if (and (boundp 'emacs-minor-version)
239 (or (> emacs-major-version 19)
240 (and (> emacs-major-version 18)
241 (> emacs-minor-version 28))))
242 (setq intangible 'intangible)
243 (setq intangible 'intangible-if-it-had-been-working))
244 "The symbol making text intangible.")
246 (defconst rear-nonsticky (if (string-match "XEmacs" emacs-version)
249 "The symbol making text properties non-sticky in the rear end.")
251 (defconst front-sticky (if (string-match "XEmacs" emacs-version)
254 "The symbol making text properties sticky in the front.")
256 (defconst mouse-face (if (string-match "XEmacs" emacs-version)
259 "Symbol used for highlighting text under mouse.")
261 ;; Put it in the Help menu, if possible.
262 (if (string-match "XEmacs" emacs-version)
263 ;; XEmacs (disabled because it doesn't work)
265 (add-menu-item '("Help") "Customize..." 'customize t))
266 ;; Emacs 19.28 and earlier
267 (global-set-key [ menu-bar help customize ]
268 '("Customize..." . customize))
269 ;; Emacs 19.29 and later
270 (global-set-key [ menu-bar help-menu customize ]
271 '("Customize..." . customize)))
273 ;; XEmacs popup-menu stolen from w3.el.
274 (defun custom-x-really-popup-menu (pos title menudesc)
275 "My hacked up function to do a blocking popup menu..."
276 (let ((echo-keystrokes 0)
279 (setq menu (cons (vector (car (car menudesc))
280 (list (car (car menudesc))) t) menu)
281 menudesc (cdr menudesc)))
282 (setq menu (cons title menu))
286 (setq event (next-command-event event))
287 (cond ((and (misc-user-event-p event) (stringp (car-safe (event-object event))))
288 (throw 'popup-done (event-object event)))
289 ((and (misc-user-event-p event)
290 (or (eq (event-object event) 'abort)
291 (eq (event-object event) 'menu-no-selection-hook)))
293 ((not (popup-menu-up-p))
294 (throw 'popup-done nil))
295 ((button-release-event-p event);; don't beep twice
299 (message "please make a choice from the menu.")))))))
303 ;; XEmacs use inheritable extents for the same purpose as Emacs uses
304 ;; the category text property.
306 (if (string-match "XEmacs" emacs-version)
308 ;; XEmacs categories.
309 (defun custom-category-create (name)
310 (set name (make-extent nil nil))
311 "Create a text property category named NAME.")
313 (defun custom-category-put (name property value)
314 "In CATEGORY set PROPERTY to VALUE."
315 (set-extent-property (symbol-value name) property value))
317 (defun custom-category-get (name property)
318 "In CATEGORY get PROPERTY."
319 (extent-property (symbol-value name) property))
321 (defun custom-category-set (from to category)
322 "Make text between FROM and TWO have category CATEGORY."
323 (let ((extent (make-extent from to)))
324 (set-extent-parent extent (symbol-value category)))))
327 (defun custom-category-create (name)
328 "Create a text property category named NAME."
331 (defun custom-category-put (name property value)
332 "In CATEGORY set PROPERTY to VALUE."
333 (put name property value))
335 (defun custom-category-get (name property)
336 "In CATEGORY get PROPERTY."
339 (defun custom-category-set (from to category)
340 "Make text between FROM and TWO have category CATEGORY."
341 (custom-put-text-property from to 'category category)))
345 ;; The following functions and variables defines the interface for
346 ;; connecting a CUSTOM with an external entity, by default an emacs
349 (defvar custom-external 'default-value
350 "Function returning the external value of NAME.")
352 (defvar custom-external-set 'set-default
353 "Function setting the external value of NAME to VALUE.")
355 (defun custom-external (name)
356 "Get the external value associated with NAME."
357 (funcall custom-external name))
359 (defun custom-external-set (name value)
360 "Set the external value associated with NAME to VALUE."
361 (funcall custom-external-set name value))
363 (defvar custom-name-fields nil
364 "Alist of custom names and their associated editing field.")
365 (make-variable-buffer-local 'custom-name-fields)
367 (defun custom-name-enter (name field)
368 "Associate NAME with FIELD."
371 (custom-assert 'field)
372 (setq custom-name-fields (cons (cons name field) custom-name-fields))))
374 (defun custom-name-field (name)
375 "The editing field associated with NAME."
376 (cdr (assq name custom-name-fields)))
378 (defun custom-name-value (name)
379 "The value currently displayed for NAME in the customization buffer."
380 (let* ((field (custom-name-field name))
381 (custom (custom-field-custom field)))
382 (custom-field-parse field)
383 (funcall (custom-property custom 'export) custom
384 (car (custom-field-extract custom field)))))
386 (defvar custom-save 'custom-save
387 "Function that will save current customization buffer.")
389 ;;; Custom Functions:
391 ;; The following functions are part of the public interface to the
392 ;; CUSTOM datastructure. Each CUSTOM describes a group of variables,
393 ;; a single variable, or a component of a structured variable. The
394 ;; CUSTOM instances are part of two hierarchies, the first is the
395 ;; `part-of' hierarchy in which each CUSTOM is a component of another
396 ;; CUSTOM, except for the top level CUSTOM which is contained in
397 ;; `custom-data'. The second hierarchy is a `is-a' type hierarchy
398 ;; where each CUSTOM is a leaf in the hierarchy defined by the `type'
399 ;; property and `custom-type-properties'.
401 (defvar custom-file "~/.custom.el"
402 "Name of file with customization information.")
404 (defconst custom-data
406 (doc . "The extensible self-documenting text editor.")
413 Press [Save] to save any changes permanently after you are done editing.
414 You can load customization information from other files by editing the
415 `File' field and pressing the [Load] button. When you press [Save] the
416 customization information of all files you have loaded, plus any
417 changes you might have made manually, will be stored in the file
418 specified by the `File' field.")
419 (data ((tag . "Load")
421 (query . custom-load))
424 (query . custom-save))
425 ((name . custom-file)
426 (default . "~/.custom.el")
427 (doc . "Name of file with customization information.\n")
430 "The global customization information.
431 A custom association list.")
433 (defun custom-declare (path custom)
434 "Declare variables for customization.
435 PATH is a list of tags leading to the place in the customization
436 hierarchy the new entry should be added. CUSTOM is the entry to add."
437 (custom-initialize custom)
438 (let ((current (custom-travel-path custom-data path)))
439 (or (member custom (custom-data current))
440 (nconc (custom-data current) (list custom)))))
442 (put 'custom-declare 'lisp-indent-hook 1)
444 (defconst custom-type-properties
445 '((repeat (type . default)
446 ;; See `custom-match'.
447 (import . custom-repeat-import)
448 (eval . custom-repeat-eval)
449 (quote . custom-repeat-quote)
450 (accept . custom-repeat-accept)
451 (extract . custom-repeat-extract)
452 (validate . custom-repeat-validate)
453 (insert . custom-repeat-insert)
454 (match . custom-repeat-match)
455 (query . custom-repeat-query)
461 (accept . custom-pair-accept)
462 (eval . custom-pair-eval)
463 (import . custom-pair-import)
464 (quote . custom-pair-quote)
465 (valid . (lambda (c d) (consp d)))
466 (extract . custom-pair-extract))
469 (quote . custom-list-quote)
470 (valid . (lambda (c d)
472 (extract . custom-list-extract))
473 (group (type . default)
474 ;; See `custom-match'.
476 (eval . custom-group-eval)
477 (import . custom-group-import)
478 (initialize . custom-group-initialize)
479 (apply . custom-group-apply)
480 (reset . custom-group-reset)
481 (factory-reset . custom-group-factory-reset)
483 (validate . custom-group-validate)
484 (query . custom-toggle-hide)
485 (accept . custom-group-accept)
486 (insert . custom-group-insert)
487 (find . custom-group-find))
488 (toggle (type . choice)
490 (data ((type . const)
496 (triggle (type . choice)
498 (data ((type . const)
506 (default . custom:asis))))
507 (choice (type . default)
508 ;; See `custom-match'.
509 (query . custom-choice-query)
510 (accept . custom-choice-accept)
511 (extract . custom-choice-extract)
512 (validate . custom-choice-validate)
513 (insert . custom-choice-insert)
514 (none (tag . "Unknown")
515 (default . __uninitialized__)
517 (const (type . default)
518 ;; A `const' only matches a single lisp value.
519 (extract . (lambda (c f) (list (custom-default c))))
520 (validate . (lambda (c f) nil))
521 (valid . custom-const-valid)