Mercurial > hg > xemacs-beta
diff lisp/custom/wid-edit.el @ 207:e45d5e7c476e r20-4b2
Import from CVS: tag r20-4b2
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:03:52 +0200 |
parents | 850242ba4a81 |
children |
line wrap: on
line diff
--- a/lisp/custom/wid-edit.el Mon Aug 13 10:02:48 2007 +0200 +++ b/lisp/custom/wid-edit.el Mon Aug 13 10:03:52 2007 +0200 @@ -37,18 +37,12 @@ (autoload 'pp-to-string "pp") (autoload 'finder-commentary "finder" nil t) -(defun widget-event-point (event) - "Character position of the end of event if that exists, or nil." - (if (mouse-event-p event) - (event-point event) - nil)) - ;;; Customization. (defgroup widgets nil "Customization support for the Widget Library." :link '(custom-manual "(widget)Top") - :link '(url-link :tag "Development Page" + :link '(url-link :tag "Development Page" "http://www.dina.kvl.dk/~abraham/custom/") :link '(emacs-library-link :tag "Lisp File" "widget.el") :prefix "widget-" @@ -98,7 +92,7 @@ (((class grayscale color) (background dark)) (:background "dim gray")) - (t + (t (:italic t))) "Face used for editable fields." :group 'widget-faces) @@ -110,7 +104,7 @@ ; (((class grayscale color) ; (background dark)) ; (:background "dim gray")) -; (t +; (t ; (:italic t))) ; "Face used for editable fields spanning only a single line." ; :group 'widget-faces) @@ -200,7 +194,7 @@ (cons title (mapcar (lambda (x) (if (stringp x) - (vector x nil nil) + (vector x nil nil) (vector (car x) (list (car x)) t))) items))))) (setq val (and val @@ -249,7 +243,8 @@ (lookup-key overriding-terminal-local-map (read-key-sequence (concat title ": ") t))))) (message "") - (when (eq value 'keyboard-quit) + (when (or (eq value 'keyboard-quit) + (null value)) (error "Canceled")) value)) (t @@ -265,8 +260,8 @@ ;;; Widget text specifications. -;; -;; These functions are for specifying text properties. +;; +;; These functions are for specifying text properties. (defcustom widget-field-add-space t ;; Setting this to nil might be available, once some problems are resolved. @@ -282,7 +277,7 @@ (> emacs-major-version 19)) (not (string-match "XEmacs" emacs-version))) "Non-nil means use `before-change-functions' to track editable fields. -This enables the use of undo, but doesn't work on Emacs 19.34 and earlier. +This enables the use of undo, but doesn't work on Emacs 19.34 and earlier. Using before hooks also means that the :notify function can't know the new value." :type 'boolean @@ -305,14 +300,14 @@ (help-echo (widget-get widget :help-echo)) (extent (make-extent from to))) (unless (or (stringp help-echo) (null help-echo)) - (setq help-echo 'widget-mouse-help)) + (setq help-echo 'widget-mouse-help)) (widget-put widget :field-extent extent) (and (or (not widget-field-add-space) (widget-get widget :size)) (set-extent-property extent 'end-closed t)) (set-extent-property extent 'detachable nil) (set-extent-property extent 'field widget) - (set-extent-property extent 'tabable t) + (set-extent-property extent 'button-or-field t) (set-extent-property extent 'keymap map) (set-extent-property extent 'face face) (set-extent-property extent 'balloon-help help-echo) @@ -322,17 +317,19 @@ "Specify button for WIDGET between FROM and TO." (let ((face (widget-apply widget :button-face-get)) (help-echo (widget-get widget :help-echo)) - (extent (make-extent from to))) + (extent (make-extent from to)) + (map (widget-get widget :button-keymap))) (widget-put widget :button-extent extent) (unless (or (null help-echo) (stringp help-echo)) (setq help-echo 'widget-mouse-help)) (set-extent-property extent 'start-open t) (set-extent-property extent 'button widget) - (set-extent-property extent 'tabable t) + (set-extent-property extent 'button-or-field t) (set-extent-property extent 'mouse-face widget-mouse-face) (set-extent-property extent 'balloon-help help-echo) (set-extent-property extent 'help-echo help-echo) - (set-extent-property extent 'face face))) + (set-extent-property extent 'face face) + (set-extent-property extent 'keymap map))) (defun widget-mouse-help (extent) "Find mouse help string for button in extent." @@ -378,51 +375,112 @@ (delete-region (1- (point-max)) (point-max)) (goto-char (point-max)))))) +(put 'widget-specify-insert 'edebug-form-spec '(&rest form)) + + +;;; Inactive Widgets. + (defface widget-inactive-face '((((class grayscale color) (background dark)) (:foreground "light gray")) (((class grayscale color) (background light)) (:foreground "dim gray")) - (t + (t (:italic t))) "Face used for inactive widgets." :group 'widget-faces) +;; For inactiveness to work on complex structures, it is not +;; sufficient to keep track of whether a button/field/glyph is +;; inactive or not -- we must know how many time it was deactivated +;; (inactiveness level). Successive deactivations of the same button +;; increment its inactive-count, and activations decrement it. When +;; inactive-count reaches 0, the button/field/glyph is reactivated. + +(defun widget-activation-widget-mapper (extent action) + "Activate or deactivate EXTENT's widget (button or field). +Suitable for use with `map-extents'." + (ecase action + (:activate + (decf (extent-property extent :inactive-count)) + (when (zerop (extent-property extent :inactive-count)) + (set-extent-properties + extent (extent-property extent :inactive-plist)) + (set-extent-property extent :inactive-plist nil))) + (:deactivate + (incf (extent-property extent :inactive-count 0)) + ;; Store a plist of old properties, which will be fed to + ;; `set-extent-properties'. + (unless (extent-property extent :inactive-plist) + (set-extent-property + extent :inactive-plist + (list 'mouse-face (extent-property extent 'mouse-face) + 'help-echo (extent-property extent 'help-echo) + 'keymap (extent-property extent 'keymap))) + (set-extent-properties + extent '(mouse-face nil help-echo nil keymap nil))))) + nil) + +(defun widget-activation-glyph-mapper (extent action) + (let ((activate-p (if (eq action :activate) t nil))) + (if activate-p + (decf (extent-property extent :inactive-count)) + (incf (extent-property extent :inactive-count 0))) + (when (or (and activate-p + (zerop (extent-property extent :inactive-count))) + (and (not activate-p) + (not (zerop (extent-property extent :inactive-count))))) + (let* ((glyph-widget (extent-property extent 'glyph-widget)) + (up-glyph (widget-get glyph-widget :glyph-up)) + (inactive-glyph (widget-get glyph-widget :glyph-inactive)) + (new-glyph (if activate-p up-glyph inactive-glyph))) + ;; Check that the new glyph exists, and differs from the + ;; default one. + (and up-glyph inactive-glyph (not (eq up-glyph inactive-glyph)) + ;; Check if the glyph is already installed. + (not (eq (extent-end-glyph extent) new-glyph)) + ;; Change it. + (set-extent-end-glyph extent new-glyph))))) + nil) + (defun widget-specify-inactive (widget from to) "Make WIDGET inactive for user modifications." (unless (widget-get widget :inactive) (let ((extent (make-extent from to))) - (set-extent-property extent 'start-open t) - (set-extent-property extent 'face 'widget-inactive-face) - ;; This is disabled, as it makes the mouse cursor change shape. - ;(set-extent-property extent 'mouse-face 'widget-inactive-face) - ;; ...actually, in XEmacs, we can easily choose our own pointer - ;; shapes. However, the mouse-face of the "inner" extent will - ;; still be drawn. - (set-extent-property extent 'detachable t) - (set-extent-property extent 'priority 100) - (set-extent-property extent 'read-only 't) - (widget-put widget :inactive extent)))) - -;; We don't have modification functions, so this is unused. -;(defun widget-overlay-inactive (&rest junk) -; "Ignoring the arguments, signal an error." -; (unless inhibit-read-only -; (error "Attempt to modify inactive widget"))) - + ;; It is no longer necessary for the extent to be read-only, as + ;; the inactive editable fields now lose their keymaps. + (set-extent-properties + extent '(start-open t face widget-inactive-face + detachable t priority 2001 widget-inactive t)) + (widget-put widget :inactive extent)) + ;; Deactivate the buttons and fields within the range. In some + ;; cases, the fields are not yet setup at the time this function + ;; is called. Those fields are deactivated explicitly by + ;; `widget-setup'. + (map-extents 'widget-activation-widget-mapper + nil from to :deactivate nil 'button-or-field) + ;; Deactivate glyphs. + (map-extents 'widget-activation-glyph-mapper + nil from to :deactivate nil 'glyph-widget))) (defun widget-specify-active (widget) "Make WIDGET active for user modifications." (let ((inactive (widget-get widget :inactive))) (when inactive + ;; Reactivate the buttons and fields covered by the extent. + (map-extents 'widget-activation-widget-mapper + inactive nil nil :activate nil 'button-or-field) + ;; Reactivate the glyphs. + (map-extents 'widget-activation-glyph-mapper + inactive nil nil :activate nil 'end-glyph) (delete-extent inactive) (widget-put widget :inactive nil)))) ;;; Widget Properties. -(defun widget-type (widget) +(defsubst widget-type (widget) "Return the type of WIDGET, a symbol." (car widget)) @@ -448,13 +506,13 @@ missing nil)) ((setq tmp (car widget)) (setq widget (get tmp 'widget-type))) - (t + (t (setq missing nil)))) value))) (defun widget-get-indirect (widget property) "In WIDGET, get the value of PROPERTY. -If the value is a symbol, return its binding. +If the value is a symbol, return its binding. Otherwise, just return the value." (let ((value (widget-get widget property))) (if (symbolp value) @@ -583,9 +641,13 @@ xbm, gif, jpg, or png) located in `widget-glyph-directory', or in one of the data directories. It can also be a valid image instantiator, in which case it will be - used to make the glyph, with an additional TAG string fallback. -If IMAGE is a list, it will be given unchanged to `make-glyph'." - (cond ((not (and image widget-glyph-enable)) + used to make the glyph, with an additional TAG string fallback." + (cond ((not (and image widget-glyph-enable + ;; We don't use glyphs on TTY consoles, although we + ;; could. However, glyph faces aren't yet working + ;; properly, and movement through glyphs is + ;; unintuitive. + (console-on-window-system-p))) ;; We don't want to use glyphs. nil) ((glyphp image) @@ -601,6 +663,9 @@ (formats widget-image-conversion) file) (while (and formats (not file)) + ;; This dance is necessary, because XEmacs signals an + ;; error when it encounters an unrecognized image + ;; format. (when (valid-image-instantiator-format-p (caar formats)) (setq file (locate-file image dirlist (mapconcat 'identity (cdar formats) @@ -613,16 +678,12 @@ (let ((glyph (make-glyph `([,(caar formats) :file ,file] [string :data ,tag])))) ;; Cache the glyph - (setq widget-glyph-cache - (lax-plist-put widget-glyph-cache image glyph)) + (laxputf widget-glyph-cache image glyph) ;; ...and return it glyph))))) ((valid-instantiator-p image 'image) ;; A valid image instantiator (e.g. [gif :file "somefile"] etc.) (make-glyph `(,image [string :data ,tag]))) - ((consp image) - ;; This could be virtually anything. Let `make-glyph' sort it out. - (make-glyph image)) (t ;; Oh well. nil))) @@ -634,18 +695,30 @@ `widget-glyph-directory', or anything else allowed by `widget-glyph-find'. -Optional arguments DOWN and INACTIVE is used instead of IMAGE when the -glyph is pressed or inactive, respectively. - -Instead of an instantiator, you can also use a list of instantiators, -or whatever `make-glyph' will accept. However, in that case you must -provide the fallback TAG as a part of the instantiator yourself." +If IMAGE is a list, it will be taken as a list of (UP DOWN INACTIVE) +glyphs. The down and inactive glyphs are shown when glyph is pressed +or inactive, respectively. + +The optional DOWN and INACTIVE arguments are deprecated, and exist +only because of compatibility." + ;; Convert between IMAGE being a list, etc. Must use `psetq', + ;; because otherwise change to `image' screws up the rest. + (psetq image (or (and (consp image) + (car image)) + image) + down (or (and (consp image) + (nth 1 image)) + down) + inactive (or (and (consp image) + (nth 2 image)) + inactive)) (let ((glyph (widget-glyph-find image tag))) - (if glyph + (if glyph (widget-glyph-insert-glyph widget glyph (widget-glyph-find down tag) (widget-glyph-find inactive tag)) - (insert tag)))) + (insert tag)) + glyph)) (defun widget-glyph-insert-glyph (widget glyph &optional down inactive) "In WIDGET, insert GLYPH. @@ -653,12 +726,19 @@ glyphs used when the widget is pushed and inactive, respectively." (insert "*") (let ((extent (make-extent (point) (1- (point)))) - (help-echo (and widget (widget-get widget :help-echo)))) - (set-extent-property extent 'widget widget) + (help-echo (and widget (widget-get widget :help-echo))) + (map (and widget (widget-get widget :button-keymap)))) + (set-extent-property extent 'glyph-widget widget) + ;; It would be fun if we could make this extent atomic, so it + ;; doesn't mess with cursor motion. But atomic-extents library is + ;; currently a mess, so I'd rather not use it. (set-extent-property extent 'invisible t) (set-extent-property extent 'start-open t) (set-extent-property extent 'end-open t) + (set-extent-property extent 'keymap map) (set-extent-end-glyph extent glyph) + (unless (or (stringp help-echo) (null help-echo)) + (setq help-echo 'widget-mouse-help)) (when help-echo (set-extent-property extent 'balloon-help help-echo) (set-extent-property extent 'help-echo help-echo))) @@ -689,7 +769,7 @@ ;;;###autoload (defun widget-create (type &rest args) - "Create widget of TYPE. + "Create widget of TYPE. The optional ARGS are additional keyword arguments." (let ((widget (apply 'widget-convert type args))) (widget-apply widget :create) @@ -736,10 +816,10 @@ (widget-apply widget :delete)) (defun widget-convert (type &rest args) - "Convert TYPE to a widget without inserting it in the buffer. + "Convert TYPE to a widget without inserting it in the buffer. The optional ARGS are additional keyword arguments." ;; Don't touch the type. - (let* ((widget (if (symbolp type) + (let* ((widget (if (symbolp type) (list type) (copy-sequence type))) (current widget) @@ -765,10 +845,10 @@ (setq widget (funcall convert-widget widget)))) (setq type (get (car type) 'widget-type))) ;; Finally set the keyword args. - (while keys + (while keys (let ((next (nth 0 keys))) (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:)) - (progn + (progn (widget-put widget next (nth 1 keys)) (setq keys (nthcdr 2 keys))) (setq keys nil)))) @@ -846,16 +926,12 @@ "Keymap containing useful binding for buffers containing widgets. Recommended as a parent keymap for modes using widgets.") -(unless widget-keymap +(unless widget-keymap (setq widget-keymap (make-sparse-keymap)) (define-key widget-keymap [tab] 'widget-forward) (define-key widget-keymap [(shift tab)] 'widget-backward) (define-key widget-keymap [(meta tab)] 'widget-backward) - (define-key widget-keymap [backtab] 'widget-backward) - ;;Glyph support. - (define-key widget-keymap [button1] 'widget-button1-click) - (define-key widget-keymap [button2] 'widget-button-click) - (define-key widget-keymap "\C-m" 'widget-button-press)) + (define-key widget-keymap [backtab] 'widget-backward)) (defvar widget-global-map global-map "Keymap used for events the widget does not handle themselves.") @@ -864,7 +940,7 @@ (defvar widget-field-keymap nil "Keymap used inside an editable field.") -(unless widget-field-keymap +(unless widget-field-keymap (setq widget-field-keymap (make-sparse-keymap)) (set-keymap-parents widget-field-keymap global-map) (define-key widget-field-keymap "\C-k" 'widget-kill-line) @@ -879,24 +955,38 @@ (defvar widget-text-keymap nil "Keymap used inside a text field.") -(unless widget-text-keymap +(unless widget-text-keymap (setq widget-text-keymap (make-sparse-keymap)) (set-keymap-parents widget-field-keymap global-map) (define-key widget-text-keymap "\C-a" 'widget-beginning-of-line) (define-key widget-text-keymap "\C-e" 'widget-end-of-line) (define-key widget-text-keymap "\C-t" 'widget-transpose-chars)) +(defvar widget-button-keymap nil + "Keymap used inside a button.") + +(unless widget-button-keymap + (setq widget-button-keymap (make-sparse-keymap)) + (set-keymap-parents widget-button-keymap widget-keymap) + (define-key widget-button-keymap "\C-m" 'widget-button-press) + (define-key widget-button-keymap [button2] 'widget-button-click) + ;; Ideally, button3 within a button should invoke a button-specific + ;; menu. + (define-key widget-button-keymap [button3] 'widget-button-click) + ;;Glyph support. + (define-key widget-button-keymap [button1] 'widget-button1-click)) + (defun widget-field-activate (pos &optional event) "Invoke the ediable field at point." (interactive "@d") - (let ((field (get-char-property pos 'field))) + (let ((field (widget-field-find pos))) (if field (widget-apply-action field event) (call-interactively (lookup-key widget-global-map (this-command-keys)))))) -(defface widget-button-pressed-face +(defface widget-button-pressed-face '((((class color)) (:foreground "red")) (t @@ -904,6 +994,11 @@ "Face used for pressed buttons." :group 'widget-faces) +(defun widget-event-point (event) + "Character position of the mouse event, or nil." + (and (mouse-event-p event) + (event-point event))) + (defun widget-button-click (event) "Invoke button below mouse pointer." (interactive "@e") @@ -915,56 +1010,46 @@ (if button (let* ((extent (widget-get button :button-extent)) (face (extent-property extent 'face)) - (mouse-face (extent-property extent 'mouse-face))) + (mouse-face (extent-property extent 'mouse-face)) + (help-echo (extent-property extent 'help-echo))) (unwind-protect (progn - (set-extent-property extent 'face - 'widget-button-pressed-face) - (set-extent-property extent 'mouse-face - 'widget-button-pressed-face) + ;; Merge relevant faces, and make the result mouse-face. + (let ((merge `(widget-button-pressed-face ,mouse-face))) + (nconc merge (if (listp face) + face (list face))) + (setq merge (delete-if-not 'find-face merge)) + (set-extent-property extent 'mouse-face merge)) (unless (widget-apply button :mouse-down-action event) - (while (not (button-release-event-p event)) - (setq event (next-event) - pos (widget-event-point event)) - (if (and pos - (eq (get-char-property pos 'button) - button)) - (progn - (set-extent-property extent 'face - 'widget-button-pressed-face) - (set-extent-property extent 'mouse-face - 'widget-button-pressed-face)) - (set-extent-property extent 'face face) - (set-extent-property extent - 'mouse-face mouse-face)))) - (when (and pos - (eq (get-char-property pos 'button) button)) + ;; Wait for button release. + (while (not (button-release-event-p + (setq event (next-event)))) + (dispatch-event event))) + ;; Disallow mouse-face and help-echo. + (set-extent-property extent 'mouse-face nil) + (set-extent-property extent 'help-echo nil) + (setq pos (widget-event-point event)) + (unless (eq (current-buffer) (extent-object extent)) + ;; Barf if dispatch-event tripped us by + ;; changing buffer. + (error "Buffer changed during mouse motion")) + ;; Do the associated action. + (when (and pos (extent-in-region-p extent pos pos)) (widget-apply-action button event))) - (set-extent-property extent 'face face) - (set-extent-property extent 'mouse-face mouse-face))) - (let ((up t) - command) - ;; Find the global command to run, and check whether it - ;; is bound to an up event. - (cond ((setq command ;down event - (lookup-key widget-global-map [button2])) - (setq up nil)) - ((setq command ;up event - (lookup-key widget-global-map [button2up])))) - (when up - ;; Don't execute up events twice. - (while (not (button-release-event-p event)) - (setq event (next-event)))) - (when command - (call-interactively command)))))) + ;; Unwinding: fully release the button. + (set-extent-property extent 'mouse-face mouse-face) + (set-extent-property extent 'help-echo help-echo))) + ;; This should not happen! + (error "`widget-button-click' called outside button")))) (t - (message "You clicked somewhere weird.")))) + (message "You clicked somewhere weird")))) (defun widget-button1-click (event) "Invoke glyph below mouse pointer." (interactive "@e") (if (event-glyph event) (widget-glyph-click event) + ;; Should somehow avoid this. (let ((command (lookup-key widget-global-map (this-command-keys)))) (and (commandp command) (call-interactively command))))) @@ -973,28 +1058,55 @@ "Handle click on a glyph." (let* ((glyph (event-glyph event)) (extent (event-glyph-extent event)) - (widget (extent-property extent 'widget)) + (widget (extent-property extent 'glyph-widget)) (down-glyph (or (and widget (widget-get widget :glyph-down)) glyph)) (up-glyph (or (and widget (widget-get widget :glyph-up)) glyph)) (last event)) - ;; Wait for the release. - (while (not (button-release-event-p last)) - (if (eq extent (event-glyph-extent last)) - (set-extent-property extent 'end-glyph down-glyph) - (set-extent-property extent 'end-glyph up-glyph)) - (setq last (next-event event))) - ;; Release glyph. - (when down-glyph - (set-extent-property extent 'end-glyph up-glyph)) - ;; Apply widget action. - (when (eq extent (event-glyph-extent last)) - (let ((widget (extent-property (event-glyph-extent event) 'widget))) - (cond ((null widget) - (message "You clicked on a glyph.")) - ((not (widget-apply widget :active)) - (message "This glyph is inactive.")) - (t - (widget-apply-action widget event))))))) + (unless (widget-apply widget :active) + (error "This widget is inactive")) + (let ((current-glyph 'down)) + ;; We always know what glyph is drawn currently, to avoid + ;; unnecessary extent changes. Is this any noticable gain? + (unwind-protect + (progn + ;; Press the glyph. + (set-extent-end-glyph extent down-glyph) + ;; Redisplay (shouldn't be needed, but...) + (sit-for 0) + (unless (widget-apply widget :mouse-down-action event) + ;; Wait for the release. + (while (not (button-release-event-p last)) + (unless (button-press-event-p last) + (dispatch-event last)) + (when (motion-event-p last) + ;; Update glyphs on mouse motion. + (if (eq extent (event-glyph-extent last)) + (unless (eq current-glyph 'down) + (set-extent-end-glyph extent down-glyph) + (setq current-glyph 'down)) + (unless (eq current-glyph 'up) + (set-extent-end-glyph extent up-glyph) + (setq current-glyph 'up)))) + (setq last (next-event event)))) + (unless (eq (current-buffer) (extent-object extent)) + ;; Barf if dispatch-event tripped us by changing buffer. + (error "Buffer changed during mouse motion")) + ;; Apply widget action. + (when (eq extent (event-glyph-extent last)) + (let ((widget (extent-property (event-glyph-extent event) + 'glyph-widget))) + (cond ((null widget) + (message "You clicked on a glyph")) + ((not (widget-apply widget :active)) + (error "This glyph is inactive")) + (t + (widget-apply-action widget event)))))) + ;; Release the glyph. + (and (eq current-glyph 'down) + ;; The extent might have been detached or deleted + (extent-live-p extent) + (not (extent-detached-p extent)) + (set-extent-end-glyph extent up-glyph)))))) (defun widget-button-press (pos &optional event) "Invoke button at POS." @@ -1015,7 +1127,8 @@ (if widget (let ((order (widget-get widget :tab-order))) (if order - (if last-tab (and (= order (if backwardp (1- last-tab) + (if last-tab (and (= order (if backwardp + (1- last-tab) (1+ last-tab))) widget) (and (> order 0) widget)) @@ -1039,10 +1152,11 @@ (lambda (ext ignore) ext) nil (if at-point (extent-end-position at-point) pos) - nil nil 'start-open 'tabable))) + nil nil 'start-open 'button-or-field))) (and extent (extent-start-position extent)))) +;; This is too slow in buffers with many buttons (W3). (defun widget-previous-button-or-field (pos) "Find the previous button, or field, and return its start position, or nil. Internal function, don't use it outside `wid-edit'." @@ -1051,10 +1165,13 @@ (map-extents (lambda (ext ignore) (if (eq ext at-point) - previous-extent + ;; We reached the extent we were on originally + (if (= pos (extent-start-position at-point)) + previous-extent + (setq previous-extent at-point)) (setq previous-extent ext) nil)) - nil nil pos nil 'start-open 'tabable) + nil nil pos nil 'start-open 'button-or-field) (and previous-extent (extent-start-position previous-extent)))) @@ -1070,7 +1187,8 @@ (if nextpos (progn (goto-char nextpos) - (when (widget-tabable-at nil last-tab t) + (when (and (not (get-char-property nextpos 'widget-inactive)) + (widget-tabable-at nil last-tab t)) (incf arg) (setq found t last-tab (widget-get (widget-at (point)) @@ -1086,7 +1204,8 @@ (if nextpos (progn (goto-char nextpos) - (when (widget-tabable-at nil last-tab) + (when (and (not (get-char-property nextpos 'widget-inactive)) + (widget-tabable-at nil last-tab)) (decf arg) (setq found t last-tab (widget-get (widget-at (point)) @@ -1154,14 +1273,19 @@ (goto-char end) (skip-chars-backward " \t\n" start) (point))))) - (if (and last-non-space - (= last-non-space (1+ start))) - ;; 1-character field - nil - (when (and (null arg) - (= last-non-space (point))) - (forward-char -1)) - (transpose-chars arg)))) + (cond ((and last-non-space + (or (= last-non-space start) + (= last-non-space (1+ start)))) + ;; empty or one-character field + nil) + ((= (point) start) + ;; at the beginning of the field -- we would get an error here. + (error "Cannot transpose at beginning of field")) + (t + (when (and (null arg) + (= last-non-space (point))) + (forward-char -1)) + (transpose-chars arg))))) (defcustom widget-complete-field (lookup-key global-map "\M-\t") "Default function to call for completion inside fields." @@ -1199,11 +1323,17 @@ (setq field (car widget-field-new) widget-field-new (cdr widget-field-new) widget-field-list (cons field widget-field-list)) - (let ((extent (widget-get field :field-extent))) + (let ((from (car (widget-get field :field-extent))) + (to (cdr (widget-get field :field-extent)))) (widget-specify-field field - (extent-start-position extent) - (extent-end-position extent)) - (delete-extent extent)))) + (marker-position from) (marker-position to)) + (set-marker from nil) + (set-marker to nil)) + ;; If the field is placed within the inactive zone, deactivate it. + (let ((extent (widget-get field :field-extent))) + (when (get-char-property (extent-start-position extent) + 'widget-inactive) + (widget-activation-widget-mapper extent :deactivate))))) (widget-clear-undo) (widget-add-change)) @@ -1237,32 +1367,45 @@ (defun widget-field-find (pos) "Return the field at POS. Unlike (get-char-property POS 'field) this, works with empty fields too." - (let ((fields widget-field-list) - field found) - (while fields - (setq field (car fields) - fields (cdr fields)) - (let ((start (widget-field-start field)) - (end (widget-field-end field))) - (when (and (<= start pos) (<= pos end)) - (when found - (debug "Overlapping fields")) - (setq found field)))) - found)) + (let ((field-extent (map-extents (lambda (extent ignore) + extent) + nil pos pos nil nil 'field))) + (and field-extent + (extent-property field-extent 'field)))) + +;; Old version, without `map-extents'. +;(defun widget-field-find (pos) +; (let ((fields widget-field-list) +; field found) +; (while fields +; (setq field (car fields) +; fields (cdr fields)) +; (let ((start (widget-field-start field)) +; (end (widget-field-end field))) +; (when (and (<= start pos) (<= pos end)) +; (when found +; (debug "Overlapping fields")) +; (setq found field)))) +; found)) (defun widget-before-change (from to) - ;; This is how, for example, a variable changes its state to `modified'. - ;; when it is being edited. + ;; Barf if the text changed is outside the editable fields. (unless inhibit-read-only (let ((from-field (widget-field-find from)) (to-field (widget-field-find to))) - (cond ((not (eq from-field to-field)) + (cond ((or (null from-field) + (null to-field)) + ;; Either end of change is not within a field. + (add-hook 'post-command-hook 'widget-add-change nil t) + (error "Attempt to change text outside editable field")) + ((not (eq from-field to-field)) + ;; The change begins in one fields, and ends in another one. (add-hook 'post-command-hook 'widget-add-change nil t) (error "Change should be restricted to a single field")) - ((null from-field) - (add-hook 'post-command-hook 'widget-add-change nil t) - (error "Attempt to change text outside editable field")) (widget-field-use-before-change + ;; #### Bletch! This loses because XEmacs get confused + ;; if before-change-functions change the contents of + ;; buffer before from/to. (condition-case nil (widget-apply from-field :notify from-field) (error (debug "Before Change")))))))) @@ -1277,6 +1420,9 @@ (defun widget-after-change (from to old) ;; Adjust field size and text properties. + + ;; Also, notify the widgets (so, for example, a variable changes its + ;; state to `modified'. when it is being edited.) (condition-case nil (let ((field (widget-field-find from)) (other (widget-field-find to))) @@ -1285,7 +1431,7 @@ (debug "Change in different fields")) (let ((size (widget-get field :size)) (secret (widget-get field :secret))) - (when size + (when size (let ((begin (widget-field-start field)) (end (widget-field-end field))) (cond ((< (- end begin) size) @@ -1309,7 +1455,7 @@ (when secret (let ((begin (widget-field-start field)) (end (widget-field-end field))) - (when size + (when size (while (and (> end begin) (eq (char-after (1- end)) ?\ )) (setq end (1- end)))) @@ -1325,7 +1471,7 @@ ;;; Widget Functions ;; -;; These functions are used in the definition of multiple widgets. +;; These functions are used in the definition of multiple widgets. (defun widget-parent-action (widget &optional event) "Tell :parent of WIDGET to handle the :action. @@ -1357,11 +1503,11 @@ (defun widget-value-convert-widget (widget) "Initialize :value from :args in WIDGET." (let ((args (widget-get widget :args))) - (when args + (when args (widget-put widget :value (car args)) ;; Don't convert :value here, as this is done in `widget-convert'. ;; (widget-put widget :value (widget-apply widget - ;; :value-to-internal (car args))) + ;; :value-to-internal (car args))) (widget-put widget :args nil))) widget) @@ -1377,13 +1523,14 @@ :value-to-external (lambda (widget value) value) :button-prefix 'widget-button-prefix :button-suffix 'widget-button-suffix - :complete 'widget-default-complete + :complete 'widget-default-complete :create 'widget-default-create :indent nil :offset 0 :format-handler 'widget-default-format-handler - :button-face-get 'widget-default-button-face-get - :sample-face-get 'widget-default-sample-face-get + :button-face-get 'widget-default-button-face-get + :sample-face-get 'widget-default-sample-face-get + :button-keymap widget-button-keymap :delete 'widget-default-delete :value-set 'widget-default-value-set :value-inline 'widget-default-value-inline @@ -1407,24 +1554,25 @@ "Create WIDGET at point in the current buffer." (widget-specify-insert (let ((from (point)) - button-begin button-end + button-begin button-end button-glyph sample-begin sample-end doc-begin doc-end value-pos) (insert (widget-get widget :format)) (goto-char from) - ;; Parse escapes in format. + ;; Parse escapes in format. Coding this in C would speed up + ;; things *a lot*. (while (re-search-forward "%\\(.\\)" nil t) (let ((escape (aref (match-string 1) 0))) (replace-match "" t t) (cond ((eq escape ?%) (insert "%")) ((eq escape ?\[) - (setq button-begin (point)) - (insert (widget-get-indirect widget :button-prefix))) + (setq button-begin (point-marker)) + (set-marker-insertion-type button-begin nil)) ((eq escape ?\]) - (insert (widget-get-indirect widget :button-suffix)) - (setq button-end (point))) + (setq button-end (point-marker)) + (set-marker-insertion-type button-end nil)) ((eq escape ?\{) (setq sample-begin (point))) ((eq escape ?\}) @@ -1434,10 +1582,12 @@ (insert "\n") (insert-char ?\ (widget-get widget :indent)))) ((eq escape ?t) - (let ((glyph (widget-get widget :tag-glyph)) - (tag (widget-get widget :tag))) - (cond (glyph - (widget-glyph-insert widget (or tag "image") glyph)) + (let* ((tag (widget-get widget :tag)) + (glyph (widget-get widget :tag-glyph))) + (cond (glyph + (setq button-glyph + (widget-glyph-insert + widget (or tag "Image") glyph))) (tag (insert tag)) (t @@ -1455,12 +1605,21 @@ ((eq escape ?v) (if (and button-begin (not button-end)) (widget-apply widget :value-create) - (setq value-pos (point)))) + (setq value-pos (point-marker)))) (t (widget-apply widget :format-handler escape))))) ;; Specify button, sample, and doc, and insert value. - (and button-begin button-end - (widget-specify-button widget button-begin button-end)) + (when (and button-begin button-end) + (unless button-glyph + (goto-char button-begin) + (insert (widget-get-indirect widget :button-prefix)) + (goto-char button-end) + (set-marker-insertion-type button-end t) + (insert (widget-get-indirect widget :button-suffix))) + (widget-specify-button widget button-begin button-end) + ;; Is this necessary? + (set-marker button-begin nil) + (set-marker button-end nil)) (and sample-begin sample-end (widget-specify-sample widget sample-begin sample-end)) (and doc-begin doc-end @@ -1468,8 +1627,8 @@ (when value-pos (goto-char value-pos) (widget-apply widget :value-create))) - (let ((from (copy-marker (point-min))) - (to (copy-marker (point-max)))) + (let ((from (point-min-marker)) + (to (point-max-marker))) (set-marker-insertion-type from t) (set-marker-insertion-type to nil) (widget-put widget :from from) @@ -1483,7 +1642,7 @@ (let* ((doc-property (widget-get widget :documentation-property)) (doc-try (cond ((widget-get widget :doc)) ((symbolp doc-property) - (documentation-property + (documentation-property (widget-get widget :value) doc-property)) (t @@ -1496,7 +1655,7 @@ (when doc-text (and (eq (preceding-char) ?\n) (widget-get widget :indent) - (insert-char ? (widget-get widget :indent))) + (insert-char ?\ (widget-get widget :indent))) ;; The `*' in the beginning is redundant. (when (eq (aref doc-text 0) ?*) (setq doc-text (substring doc-text 1))) @@ -1505,14 +1664,14 @@ (setq doc-text (substring doc-text 0 (match-beginning 0)))) (push (widget-create-child-and-convert widget 'documentation-string - :indent (cond ((numberp doc-indent ) + :indent (cond ((numberp doc-indent) doc-indent) ((null doc-indent) nil) (t 0)) doc-text) buttons)))) - (t + (t (error "Unknown escape `%c'" escape))) (widget-put widget :buttons buttons))) @@ -1565,17 +1724,17 @@ (- old-pos to 1) (- old-pos from))))) ;;??? Bug: this ought to insert the new value before deleting the old one, - ;; so that markers on either side of the value automatically + ;; so that markers on either side of the value automatically ;; stay on the same side. -- rms. (save-excursion (goto-char (widget-get widget :from)) (widget-apply widget :delete) (widget-put widget :value value) (widget-apply widget :create)) - (if offset - (if (< offset 0) - (goto-char (+ (widget-get widget :to) offset 1)) - (goto-char (min (+ from offset) (1- (widget-get widget :to)))))))) + (when offset + (if (< offset 0) + (goto-char (+ (widget-get widget :to) offset 1)) + (goto-char (min (+ from offset) (1- (widget-get widget :to)))))))) (defun widget-default-value-inline (widget) ;; Wrap value in a list unless it is inline. @@ -1593,7 +1752,7 @@ "Return t iff this widget active (user modifiable)." (and (not (widget-get widget :inactive)) (let ((parent (widget-get widget :parent))) - (or (null parent) + (or (null parent) (widget-apply parent :active))))) (defun widget-default-deactivate (widget) @@ -1710,15 +1869,15 @@ ((and widget-push-button-gui (console-on-window-system-p)) (unless gui-glyphs - (let ((gui (make-gui-button tag 'widget-gui-action widget))) + (let* ((gui-button-shadow-thickness 1) + (gui (make-gui-button tag 'widget-gui-action widget))) (setq gui-glyphs (list (make-glyph `(,(nth 0 (aref gui 1)) [string :data ,text])) (make-glyph `(,(nth 1 (aref gui 1)) [string :data ,text])) (make-glyph `(,(nth 2 (aref gui 1)) [string :data ,text])))) - (setq widget-push-button-cache - (lax-plist-put widget-push-button-cache tag gui-glyphs)))) + (laxputf widget-push-button-cache tag gui-glyphs))) (widget-glyph-insert-glyph widget (nth 0 gui-glyphs) (nth 1 gui-glyphs) (nth 2 gui-glyphs))) (t @@ -1744,7 +1903,7 @@ "An embedded link." :button-prefix 'widget-link-prefix :button-suffix 'widget-link-suffix - :help-echo "Follow the link." + :help-echo "Follow the link" :format "%[%t%]") ;;; The `info-link' Widget. @@ -1769,7 +1928,7 @@ :action 'widget-url-link-action) (defun widget-url-link-help-echo (widget) - (concat "Go to <URL:" (widget-value widget) ">")) + (concat "Visit <URL:" (widget-value widget) ">")) (defun widget-url-link-action (widget &optional event) "Open the url specified by WIDGET." @@ -1874,15 +2033,27 @@ (defun widget-field-action (widget &optional event) ;; Edit the value in the minibuffer. - (let ((invalid (widget-apply widget :validate))) - (let ((prompt (concat (widget-apply widget :menu-tag-get) ": ")) - (value (unless invalid - (widget-value widget)))) - (let ((answer (widget-apply widget :prompt-value prompt value invalid))) - (widget-value-set widget answer))) - (widget-apply widget :notify widget event) - (widget-setup)) - (run-hook-with-args 'widget-edit-functions widget)) + (let* ((invalid (widget-apply widget :validate)) + (prompt (concat (widget-apply widget :menu-tag-get) ": ")) + (value (unless invalid + (widget-value widget))) + (answer (widget-apply widget :prompt-value prompt value invalid))) + (unless (equal value answer) + ;; This is a hack. We can't properly validate the widget + ;; because validation requires the new value to be in the field. + ;; However, widget-field-value-create will not function unless + ;; the new value matches. So, we check whether the thing + ;; matches, and if it does, use either the real or a dummy error + ;; message. + (unless (widget-apply widget :match answer) + (let ((error-message (or (widget-get widget :type-error) + "Invalid field contents"))) + (widget-put widget :error error-message) + (error error-message))) + (widget-value-set widget answer) + (widget-apply widget :notify widget event) + (widget-setup)) + (run-hook-with-args 'widget-edit-functions widget))) ;(defun widget-field-action (widget &optional event) ; ;; Move to next field. @@ -1903,23 +2074,24 @@ (let ((size (widget-get widget :size)) (value (widget-get widget :value)) (from (point)) - ;; This used to make `field-overlay' a cons of two markers, - ;; and revert them to a real overlay in `widget-setup', - ;; because you can't change overlay insertion type. However, - ;; we can do that with extents. - extent) + ;; This is changed to a real extent in `widget-setup'. We + ;; need the end points to behave differently until + ;; `widget-setup' is called. Should probably be replaced with + ;; a genuine extent, but some things break, then. + (extent (cons (make-marker) (make-marker)))) + (widget-put widget :field-extent extent) (insert value) (and size (< (length value) size) (insert-char ?\ (- size (length value)))) (unless (memq widget widget-field-list) (push widget widget-field-new)) - (setq extent (make-extent from (point))) - (set-extent-property extent 'end-open t) - (widget-put widget :field-extent extent) + (move-marker (cdr extent) (point)) + (set-marker-insertion-type (cdr extent) nil) (when (null size) (insert ?\n)) - (set-extent-property extent 'start-open t))) + (move-marker (car extent) from) + (set-marker-insertion-type (car extent) t))) (defun widget-field-value-delete (widget) ;; Remove the widget from the list of active editing fields. @@ -1937,24 +2109,25 @@ (size (widget-get widget :size)) (secret (widget-get widget :secret)) (old (current-buffer))) - (if (and from to) - (progn - (set-buffer buffer) - (while (and size - (not (zerop size)) - (> to from) - (eq (char-after (1- to)) ?\ )) - (setq to (1- to))) - (let ((result (buffer-substring-no-properties from to))) - (when secret - (let ((index 0)) - (while (< (+ from index) to) - (aset result index - (get-char-property (+ from index) 'secret)) - (incf index)))) - (set-buffer old) - result)) - (widget-get widget :value)))) + (cond + ((and from to) + (set-buffer buffer) + (while (and size + (not (zerop size)) + (> to from) + (eq (char-after (1- to)) ?\ )) + (setq to (1- to))) + (let ((result (buffer-substring-no-properties from to))) + (when secret + (let ((index 0)) + (while (< (+ from index) to) + (aset result index + (get-char-property (+ from index) 'secret)) + (incf index)))) + (set-buffer old) + result)) + (t + (widget-get widget :value))))) (defun widget-field-match (widget value) ;; Match any string. @@ -2078,7 +2251,7 @@ choices))) (widget-choose tag (reverse choices) event)))) (when current - (widget-value-set widget + (widget-value-set widget (widget-apply current :value-to-external (widget-get current :value))) (widget-setup) @@ -2128,12 +2301,12 @@ (defun widget-toggle-value-create (widget) ;; Insert text representing the `on' and `off' states. (if (widget-value widget) - (widget-glyph-insert widget - (widget-get widget :on) + (widget-glyph-insert widget + (widget-get widget :on) (widget-get widget :on-glyph)) - (widget-glyph-insert widget - (widget-get widget :off) - (widget-get widget :off-glyph)))) + (widget-glyph-insert widget + (widget-get widget :off) + (widget-get widget :off-glyph)))) (defun widget-toggle-action (widget &optional event) ;; Toggle value. @@ -2184,7 +2357,7 @@ ;; Insert all values (let ((alist (widget-checklist-match-find widget (widget-get widget :value))) (args (widget-get widget :args))) - (while args + (while args (widget-checklist-add-item widget (car args) (assq (car args) alist)) (setq args (cdr args))) (widget-put widget :children (nreverse (widget-get widget :children))))) @@ -2194,8 +2367,8 @@ ;; If the item is checked, CHOSEN is a cons whose cdr is the value. (and (eq (preceding-char) ?\n) (widget-get widget :indent) - (insert-char ? (widget-get widget :indent))) - (widget-specify-insert + (insert-char ?\ (widget-get widget :indent))) + (widget-specify-insert (let* ((children (widget-get widget :children)) (buttons (widget-get widget :buttons)) (button-args (or (widget-get type :sibling-args) @@ -2227,7 +2400,7 @@ (t (widget-create-child-value widget type (car (cdr chosen))))))) - (t + (t (error "Unknown escape `%c'" escape))))) ;; Update properties. (and button child (widget-put child :button button)) @@ -2267,14 +2440,14 @@ found) (while vals (let ((answer (widget-checklist-match-up args vals))) - (cond (answer + (cond (answer (let ((match (widget-match-inline answer vals))) (setq found (cons (cons answer (car match)) found) vals (cdr match) args (delq answer args)))) (greedy (setq vals (cdr vals))) - (t + (t (setq vals nil))))) found)) @@ -2293,7 +2466,7 @@ ;; The values of all selected items. (let ((children (widget-get widget :children)) child result) - (while children + (while children (setq child (car children) children (cdr children)) (if (widget-value (widget-get child :button)) @@ -2334,7 +2507,7 @@ :button-suffix "" :button-prefix "" :on "(*)" - :on-glyph "radio1" + :on-glyph '("radio1" nil "radio0") :off "( )" :off-glyph "radio0") @@ -2366,7 +2539,7 @@ ;; Insert all values (let ((args (widget-get widget :args)) arg) - (while args + (while args (setq arg (car args) args (cdr args)) (widget-radio-add-item widget arg)))) @@ -2376,8 +2549,8 @@ ;; (setq type (widget-convert type)) (and (eq (preceding-char) ?\n) (widget-get widget :indent) - (insert-char ? (widget-get widget :indent))) - (widget-specify-insert + (insert-char ?\ (widget-get widget :indent))) + (widget-specify-insert (let* ((value (widget-get widget :value)) (children (widget-get widget :children)) (buttons (widget-get widget :buttons)) @@ -2397,7 +2570,7 @@ (insert "%")) ((eq escape ?b) (setq button (apply 'widget-create-child-and-convert - widget 'radio-button + widget 'radio-button :value (not (null chosen)) button-args))) ((eq escape ?v) @@ -2405,14 +2578,14 @@ (widget-create-child-value widget type value) (widget-create-child widget type))) - (unless chosen + (unless chosen (widget-apply child :deactivate))) - (t + (t (error "Unknown escape `%c'" escape))))) ;; Update properties. (when chosen (widget-put widget :choice type)) - (when button + (when button (widget-put child :button button) (widget-put widget :buttons (nconc buttons (list button)))) (when child @@ -2465,8 +2638,8 @@ (match (and (not found) (widget-apply current :match value)))) (widget-value-set button match) - (if match - (progn + (if match + (progn (widget-value-set current value) (widget-apply current :activate)) (widget-apply current :deactivate)) @@ -2509,12 +2682,12 @@ (define-widget 'insert-button 'push-button "An insert button for the `editable-list' widget." :tag "INS" - :help-echo "Insert a new item into the list at this position." + :help-echo "Insert a new item into the list at this position" :action 'widget-insert-button-action) (defun widget-insert-button-action (widget &optional event) ;; Ask the parent to insert a new item. - (widget-apply (widget-get widget :parent) + (widget-apply (widget-get widget :parent) :insert-before (widget-get widget :widget))) ;;; The `delete-button' Widget. @@ -2522,12 +2695,12 @@ (define-widget 'delete-button 'push-button "A delete button for the `editable-list' widget." :tag "DEL" - :help-echo "Delete this item from the list." + :help-echo "Delete this item from the list" :action 'widget-delete-button-action) (defun widget-delete-button-action (widget &optional event) ;; Ask the parent to insert a new item. - (widget-apply (widget-get widget :parent) + (widget-apply (widget-get widget :parent) :delete-at (widget-get widget :widget))) ;;; The `editable-list' Widget. @@ -2559,11 +2732,11 @@ (let ((widget-push-button-gui widget-editable-list-gui)) (cond ((eq escape ?i) (and (widget-get widget :indent) - (insert-char ? (widget-get widget :indent))) - (apply 'widget-create-child-and-convert + (insert-char ?\ (widget-get widget :indent))) + (apply 'widget-create-child-and-convert widget 'insert-button (widget-get widget :append-button-args))) - (t + (t (widget-default-format-handler widget escape))))) (defun widget-editable-list-value-create (widget) @@ -2617,11 +2790,11 @@ (inhibit-read-only t) before-change-functions after-change-functions) - (cond (before + (cond (before (goto-char (widget-get before :entry-from))) (t (goto-char (widget-get widget :value-pos)))) - (let ((child (widget-editable-list-entry-create + (let ((child (widget-editable-list-entry-create widget nil nil))) (when (< (widget-get child :entry-from) (widget-get widget :from)) (set-marker (widget-get widget :from) @@ -2667,10 +2840,10 @@ (let ((type (nth 0 (widget-get widget :args))) (widget-push-button-gui widget-editable-list-gui) child delete insert) - (widget-specify-insert + (widget-specify-insert (save-excursion (and (widget-get widget :indent) - (insert-char ? (widget-get widget :indent))) + (insert-char ?\ (widget-get widget :indent))) (insert (widget-get widget :entry-format))) ;; Parse % escapes in format. (while (re-search-forward "%\\(.\\)" nil t) @@ -2688,13 +2861,13 @@ (widget-get widget :delete-button-args)))) ((eq escape ?v) (if conv - (setq child (widget-create-child-value + (setq child (widget-create-child-value widget type value)) (setq child (widget-create-child widget type)))) - (t + (t (error "Unknown escape `%c'" escape))))) - (widget-put widget - :buttons (cons delete + (widget-put widget + :buttons (cons delete (cons insert (widget-get widget :buttons)))) (let ((entry-from (copy-marker (point-min))) @@ -2732,7 +2905,7 @@ value (cdr answer)) (and (eq (preceding-char) ?\n) (widget-get widget :indent) - (insert-char ? (widget-get widget :indent))) + (insert-char ?\ (widget-get widget :indent))) (push (cond ((null answer) (widget-create-child widget arg)) ((widget-get arg :inline) @@ -2793,8 +2966,8 @@ widget-push-button-suffix)) (setq off "")) (if (widget-value widget) - (widget-glyph-insert widget on "down" "down-pushed") - (widget-glyph-insert widget off "right" "right-pushed")))) + (widget-glyph-insert widget on '("down" "down-pushed")) + (widget-glyph-insert widget off '("right" "right-pushed"))))) ;;; The `documentation-link' Widget. ;; @@ -2865,7 +3038,7 @@ (widget-put widget :buttons buttons))) (let ((indent (widget-get widget :indent))) (when (and indent (not (zerop indent))) - (save-excursion + (save-excursion (save-restriction (narrow-to-region from to) (goto-char (point-min)) @@ -2896,24 +3069,17 @@ (push (widget-create-child-and-convert widget 'visibility :help-echo (lambda (widget) - ;; This can get called directly from - ;; default-mouse-motion-handler, with an - ;; extent argument. - (and (extentp widget) - (setq - widget (widget-at - (extent-start-position widget)))) (concat (if (widget-value widget) "Hide" "Show") - " the rest of the documentation.")) + " the rest of the documentation")) :off "More" :action 'widget-parent-action shown) buttons) (when shown (setq start (point)) - (when (and indent (not (zerop indent))) + (when indent (insert-char ?\ indent)) (insert after) (widget-documentation-link-add widget start (point))) @@ -2925,7 +3091,7 @@ (defun widget-documentation-string-action (widget &rest ignore) ;; Toggle documentation. (let ((parent (widget-get widget :parent))) - (widget-put parent :documentation-shown + (widget-put parent :documentation-shown (not (widget-get parent :documentation-shown)))) ;; Redraw. (widget-value-set widget (widget-value widget))) @@ -2982,15 +3148,15 @@ (defun widget-regexp-validate (widget) "Check that the value of WIDGET is a valid regexp." - (let ((val (widget-value widget))) + (let ((value (widget-value widget))) (condition-case data (prog1 nil - (string-match val "")) + (string-match value "")) (error (widget-put widget :error (error-message-string data)) widget)))) (define-widget 'file 'string - "A file widget. + "A file widget. It will read a file name from the minibuffer when invoked." :complete-function 'widget-file-complete :prompt-value 'widget-file-prompt-value @@ -3050,7 +3216,7 @@ ;;; (widget-apply widget :notify widget event))) (define-widget 'directory 'file - "A directory widget. + "A directory widget. It will read a directory name from the minibuffer when invoked." :tag "Directory") @@ -3078,7 +3244,7 @@ (defun widget-symbol-prompt-internal (widget prompt initial history) ;; Read file from minibuffer. - (let ((answer (completing-read prompt obarray + (let ((answer (completing-read prompt obarray (widget-get widget :prompt-match) nil initial history))) (if (and (stringp answer) @@ -3251,12 +3417,12 @@ "A character." :tag "Character" :value 0 - :size 1 + :size 1 :format "%{%t%}: %v\n" - :valid-regexp "\\`.\\'" + :valid-regexp "\\`[\0-\377]\\'" :error "This field should contain a single character" :value-to-internal (lambda (widget value) - (if (stringp value) + (if (stringp value) value (char-to-string value))) :value-to-external (lambda (widget value) @@ -3279,7 +3445,7 @@ :value-to-internal (lambda (widget value) (append value nil)) :value-to-external (lambda (widget value) (vconcat value))) -(defun widget-vector-match (widget value) +(defun widget-vector-match (widget value) (and (vectorp value) (widget-group-match widget (widget-apply widget :value-to-internal value)))) @@ -3294,7 +3460,7 @@ :value-to-external (lambda (widget value) (cons (car value) (cadr value)))) -(defun widget-cons-match (widget value) +(defun widget-cons-match (widget value) (and (consp value) (widget-group-match widget (widget-apply widget :value-to-internal value)))) @@ -3308,7 +3474,7 @@ :prompt-value 'widget-choice-prompt-value) (defun widget-choice-prompt-value (widget prompt value unbound) - "Make a choice." + "Make a choice." (let ((args (widget-get widget :args)) (completion-ignore-case (widget-get widget :case-fold)) current choices old) @@ -3381,7 +3547,7 @@ ;;; The `color' Widget. -(define-widget 'color 'editable-field +(define-widget 'color 'editable-field "Choose a color name (with sample)." :format "%[%t%]: %v (%{sample%})\n" :size 10 @@ -3403,7 +3569,7 @@ ((null completion) (error "Can't find completion for \"%s\"" prefix)) ((not (string-equal prefix completion)) - (insert-and-inherit (substring completion (length prefix)))) + (insert (substring completion (length prefix)))) (t (message "Making completion list...") (let ((list (all-completions prefix list nil))) @@ -3412,13 +3578,17 @@ (message "Making completion list...done"))))) (defun widget-color-sample-face-get (widget) - (let* ((value (condition-case nil - (widget-value widget) - (error (widget-get widget :value)))) - (symbol (intern (concat "fg:" value)))) - (prog1 symbol - (or (find-face symbol) - (set-face-foreground (make-face symbol) value))))) + (or (widget-get widget :sample-face) + (let ((color (widget-value widget)) + (face (make-face (gensym "sample-face-") nil t))) + ;; Use the face object, not its name, to prevent lossage if gc + ;; happens before applying the face. + (widget-put widget :sample-face face) + (and color + (not (equal color "")) + (valid-color-name-p color) + (set-face-foreground face color)) + face))) (defvar widget-color-choice-list nil) ;; Variable holding the possible colors. @@ -3441,10 +3611,14 @@ (defun widget-color-notify (widget child &optional event) "Update the sample, and notify the parent." - (set-extent-property (widget-get widget :sample-extent) - 'face (widget-apply widget :sample-face-get)) + (let* ((face (widget-apply widget :sample-face-get)) + (color (widget-value widget))) + (if (valid-color-name-p color) + (set-face-foreground face color) + (remove-face-property face 'foreground))) (widget-default-notify widget child event)) +;; Is this a misnomer? (defun widget-at (pos) "The button or field at POS." (or (get-char-property pos 'button) @@ -3454,11 +3628,10 @@ "Display the help echo for widget at POS." (let* ((widget (widget-at pos)) (help-echo (and widget (widget-get widget :help-echo)))) - (cond ((stringp help-echo) - (message "%s" help-echo)) - ((and (functionp help-echo) - (stringp (setq help-echo (funcall help-echo widget)))) - (message "%s" help-echo))))) + (and (functionp help-echo) + (setq help-echo (funcall help-echo widget))) + (when (stringp help-echo) + (display-message 'no-log help-echo)))) ;;; The End: