;;; custom.el --- User friendly customization support.
-;; Copyright (C) 1995 Free Software Foundation, Inc.
-;;
+
+;; Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
;; Keywords: help
;; Version: 0.5
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
;;; Commentary:
-;;
+
;; WARNING: This package is still under construction and not all of
;; the features below are implemented.
;;
;; editing a text file in some arcane syntax is user hostile in the
;; extreme, and to most users emacs lisp definitely count as arcane.
;;
-;; The intension is that authors of emacs lisp packages declare the
+;; The intent is that authors of emacs lisp packages declare the
;; variables intended for user customization with `custom-declare'.
;; Custom can then automatically generate a customization buffer with
;; `custom-buffer-create' where the user can edit the package
;; - Make it possible to declare default value and type for a single
;; variable, storing the data in a symbol property.
;; - Syntactic sugar for CUSTOM declarations.
-;; - Use W3 for variable documenation.
+;; - Use W3 for variable documentation.
;;; Code:
+(eval-when-compile
+ (require 'cl))
+
;;; Compatibility:
-(or (fboundp 'buffer-substring-no-properties)
- ;; Introduced in Emacs 19.29.
- (defun buffer-substring-no-properties (beg end)
- "Return the text from BEG to END, without text properties, as a string."
- (let ((string (buffer-substring beg end)))
- (set-text-properties 0 (length string) nil string)
- string)))
+(defun custom-xmas-add-text-properties (start end props &optional object)
+ (add-text-properties start end props object)
+ (put-text-property start end 'start-open t object)
+ (put-text-property start end 'end-open t object))
+
+(defun custom-xmas-put-text-property (start end prop value &optional object)
+ (put-text-property start end prop value object)
+ (put-text-property start end 'start-open t object)
+ (put-text-property start end 'end-open t object))
+
+(defun custom-xmas-extent-start-open ()
+ (map-extents (lambda (extent arg)
+ (set-extent-property extent 'start-open t))
+ nil (point) (min (1+ (point)) (point-max))))
+
+(if (string-match "XEmacs\\|Lucid" emacs-version)
+ (progn
+ (fset 'custom-add-text-properties 'custom-xmas-add-text-properties)
+ (fset 'custom-put-text-property 'custom-xmas-put-text-property)
+ (fset 'custom-extent-start-open 'custom-xmas-extent-start-open)
+ (fset 'custom-set-text-properties
+ (if (fboundp 'set-text-properties)
+ 'set-text-properties))
+ (fset 'custom-buffer-substring-no-properties
+ (if (fboundp 'buffer-substring-no-properties)
+ 'buffer-substring-no-properties
+ 'custom-xmas-buffer-substring-no-properties)))
+ (fset 'custom-add-text-properties 'add-text-properties)
+ (fset 'custom-put-text-property 'put-text-property)
+ (fset 'custom-extent-start-open 'ignore)
+ (fset 'custom-set-text-properties 'set-text-properties)
+ (fset 'custom-buffer-substring-no-properties
+ 'buffer-substring-no-properties))
+
+(defun custom-xmas-buffer-substring-no-properties (beg end)
+ "Return the text from BEG to END, without text properties, as a string."
+ (let ((string (buffer-substring beg end)))
+ (custom-set-text-properties 0 (length string) nil string)
+ string))
(or (fboundp 'add-to-list)
;; Introduced in Emacs 19.29.
(and (fboundp 'set-face-underline-p)
(funcall 'set-face-underline-p 'underline t))))
-(or (fboundp 'set-text-properties)
- ;; Missing in XEmacs 19.12.
- (defun set-text-properties (start end props &optional buffer)
- (if (or (null buffer) (bufferp buffer))
- (if props
- (while props
- (put-text-property
- start end (car props) (nth 1 props) buffer)
- (setq props (nthcdr 2 props)))
- (remove-text-properties start end ())))))
-
-(or (fboundp 'event-closest-point)
+(defun custom-xmas-set-text-properties (start end props &optional buffer)
+ (if (null buffer)
+ (if props
+ (while props
+ (custom-put-text-property
+ start end (car props) (nth 1 props) buffer)
+ (setq props (nthcdr 2 props)))
+ (remove-text-properties start end ()))))
+
+(or (fboundp 'event-point)
;; Missing in Emacs 19.29.
(defun event-point (event)
"Return the character position of the given mouse-motion, button-press,
(defvar custom-mouse-face nil)
(defvar custom-field-active-face nil))
-(or (and (fboundp 'modify-face) (not (featurep 'face-lock)))
- ;; Introduced in Emacs 19.29. Incompatible definition also introduced
- ;; by face-lock.el version 3.00 and above for Emacs 19.28 and below.
- ;; face-lock does not call modify-face, so we can safely redefine it.
- (defun modify-face (face foreground background stipple
- bold-p italic-p underline-p)
- "Change the display attributes for face FACE.
-FOREGROUND and BACKGROUND should be color strings or nil.
-STIPPLE should be a stipple pattern name or nil.
-BOLD-P, ITALIC-P, and UNDERLINE-P specify whether the face should be set bold,
-in italic, and underlined, respectively. (Yes if non-nil.)
-If called interactively, prompts for a face and face attributes."
- (interactive
- (let* ((completion-ignore-case t)
- (face (symbol-name (read-face-name "Modify face: ")))
- (colors (mapcar 'list x-colors))
- (stipples (mapcar 'list
- (apply 'nconc
- (mapcar 'directory-files
- x-bitmap-file-path))))
- (foreground (modify-face-read-string
- face (face-foreground (intern face))
- "foreground" colors))
- (background (modify-face-read-string
- face (face-background (intern face))
- "background" colors))
- (stipple (modify-face-read-string
- face (face-stipple (intern face))
- "stipple" stipples))
- (bold-p (y-or-n-p (concat "Set face " face " bold ")))
- (italic-p (y-or-n-p (concat "Set face " face " italic ")))
- (underline-p (y-or-n-p (concat "Set face " face " underline "))))
- (message "Face %s: %s" face
- (mapconcat 'identity
- (delq nil
- (list (and foreground (concat (downcase foreground) " foreground"))
- (and background (concat (downcase background) " background"))
- (and stipple (concat (downcase stipple) " stipple"))
- (and bold-p "bold") (and italic-p "italic")
- (and underline-p "underline"))) ", "))
- (list (intern face) foreground background stipple
- bold-p italic-p underline-p)))
- (condition-case nil (set-face-foreground face foreground) (error nil))
- (condition-case nil (set-face-background face background) (error nil))
- (condition-case nil (set-face-stipple face stipple) (error nil))
- (if (string-match "XEmacs" emacs-version)
- (progn
- (funcall (if bold-p 'make-face-bold 'make-face-unbold) face)
- (funcall (if italic-p 'make-face-italic 'make-face-unitalic) face))
- (funcall (if bold-p 'make-face-bold 'make-face-unbold) face nil t)
- (funcall (if italic-p 'make-face-italic 'make-face-unitalic) face nil t))
- (set-face-underline-p face underline-p)
- (and (interactive-p) (redraw-display))))
-
;; We can't easily check for a working intangible.
(defconst intangible (if (and (boundp 'emacs-minor-version)
(or (> emacs-major-version 19)
(> emacs-minor-version 28))))
(setq intangible 'intangible)
(setq intangible 'intangible-if-it-had-been-working))
- "The symbol making text intangible")
+ "The symbol making text intangible.")
(defconst rear-nonsticky (if (string-match "XEmacs" emacs-version)
'end-open
'rear-nonsticky)
- "The symbol making text proeprties non-sticky in the rear end.")
+ "The symbol making text properties non-sticky in the rear end.")
(defconst front-sticky (if (string-match "XEmacs" emacs-version)
'front-closed
;; Put it in the Help menu, if possible.
(if (string-match "XEmacs" emacs-version)
- ;; XEmacs (disabled because it doesn't work)
- (add-menu-item '("Help") "Customize..." 'customize nil)
+ (if (featurep 'menubar)
+ ;; XEmacs (disabled because it doesn't work)
+ (and current-menubar
+ (add-menu-item '("Help") "Customize..." 'customize t)))
;; Emacs 19.28 and earlier
(global-set-key [ menu-bar help customize ]
'("Customize..." . customize))
(defun custom-category-set (from to category)
"Make text between FROM and TWO have category CATEGORY."
- (put-text-property from to 'category category)))
+ (custom-put-text-property from to 'category category)))
;;; External Data:
;;
;; The following functions are part of the public interface to the
;; CUSTOM datastructure. Each CUSTOM describes a group of variables,
;; a single variable, or a component of a structured variable. The
-;; CUSTOM instances are part of two hiearachies, the first is the
+;; CUSTOM instances are part of two hierarchies, the first is the
;; `part-of' hierarchy in which each CUSTOM is a component of another
;; CUSTOM, except for the top level CUSTOM which is contained in
-;; `custom-data'. The second hiearachy is a `is-a' type hierarchy
+;; `custom-data'. The second hierarchy is a `is-a' type hierarchy
;; where each CUSTOM is a leaf in the hierarchy defined by the `type'
;; property and `custom-type-properties'.
((type . const)
(tag . "Off")
(default . nil))))
+ (triggle (type . choice)
+ ;; On/Off/Default.
+ (data ((type . const)
+ (tag . "On ")
+ (default . t))
+ ((type . const)
+ (tag . "Off")
+ (default . nil))
+ ((type . const)
+ (tag . "Def")
+ (default . custom:asis))))
(choice (type . default)
;; See `custom-match'.
(query . custom-choice-query)
(type . string))
"\n"
((tag . "Bold")
- (default . nil)
- (type . toggle))
+ (default . custom:asis)
+ (type . triggle))
" "
((tag . "Italic")
- (default . nil)
- (type . toggle))
+ (default . custom:asis)
+ (type . triggle))
" "
((tag . "Underline")
(hidden . t)
- (default . nil)
- (type . toggle)))
+ (default . custom:asis)
+ (type . triggle)))
(default . (custom-face-lookup "default" "default" "default"
nil nil nil))
(type . list))
(defconst custom-invalid '__invalid__
"Special value representing an invalid field.")
+(defconst custom:asis 'custom:asis)
+;; Bad, ugly, and horrible kludge.
+
(defun custom-property (custom property)
"Extract from CUSTOM property PROPERTY."
(let ((entry (assq property custom)))
(cdr entry)))
(defun custom-property-set (custom property value)
- "Set CUSTOM PROPERY to VALUE by side effect.
+ "Set CUSTOM PROPERTY to VALUE by side effect.
CUSTOM must have at least one property already."
(let ((entry (assq property custom)))
(if entry
;; FIELD datatype. The FIELD instance hold information about a
;; specific editing field in the customization buffer.
;;
-;; Each FIELD can be seen as an instanciation of a CUSTOM.
+;; Each FIELD can be seen as an instantiation of a CUSTOM.
(defvar custom-field-last nil)
;; Last field containing point.
(defun custom-field-accept (field value &optional original)
"Store a new value into field FIELD, taking it from VALUE.
-If optional ORIGINAL is non-nil, concider VALUE for the original value."
+If optional ORIGINAL is non-nil, consider VALUE for the original value."
(let ((inhibit-point-motion-hooks t))
(funcall (custom-property (custom-field-custom field) 'accept)
field value original)))
(end (make-marker))
(data (vector repeat nil start end))
field)
+ (custom-extent-start-open)
(insert-before-markers "\n")
(backward-char 1)
(set-marker start (point))
(cons (nreverse matches) values)))
(defun custom-repeat-extract (custom field)
- "Extract list of childrens values."
+ "Extract list of children's values."
(let ((values (custom-field-value field))
(data (custom-data custom))
result)
(custom-default-quote custom value)))
(defun custom-pair-extract (custom field)
- "Extract cons of childrens values."
+ "Extract cons of children's values."
(let ((values (custom-field-value field))
(data (custom-data custom))
result)
(custom-default-quote custom value)))
(defun custom-list-extract (custom field)
- "Extract list of childrens values."
+ "Extract list of children's values."
(let ((values (custom-field-value field))
(data (custom-data custom))
result)
(face-tag (custom-face-tag custom))
current)
(if face-tag
- (put-text-property from (+ from (length (custom-tag custom)))
+ (custom-put-text-property from (+ from (length (custom-tag custom)))
'face (funcall face-tag field value)))
(if original
(custom-field-original-set field value))
()
(setq begin (point)
found (custom-insert (custom-property custom 'none) nil))
- (add-text-properties begin (point)
- (list rear-nonsticky t
- 'face custom-field-uninitialized-face)))
+ (custom-add-text-properties
+ begin (point)
+ (list rear-nonsticky t
+ 'face custom-field-uninitialized-face)))
(or original
(custom-field-original-set found (custom-field-original field)))
(custom-field-accept found value original)
(custom-field-move field from end))))
(defun custom-choice-extract (custom field)
- "Extract childs value."
+ "Extract child's value."
(let ((value (custom-field-value field)))
(custom-field-extract (custom-field-custom value) value)))
(defun custom-choice-validate (custom field)
- "Validate childs value."
+ "Validate child's value."
(let ((value (custom-field-value field))
(custom (custom-field-custom field)))
(if (or (eq value custom-nil)
(defun custom-face-import (custom value)
"Modify CUSTOM's VALUE to match internal expectations."
- (let ((name (symbol-name value)))
+ (let ((name (or (and (facep value) (symbol-name (face-name value)))
+ (symbol-name value))))
(list (if (string-match "\
custom-face-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)"
name)
(intern (match-string 6 name)))
value))))
-(defun custom-face-lookup (fg bg stipple bold italic underline)
- "Lookup or create a face with specified attributes.
-FG BG STIPPLE BOLD ITALIC UNDERLINE"
+(defun custom-face-lookup (&optional fg bg stipple bold italic underline)
+ "Lookup or create a face with specified attributes."
(let ((name (intern (format "custom-face-%s-%s-%s-%S-%S-%S"
(or fg "default")
(or bg "default")
(if (and (custom-facep name)
(fboundp 'make-face))
()
- (make-face name)
- (modify-face name
- (if (string-equal fg "default") nil fg)
- (if (string-equal bg "default") nil bg)
- (if (string-equal stipple "default") nil stipple)
- bold italic underline))
+ (copy-face 'default name)
+ (when (and fg
+ (not (string-equal fg "default")))
+ (condition-case ()
+ (set-face-foreground name fg)
+ (error nil)))
+ (when (and bg
+ (not (string-equal bg "default")))
+ (condition-case ()
+ (set-face-background name bg)
+ (error nil)))
+ (when (and stipple
+ (not (string-equal stipple "default"))
+ (not (eq stipple 'custom:asis))
+ (fboundp 'set-face-stipple))
+ (set-face-stipple name stipple))
+ (when (and bold
+ (not (eq bold 'custom:asis)))
+ (condition-case ()
+ (make-face-bold name)
+ (error nil)))
+ (when (and italic
+ (not (eq italic 'custom:asis)))
+ (condition-case ()
+ (make-face-italic name)
+ (error nil)))
+ (when (and underline
+ (not (eq underline 'custom:asis)))
+ (condition-case ()
+ (set-face-underline-p name t)
+ (error nil))))
name))
(defun custom-face-hack (field value)
"Face that should be used for highlighting FIELD containing VALUE."
(let* ((custom (custom-field-custom field))
- (face (eval (funcall (custom-property custom 'export)
- custom value))))
+ (form (funcall (custom-property custom 'export) custom value))
+ (face (apply (car form) (cdr form))))
(if (custom-facep face) face nil)))
(defun custom-const-insert (custom level)
(face (custom-field-face field))
(from (point)))
(custom-text-insert (custom-tag custom))
- (add-text-properties from (point)
+ (custom-add-text-properties from (point)
(list 'face face
rear-nonsticky t))
(custom-documentation-insert custom)
"Update face of FIELD."
(let ((from (custom-field-start field))
(custom (custom-field-custom field)))
- (put-text-property from (+ from (length (custom-tag custom)))
+ (custom-put-text-property from (+ from (length (custom-tag custom)))
'face (custom-field-face field))))
(defun custom-const-valid (custom value)
(cond ((eq value custom-nil)
(cons start "Uninitialized field"))
((and (consp value) (eq (car value) custom-invalid))
- (cons start "Unparseable field content"))
+ (cons start "Unparsable field content"))
((custom-valid custom value)
nil)
(t
(let ((from (point)))
(insert tag)
(custom-category-set from (point) 'custom-button-properties)
- (put-text-property from (point) 'custom-tag field)
+ (custom-put-text-property from (point) 'custom-tag field)
(if data
- (add-text-properties from (point) (list 'custom-data data)))))
+ (custom-add-text-properties from (point) (list 'custom-data data)))))
(defun custom-documentation-insert (custom &rest ignore)
"Insert documentation from CUSTOM in current buffer."
"Describe how to execute COMMAND."
(let ((from (point)))
(insert "`" (key-description (where-is-internal command nil t)) "'")
- (set-text-properties from (point)
- (list 'face custom-button-face
- mouse-face custom-mouse-face
- 'custom-jump t ;Make TAB jump over it.
- 'custom-tag command))
+ (custom-set-text-properties from (point)
+ (list 'face custom-button-face
+ mouse-face custom-mouse-face
+ 'custom-jump t ;Make TAB jump over it.
+ 'custom-tag command
+ 'start-open t
+ 'end-open t))
(custom-category-set from (point) 'custom-documentation-properties))
(custom-help-insert ": " (custom-first-line (documentation command)) "\n"))
;; The Customization major mode and interactive commands.
(defvar custom-mode-map nil
- "Keymap for Custum Mode.")
+ "Keymap for Custom Mode.")
(if custom-mode-map
nil
(setq custom-mode-map (make-sparse-keymap))
(insert-char (custom-padding custom)
(- (custom-width custom) (- (point) from)))
(custom-field-move field from (point))
- (set-text-properties
+ (custom-set-text-properties
from (point)
(list 'custom-field field
'custom-tag field
'face (custom-field-face field)
- front-sticky t))))
+ 'start-open t
+ 'end-open t))))
(defun custom-field-read (field)
;; Read the screen content of FIELD.
(custom-read (custom-field-custom field)
- (buffer-substring-no-properties (custom-field-start field)
+ (custom-buffer-substring-no-properties (custom-field-start field)
(custom-field-end field))))
;; Fields are shown in a special `active' face when point is inside
;; Deactivate FIELD.
(let ((before-change-functions nil)
(after-change-functions nil))
- (put-text-property (custom-field-start field) (custom-field-end field)
+ (custom-put-text-property (custom-field-start field) (custom-field-end field)
'face (custom-field-face field))))
(defun custom-field-enter (field)
(setq pos (1- pos)))
(if (< pos (point))
(goto-char pos))))
- (put-text-property start end 'face custom-field-active-face)))
+ (custom-put-text-property start end 'face custom-field-active-face)))
(defun custom-field-resize (field)
;; Resize FIELD after change.
(let ((field custom-field-was))
(custom-assert '(prog1 field (setq custom-field-was nil)))
;; Prevent mixing fields properties.
- (put-text-property begin end 'custom-field field)
+ (custom-put-text-property begin end 'custom-field field)
;; Update the field after modification.
(if (eq (custom-field-property begin) field)
(let ((field-end (custom-field-end field)))