;;
;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
;; Keywords: help, faces
-;; Version: 1.9914
+;; Version: 1.9920
;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
;; This file is part of GNU Emacs.
(or (not hidden)
(memq category custom-magic-show-hidden)))
(insert " ")
+ (when (eq category 'group)
+ (insert-char ?\ (1+ (* 2 (widget-get parent :custom-level)))))
(push (widget-create-child-and-convert
widget 'choice-item
:help-echo "Change the state of this item."
;; We recognize extra escape sequences.
(let* ((buttons (widget-get widget :buttons))
(state (widget-get widget :custom-state))
- (level (widget-get widget :custom-level)))
+ (level (widget-get widget :custom-level))
+ (category (widget-get widget :custom-category)))
(cond ((eq escape ?l)
(when level
(insert-char ?\ (1- level))
(when (and level (not (eq state 'hidden)))
(insert-char ?- (- 76 (current-column) level))
(insert "\\")))
+ ((eq escape ?i)
+ (insert-char ?\ (+ 1 level level)))
((eq escape ?L)
(push (widget-create-child-and-convert
widget 'visibility
+ :help-echo "Show or hide this group."
:action 'custom-toggle-parent
(not (eq state 'hidden)))
buttons))
(and (eq (preceding-char) ?\n)
(widget-get widget :indent)
(insert-char ? (widget-get widget :indent)))
+ (when (eq category 'group)
+ (insert-char ?\ (1+ (* 2 level))))
(insert "See also ")
(while links
(push (widget-create-child-and-convert widget (car links))
(t
(widget-put widget :documentation-shown nil)
(widget-put widget :custom-state 'hidden)))
- (custom-redraw widget)))
+ (custom-redraw widget)
+ (widget-setup)))
(defun custom-toggle-parent (widget &rest ignore)
"Toggle visibility of parent to WIDGET."
buttons)
(push (widget-create-child-and-convert
widget 'visibility
+ :help-echo "Show the value of this option."
:action 'custom-toggle-parent
nil)
buttons))
(insert (symbol-name symbol) ": ")
(push (widget-create-child-and-convert
widget 'visibility
+ :help-echo "Hide the value of this option."
:action 'custom-toggle-parent
t)
buttons)
widget 'item
:format tag-format
:action 'custom-tag-action
+ :help-echo "Change value of this option."
:mouse-down-action 'custom-tag-mouse-down-action
:button-face 'custom-variable-button-face
:sample-face 'custom-variable-sample-face
(insert " ")
(push (widget-create-child-and-convert
widget 'visibility
+ :help-echo "Hide the value of this option."
:action 'custom-toggle-parent
t)
buttons)
(widget-put widget :custom-state state)))
(defvar custom-variable-menu
- '(("Edit" custom-variable-edit
- (lambda (widget)
- (not (eq (widget-get widget :custom-form) 'edit))))
- ("Edit Lisp" custom-variable-edit-lisp
- (lambda (widget)
- (not (eq (widget-get widget :custom-form) 'lisp))))
- ("Set" custom-variable-set
+ '(("Set" custom-variable-set
(lambda (widget)
(eq (widget-get widget :custom-state) 'modified)))
("Save" custom-variable-save
(lambda (widget)
(and (get (widget-value widget) 'standard-value)
(memq (widget-get widget :custom-state)
- '(modified set changed saved rogue))))))
+ '(modified set changed saved rogue)))))
+ ("---" ignore ignore)
+ ("Don't show as Lisp expression" custom-variable-edit
+ (lambda (widget)
+ (not (eq (widget-get widget :custom-form) 'edit))))
+ ("Show as Lisp expression" custom-variable-edit-lisp
+ (lambda (widget)
+ (not (eq (widget-get widget :custom-form) 'lisp)))))
"Alist of actions for the `custom-variable' widget.
Each entry has the form (NAME ACTION FILTER) where NAME is the name of
the menu entry, ACTION is the function to call on the widget when the
(message "Creating face editor...done")))
(defvar custom-face-menu
- '(("Edit Selected" custom-face-edit-selected
- (lambda (widget)
- (not (eq (widget-get widget :custom-form) 'selected))))
- ("Edit All" custom-face-edit-all
- (lambda (widget)
- (not (eq (widget-get widget :custom-form) 'all))))
- ("Edit Lisp" custom-face-edit-lisp
- (lambda (widget)
- (not (eq (widget-get widget :custom-form) 'lisp))))
- ("Set" custom-face-set)
+ '(("Set" custom-face-set)
("Save" custom-face-save)
("Reset to Saved" custom-face-reset-saved
(lambda (widget)
(get (widget-value widget) 'saved-face)))
("Reset to Standard Setting" custom-face-reset-standard
(lambda (widget)
- (get (widget-value widget) 'face-defface-spec))))
+ (get (widget-value widget) 'face-defface-spec)))
+ ("---" ignore ignore)
+ ("Show all display specs" custom-face-edit-all
+ (lambda (widget)
+ (not (eq (widget-get widget :custom-form) 'all))))
+ ("Just current attributes" custom-face-edit-selected
+ (lambda (widget)
+ (not (eq (widget-get widget :custom-form) 'selected))))
+ ("Show as Lisp expression" custom-face-edit-lisp
+ (lambda (widget)
+ (not (eq (widget-get widget :custom-form) 'lisp)))))
"Alist of actions for the `custom-face' widget.
Each entry has the form (NAME ACTION FILTER) where NAME is the name of
the menu entry, ACTION is the function to call on the widget when the
(define-widget 'custom-group 'custom
"Customize group."
- :format "%l %{%t%} group: %L %-\n%m%h%a%v%e"
+ :format "%l %{%t%} group: %L %-\n%m%i%h%a%v%e"
:sample-face-get 'custom-group-sample-face-get
:documentation-property 'group-documentation
:help-echo "Set or reset all members of this group."
;;
;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
;; Keywords: extensions
-;; Version: 1.9914
+;; Version: 1.9920
;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
;; This file is part of GNU Emacs.
:group 'extensions
:group 'hypermedia)
+(defgroup widget-documentation nil
+ "Options controling the display of documentation strings."
+ :group 'widgets)
+
(defgroup widget-faces nil
"Faces used by the widget library."
:group 'widgets
:group 'faces)
+(defface widget-documentation-face '((((class color)
+ (background dark))
+ (:foreground "lime green"))
+ (((class color)
+ (background light))
+ (:foreground "dark green"))
+ (t nil))
+ "Face used for documentation text."
+ :group 'widget-documentation
+ :group 'widget-faces)
+
(defface widget-button-face '((t (:bold t)))
"Face used for widget buttons."
:group 'widget-faces)
'start-open nil
'end-open nil)))
+(defcustom widget-field-add-space
+ (or (< emacs-major-version 20)
+ (and (eq emacs-major-version 20)
+ (< emacs-minor-version 3))
+ (not (string-match "XEmacs" emacs-version)))
+ "Non-nil means add extra space at the end of editable text fields.
+
+This is needed on all versions of Emacs, and on XEmacs before 20.3.
+If you don't add the space, it will become impossible to edit a zero
+size field."
+ :type 'boolean
+ :group 'widgets)
+
(defun widget-specify-field (widget from to)
"Specify editable button for WIDGET between FROM and TO."
(put-text-property from to 'read-only nil)
;; at the end of the overlay.
(save-excursion
(goto-char to)
- (insert-and-inherit " ")
+ (when widget-field-add-space
+ (insert-and-inherit " "))
(setq to (point)))
(add-text-properties (1- to) to ;to (1+ to)
'(front-sticky nil start-open t read-only to))
(add-text-properties from to (list 'start-open t
'end-open t
'face face)))))
-
(defun widget-specify-doc (widget from to)
;; Specify documentation for WIDGET between FROM and TO.
(add-text-properties from to (list 'widget-doc widget
(defun widget-apply-action (widget &optional event)
"Apply :action in WIDGET in response to EVENT."
- (let (after-change-functions)
- (if (widget-apply widget :active)
- (widget-apply widget :action event)
- (error "Attempt to perform action on inactive widget"))))
+ (if (widget-apply widget :active)
+ (widget-apply widget :action event)
+ (error "Attempt to perform action on inactive widget")))
;;; Helper functions.
;;
(let ((ext (make-extent (point) (1- (point))))
(help-echo (widget-get widget :help-echo)))
(set-extent-property ext 'invisible t)
+ (set-extent-property ext 'start-open t)
+ (set-extent-property ext 'end-open t)
(set-extent-end-glyph ext glyph)
(when help-echo
(set-extent-property ext 'balloon-help help-echo)
(apply 'insert args)
(widget-specify-text from (point))))
-(defun widget-convert-text (type from to &optional button-from button-to)
+(defun widget-convert-text (type from to
+ &optional button-from button-to
+ &rest args)
"Return a widget of type TYPE with endpoint FROM TO.
-No text will be inserted to the buffer, instead the text between FROM
+Optional ARGS are extra keyword arguments for TYPE.
and TO will be used as the widgets end points. If optional arguments
BUTTON-FROM and BUTTON-TO are given, these will be used as the widgets
-button end points."
- (let ((widget (widget-convert type))
+button end points.
+Optional ARGS are extra keyword arguments for TYPE."
+ (let ((widget (apply 'widget-convert type :delete 'widget-leave-text args))
(from (copy-marker from))
(to (copy-marker to)))
(widget-specify-text from to)
(widget-specify-button widget button-from button-to))
widget))
-(defun widget-convert-button (type from to)
+(defun widget-convert-button (type from to &rest args)
"Return a widget of type TYPE with endpoint FROM TO.
+Optional ARGS are extra keyword arguments for TYPE.
No text will be inserted to the buffer, instead the text between FROM
and TO will be used as the widgets end points, as well as the widgets
button end points."
- (widget-convert-text type from to from to))
+ (apply 'widget-convert-text type from to from to args))
+
+(defun widget-leave-text (widget)
+ "Remove markers and overlays from WIDGET and its children."
+ (let ((from (widget-get widget :from))
+ (to (widget-get widget :to))
+ (button (widget-get widget :button-overlay))
+ (field (widget-get widget :field-overlay))
+ (children (widget-get widget :children)))
+ (set-marker from nil)
+ (set-marker to nil)
+ (delete-overlay button)
+ (delete-overlay field)
+ (mapcar 'widget-leave-text children)))
;;; Keymap and Commands.
(when (commandp command)
(call-interactively command))))))
+(defun widget-tabable-at (&optional pos)
+ "Return the tabable widget at POS, or nil.
+POS defaults to the value of (point)."
+ (unless pos
+ (setq pos (point)))
+ (let ((widget (or (get-char-property (point) 'button)
+ (get-char-property (point) 'field))))
+ (if widget
+ (let ((order (widget-get widget :tab-order)))
+ (if order
+ (if (>= order 0)
+ widget
+ nil)
+ widget))
+ nil)))
+
(defun widget-move (arg)
"Move point to the ARG next field or button.
ARG may be negative to move backward."
(or (bobp) (> arg 0) (backward-char))
(let ((pos (point))
(number arg)
- (old (or (get-char-property (point) 'button)
- (get-char-property (point) 'field)))
+ (old (widget-tabable-at))
new)
;; Forward.
(while (> arg 0)
(and (eq pos (point))
(eq arg number)
(error "No buttons or fields found"))
- (let ((new (or (get-char-property (point) 'button)
- (get-char-property (point) 'field))))
+ (let ((new (widget-tabable-at)))
(when new
(unless (eq new old)
- (unless (and (widget-get new :tab-order)
- (< (widget-get new :tab-order) 0))
- (setq arg (1- arg)))
+ (setq arg (1- arg))
(setq old new)))))
;; Backward.
(while (< arg 0)
(and (eq pos (point))
(eq arg number)
(error "No buttons or fields found"))
- (let ((new (or (get-char-property (point) 'button)
- (get-char-property (point) 'field))))
+ (let ((new (widget-tabable-at)))
(when new
(unless (eq new old)
- (unless (and (widget-get new :tab-order)
- (< (widget-get new :tab-order) 0))
- (setq arg (1+ arg)))))))
- (while (or (get-char-property (point) 'button)
- (get-char-property (point) 'field))
- (backward-char))
+ (setq arg (1+ arg))))))
+ (let ((new (widget-tabable-at)))
+ (while (eq (widget-tabable-at) new)
+ (backward-char)))
(forward-char))
(widget-echo-help (point))
(run-hooks 'widget-move-hook))
(widget-clear-undo)
;; We need to maintain text properties and size of the editing fields.
(make-local-variable 'after-change-functions)
- (if (and widget-field-list)
+ (if widget-field-list
(setq after-change-functions '(widget-after-change))
(setq after-change-functions nil)))
"Return the end of WIDGET's editing field."
(let ((overlay (widget-get widget :field-overlay)))
;; Don't subtract one if local-map works at the end of the overlay.
- (and overlay (1- (overlay-end overlay)))))
+ (and overlay (if widget-field-add-space
+ (1- (overlay-end overlay))
+ (overlay-end overlay)))))
(defun widget-field-find (pos)
"Return the field at POS.
(when field
(unless (eq field other)
(debug "Change in different fields"))
- (let ((size (widget-get field :size)))
+ (let ((size (widget-get field :size))
+ (secret (widget-get field :secret)))
(when size
(let ((begin (widget-field-start field))
(end (widget-field-end field)))
(goto-char end)
(while (and (eq (preceding-char) ?\ )
(> (point) begin))
- (delete-backward-char 1))))))))
+ (delete-backward-char 1)))))))
+ (when secret
+ (let ((begin (widget-field-start field))
+ (end (widget-field-end field)))
+ (when size
+ (while (and (> end begin)
+ (eq (char-after (1- end)) ?\ ))
+ (setq end (1- end))))
+ (while (< begin end)
+ (let ((old (char-after begin)))
+ (unless (eq old secret)
+ (subst-char-in-region begin (1+ begin) old secret)
+ (put-text-property begin (1+ begin) 'secret old))
+ (setq begin (1+ begin)))))))
(widget-apply field :notify field)))
(error (debug "After Change"))))
(widget-get widget :value)))))
(doc-text (and (stringp doc-try)
(> (length doc-try) 1)
- doc-try)))
+ doc-try))
+ (doc-indent (widget-get widget :documentation-indent)))
(when doc-text
(and (eq (preceding-char) ?\n)
(widget-get widget :indent)
(setq doc-text (substring doc-text 0 (match-beginning 0))))
(push (widget-create-child-and-convert
widget 'documentation-string
+ :indent (cond ((numberp doc-indent )
+ doc-indent)
+ ((null doc-indent)
+ nil)
+ (t 0))
doc-text)
buttons))))
(t
(widget-glyph-insert widget off "right" "right-pushed")
(insert "..."))))
-;;; The `documentation-string' Widget.
+;;; The `documentation-link' Widget.
+;;
+;; This is a helper widget for `documentation-string'.
-(defface widget-documentation-face '((((class color)
- (background dark))
- (:foreground "lime green"))
- (((class color)
- (background light))
- (:foreground "dark green"))
- (t nil))
- "Face used for documentation text."
- :group 'widget-faces)
+(define-widget 'documentation-link 'link
+ "Link type used in documentation strings."
+ :tab-order -1
+ :help-echo 'widget-documentation-link-echo-help
+ :action 'widget-documentation-link-action)
+
+(defun widget-documentation-link-echo-help (widget)
+ "Tell what this link will describe."
+ (concat "Describe the `" (widget-get widget :value) "' symbol."))
+
+(defun widget-documentation-link-action (widget &optional event)
+ "Run apropos on WIDGET's value. Ignore optional argument EVENT."
+ (apropos (concat "\\`" (regexp-quote (widget-get widget :value)) "\\'")))
+
+(defcustom widget-documentation-links t
+ "Add hyperlinks to documentation strings when non-nil."
+ :type 'boolean
+ :group 'widget-documentation)
+
+(defcustom widget-documentation-link-regexp "`\\([^\n`' ]+\\)'"
+ "Regexp for matching potential links in documentation strings.
+The first group should be the link itself."
+ :type 'regexp
+ :group 'widget-documentation)
+
+(defcustom widget-documentation-link-p 'intern-soft
+ "Predicate used to test if a string is useful as a link.
+The value should be a function. The function will be called one
+argument, a string, and should return non-nil if there should be a
+link for that string."
+ :type 'function
+ :options '(widget-documentation-link-p)
+ :group 'widget-documentation)
+
+(defcustom widget-documentation-link-type 'documentation-link
+ "Widget type used for links in documentation strings."
+ :type 'symbol
+ :group 'widget-documentation)
+
+(defun widget-documentation-link-add (widget from to)
+ (widget-specify-doc widget from to)
+ (when widget-documentation-links
+ (let ((regexp widget-documentation-link-regexp)
+ (predicate widget-documentation-link-p)
+ (type widget-documentation-link-type)
+ (buttons (widget-get widget :buttons)))
+ (save-excursion
+ (goto-char from)
+ (while (re-search-forward regexp to t)
+ (let ((name (match-string 1))
+ (begin (match-beginning 0))
+ (end (match-end 0)))
+ (when (funcall predicate name)
+ (push (widget-convert-button type begin end :value name)
+ buttons)))))
+ (widget-put widget :buttons buttons)))
+ (let ((indent (widget-get widget :indent)))
+ (when (and indent (not (zerop indent)))
+ (save-excursion
+ (save-restriction
+ (narrow-to-region from to)
+ (goto-char (point-min))
+ (while (search-forward "\n" nil t)
+ (insert-char ?\ indent)))))))
+
+;;; The `documentation-string' Widget.
(define-widget 'documentation-string 'item
"A documentation string."
(defun widget-documentation-string-value-create (widget)
;; Insert documentation string.
(let ((doc (widget-value widget))
+ (indent (widget-get widget :indent))
(shown (widget-get (widget-get widget :parent) :documentation-shown))
(start (point)))
(if (string-match "\n" doc)
(after (substring doc (match-beginning 0)))
buttons)
(insert before " ")
- (widget-specify-doc widget start (point))
+ (widget-documentation-link-add widget start (point))
(push (widget-create-child-and-convert
widget 'visibility
+ :help-echo "Show or hide rest of the documentation."
:off nil
:action 'widget-parent-action
shown)
buttons)
(when shown
(setq start (point))
+ (when (and indent (not (zerop indent)))
+ (insert-char ?\ indent))
(insert after)
- (widget-specify-doc widget start (point)))
+ (widget-documentation-link-add widget start (point)))
(widget-put widget :buttons buttons))
(insert doc)
- (widget-specify-doc widget start (point))))
+ (widget-documentation-link-add widget start (point))))
(insert "\n"))
(defun widget-documentation-string-action (widget &rest ignore)
(define-widget 'choice 'menu-choice
"A union of several sexp types."
:tag "Choice"
- :format "%[%t%]: %v"
+ :format "%{%t%}: %[value menu%] %v"
+ :button-prefix 'widget-push-button-prefix
+ :button-suffix 'widget-push-button-suffix
:prompt-value 'widget-choice-prompt-value)
(defun widget-choice-prompt-value (widget prompt value unbound)
"To be nil or non-nil, that is the question."
:tag "Boolean"
:prompt-value 'widget-boolean-prompt-value
- :format "%[%t%]: %v\n")
+ :button-prefix 'widget-push-button-prefix
+ :button-suffix 'widget-push-button-suffix
+ :format "%{%t%}: %[toggle%] %v\n")
(defun widget-boolean-prompt-value (widget prompt value unbound)
;; Toggle a boolean.