diff lisp/custom/wid-edit.el @ 155:43dd3413c7c7 r20-3b4

Import from CVS: tag r20-3b4
author cvs
date Mon, 13 Aug 2007 09:39:39 +0200
parents 25f70ba0133c
children 6b37e6ddd302
line wrap: on
line diff
--- a/lisp/custom/wid-edit.el	Mon Aug 13 09:38:27 2007 +0200
+++ b/lisp/custom/wid-edit.el	Mon Aug 13 09:39:39 2007 +0200
@@ -4,7 +4,7 @@
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Keywords: extensions
-;; Version: 1.98
+;; Version: 1.9907
 ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
 
 ;; This file is part of GNU Emacs.
@@ -31,7 +31,7 @@
 ;;; Code:
 
 (require 'widget)
-(require 'cl)
+(eval-when-compile (require 'cl))
 
 ;;; Compatibility.
 
@@ -45,26 +45,6 @@
       (error (load-library "x-overlay"))))
   
   (if (string-match "XEmacs" emacs-version)
-      ;; XEmacs spell `intangible' as `atomic'.
-      (defun widget-make-intangible (from to side)
-	"Make text between FROM and TO atomic with regard to movement.
-Third argument should be `start-open' if it should be sticky to the rear,
-and `end-open' if it should sticky to the front."
-	(require 'atomic-extents)
-	(let ((ext (make-extent from to)))
-	   ;; XEmacs doesn't understant different kinds of read-only, so
-	   ;; we have to use extents instead.  
-	  (put-text-property from to 'read-only nil)
-	  (set-extent-property ext 'read-only t)
-	  (set-extent-property ext 'start-open nil)
-	  (set-extent-property ext 'end-open nil)
-	  (set-extent-property ext side t)
-	  (set-extent-property ext 'atomic t)))
-    (defun widget-make-intangible (from to size)
-      "Make text between FROM and TO intangible."
-      (put-text-property from to 'intangible 'front)))
-	  
-  (if (string-match "XEmacs" emacs-version)
       (defun widget-event-point (event)
 	"Character position of the end of event if that exists, or nil."
 	(if (mouse-event-p event)
@@ -74,7 +54,11 @@
       "Character position of the end of event if that exists, or nil."
       (posn-point (event-end event))))
 
-;; The following should go away when bundled with Emacs.
+(defalias 'widget-read-event (if (string-match "XEmacs" emacs-version)
+				   'next-event
+				 'read-event))
+
+  ;; The following should go away when bundled with Emacs.
   (condition-case ()
       (require 'custom)
     (error nil))
@@ -109,6 +93,27 @@
 	(display-error obj buf)
 	(buffer-string buf)))))
 
+(when (let ((a "foo"))
+	(put-text-property 1 2 'foo 1 a)
+	(put-text-property 1 2 'bar 2 a)
+	(set-text-properties 1 2 nil a)
+	(text-properties-at 1 a))
+  ;; XEmacs 20.2 and earlier had a buggy set-text-properties.
+  (defun set-text-properties (start end props &optional buffer-or-string)
+    "Completely replace properties of text from START to END.
+The third argument PROPS is the new property list.
+The optional fourth argument, BUFFER-OR-STRING,
+is the string or buffer containing the text."
+    (map-extents #'(lambda (extent ignored)
+		     (remove-text-properties
+		      start end
+		      (list (extent-property extent 'text-prop)
+			    nil)
+		      buffer-or-string)
+		     nil)
+		 buffer-or-string start end nil nil 'text-prop)
+    (add-text-properties start end props buffer-or-string)))
+
 ;;; Customization.
 
 (defgroup widgets nil
@@ -121,16 +126,6 @@
   :group 'faces
   :group 'hypermedia)
 
-(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 'widgets)
-
 (defface widget-button-face '((t (:bold t)))
   "Face used for widget buttons."
   :group 'widgets)
@@ -225,7 +220,7 @@
 			  (car (event-object val))))
 	   (cdr (assoc val items))))
 	(t
-	 (setq items (remove-if 'stringp items))
+	 (setq items (widget-remove-if 'stringp items))
 	 (let ((val (completing-read (concat title ": ") items nil t)))
 	   (if (stringp val)
 	       (let ((try (try-completion val items)))
@@ -234,6 +229,14 @@
 		 (cdr (assoc val items)))
 	     nil)))))
 
+(defun widget-remove-if (predictate list)
+  (let (result (tail list))
+    (while tail
+      (or (funcall predictate (car tail))
+	  (setq result (cons (car tail) result)))
+      (setq tail (cdr tail)))
+    (nreverse result)))
+
 ;;; Widget text specifications.
 ;; 
 ;; These functions are for specifying text properties. 
@@ -246,119 +249,52 @@
   ;; Default properties.
   (add-text-properties from to (list 'read-only t
 				     'front-sticky t
-				     'start-open t
-				     'end-open t
-				     'rear-nonsticky nil)))
+				     'rear-nonsticky nil
+				     'start-open nil
+				     'end-open nil)))
 
 (defun widget-specify-field (widget from to)
-  ;; Specify editable button for WIDGET between FROM and TO.
-  (widget-specify-field-update widget from to)
-
-  ;; Make it possible to edit the front end of the field.
-  (add-text-properties (1- from) from (list 'rear-nonsticky t
-					      'end-open t
-					      'invisible t))
-  (when (or (string-match "\\(.\\|\n\\)%v" (widget-get widget :format))
-	    (widget-get widget :hide-front-space))
-    ;; WARNING: This is going to lose horrible if the character just
-    ;; before the field can be modified (e.g. if it belongs to a
-    ;; choice widget).  We try to compensate by checking the format
-    ;; string, and hope the user hasn't changed the :create method.
-    (widget-make-intangible (- from 2) from 'end-open))
-  
-  ;; Make it possible to edit back end of the field.
-  (add-text-properties to (1+ to) (list 'front-sticky nil
-					'read-only t
-					'start-open t))
-
-  (cond ((widget-get widget :size)
-	 (put-text-property to (1+ to) 'invisible t)
-	 (when (or (string-match "%v\\(.\\|\n\\)" (widget-get widget :format))
-		   (widget-get widget :hide-rear-space))
-	   ;; WARNING: This is going to lose horrible if the character just
-	   ;; after the field can be modified (e.g. if it belongs to a
-	   ;; choice widget).  We try to compensate by checking the format
-	   ;; string, and hope the user hasn't changed the :create method.
-	   (widget-make-intangible to (+ to 2) 'start-open)))
-	((string-match "XEmacs" emacs-version)
-	 ;; XEmacs does not allow you to insert before a read-only
-	 ;; character, even if it is start.open.
-	 ;; XEmacs does allow you to delete an read-only extent, so
-	 ;; making the terminating newline read only doesn't help.
-	 ;; I tried putting an invisible intangible read-only space
-	 ;; before the newline, which gave really weird effects.
-	 ;; So for now, we just have trust the user not to delete the
-	 ;; newline.  
-	 (put-text-property to (1+ to) 'read-only nil))))
-
-(defun widget-specify-field-update (widget from to)
-  ;; Specify editable button for WIDGET between FROM and TO.
+  "Specify editable button for WIDGET between FROM and TO."
+  (put-text-property from to 'read-only nil)
+  ;; Terminating space is not part of the field, but necessary in
+  ;; order for local-map to work.  Remove next sexp if local-map works
+  ;; at the end of the overlay.
+  (save-excursion
+    (goto-char to)
+    (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 (1- from) from 
+		       '(rear-nonsticky t end-open t read-only from))
   (let ((map (widget-get widget :keymap))
-	(secret (widget-get widget :secret))
-	(secret-to to)
-	(size (widget-get widget :size))
-	(face (or (widget-get widget :value-face)
-		  'widget-field-face))
+	(face (or (widget-get widget :value-face) 'widget-field-face))
 	(help-echo (widget-get widget :help-echo))
-	(help-property (if (featurep 'balloon-help)
-			   'balloon-help
-			 'help-echo)))
+	(overlay (make-overlay from to nil nil t)))
     (unless (or (stringp help-echo) (null help-echo))
-      (setq help-echo 'widget-mouse-help))
-
-    (when secret 
-      (while (and size
-		  (not (zerop size))
-		  (> secret-to from)
-		  (eq (char-after (1- secret-to)) ?\ ))
-	(setq secret-to (1- secret-to)))
-
-      (save-excursion
-	(goto-char from)
-	(while (< (point) secret-to)
-	  (let ((old (get-text-property (point) 'secret)))
-	    (when old
-	      (subst-char-in-region (point) (1+ (point)) secret old)))
-	  (forward-char))))
-
-    (set-text-properties from to (list 'field widget
-				       'read-only nil
-				       'keymap map
-				       'local-map map
-				       help-property help-echo
-				       'face face))
-    
-    (when secret 
-      (save-excursion
-	(goto-char from)
-	(while (< (point) secret-to)
-	  (let ((old (following-char)))
-	    (subst-char-in-region (point) (1+ (point)) old secret)
-	    (put-text-property (point) (1+ (point)) 'secret old))
-	  (forward-char))))
-
-    (unless (widget-get widget :size)
-      (add-text-properties to (1+ to) (list 'field widget
-					    help-property help-echo
-					    'face face)))
-    (add-text-properties to (1+ to) (list 'local-map map
-					  'keymap map))))
+      (setq help-echo 'widget-mouse-help))    
+    (widget-put widget :field-overlay overlay)
+    (overlay-put overlay 'detachable nil)
+    (overlay-put overlay 'field widget)
+    (overlay-put overlay 'local-map map)
+    (overlay-put overlay 'keymap map)
+    (overlay-put overlay 'face face)
+    (overlay-put overlay 'balloon-help help-echo)
+    (overlay-put overlay 'help-echo help-echo)))
 
 (defun widget-specify-button (widget from to)
-  ;; Specify button for WIDGET between FROM and TO.
+  "Specify button for WIDGET between FROM and TO."
   (let ((face (widget-apply widget :button-face-get))
 	(help-echo (widget-get widget :help-echo))
-	(help-property (if (featurep 'balloon-help)
-			   'balloon-help
-			 'help-echo)))
+	(overlay (make-overlay from to nil t nil)))
+    (widget-put widget :button-overlay overlay)
     (unless (or (null help-echo) (stringp help-echo))
       (setq help-echo 'widget-mouse-help))
-    (add-text-properties from to (list 'button widget
-				       'mouse-face widget-mouse-face
-				       'start-open t
-				       'end-open t
-				       help-property help-echo
-				       'face face))))
+    (overlay-put overlay 'button widget)
+    (overlay-put overlay 'mouse-face widget-mouse-face)
+    (overlay-put overlay 'balloon-help help-echo)
+    (overlay-put overlay 'help-echo help-echo)
+    (overlay-put overlay 'face face)))
 
 (defun widget-mouse-help (extent)
   "Find mouse help string for button in extent."
@@ -418,6 +354,7 @@
   (unless (widget-get widget :inactive)
     (let ((overlay (make-overlay from to nil t nil)))
       (overlay-put overlay 'face 'widget-inactive-face)
+      (overlay-put overlay 'mouse-face 'widget-inactive-face)
       (overlay-put overlay 'evaporate t)
       (overlay-put overlay 'priority 100)
       (overlay-put overlay (if (string-match "XEmacs" emacs-version)
@@ -501,9 +438,10 @@
 
 (defun widget-apply-action (widget &optional event)
   "Apply :action in WIDGET in response to EVENT."
-  (if (widget-apply widget :active)
-      (widget-apply widget :action event)
-    (error "Attempt to perform action on inactive widget")))
+  (let (after-change-functions)
+    (if (widget-apply widget :active)
+	(widget-apply widget :action event)
+      (error "Attempt to perform action on inactive widget"))))
 
 ;;; Helper functions.
 ;;
@@ -560,27 +498,23 @@
 		       (repeat :tag "Suffixes"
 			       (string :format "%v")))))
 
-(defun widget-glyph-insert (widget tag image)
-  "In WIDGET, insert the text TAG or, if supported, IMAGE.
-IMAGE should either be a glyph, an image instantiator, or an image file
-name sans extension (xpm, xbm, gif, jpg, or png) located in
-`widget-glyph-directory'.
-
-WARNING: If you call this with a glyph, and you want the user to be
-able to activate the glyph, make sure it is unique.  If you use the
-same glyph for multiple widgets, activating any of the glyphs will
-cause the last created widget to be activated."
-  (cond ((not (and (string-match "XEmacs" emacs-version)
+(defun widget-glyph-find (image tag)
+  "Create a glyph corresponding to IMAGE with string TAG as fallback.
+IMAGE should either already be a glyph, or be a file name sans
+extension (xpm, xbm, gif, jpg, or png) located in
+`widget-glyph-directory'." 
+  (cond ((not (and image 
+		   (string-match "XEmacs" emacs-version)
 		   widget-glyph-enable
 		   (fboundp 'make-glyph)
 		   (fboundp 'locate-file)
 		   image))
 	 ;; We don't want or can't use glyphs.
-	 (insert tag))
+	 nil)
 	((and (fboundp 'glyphp)
 	      (glyphp image))
-	 ;; Already a glyph.  Insert it.
-	 (widget-glyph-insert-glyph widget image))
+	 ;; Already a glyph.  Use it.
+	 image)
 	((stringp image)
 	 ;; A string.  Look it up in relevant directories.
 	 (let* ((dirlist (list (or widget-glyph-directory
@@ -592,49 +526,72 @@
 	   (while (and formats (not file))
 	     (when (valid-image-instantiator-format-p (car (car formats)))
 	       (setq file (locate-file image dirlist
-				       (mapconcat 'identity (cdr (car formats))
+				       (mapconcat 'identity
+						  (cdr (car formats))
 						  ":"))))
-	     (setq formats (cdr formats)))
-	   ;; We create a glyph with the file as the default image
-	   ;; instantiator, and the TAG fallback
-	   (widget-glyph-insert-glyph
-	    widget
-	    (make-glyph (if file
-			    (list (vector (car (car formats)) ':file file)
-				  (vector 'string ':data tag))
-			  (vector 'string ':data tag))))))
+	     (unless file
+	       (setq formats (cdr formats))))
+	   (and file
+		;; We create a glyph with the file as the default image
+		;; instantiator, and the TAG fallback
+		(make-glyph (list (vector (car (car formats)) ':file file)
+				  (vector 'string ':data tag))))))
 	((valid-instantiator-p image 'image)
-	 ;; A valid image instantiator (e.g. [gif ':file "somefile"] etc.)
-	 (widget-glyph-insert-glyph widget
-				    (list image
-					  (vector 'string ':data tag))))
+	 ;; A valid image instantiator (e.g. [gif :file "somefile"] etc.)
+	 (make-glyph (list image
+			   (vector 'string ':data tag))))
+	((consp image)
+	 ;; This could be virtually anything.  Let `make-glyph' sort it out.
+	 (make-glyph image))
 	(t
 	 ;; Oh well.
-	 (insert tag))))
+	 nil)))
+
+(defun widget-glyph-insert (widget tag image &optional down inactive)
+  "In WIDGET, insert the text TAG or, if supported, IMAGE.
+IMAGE should either be a glyph, an image instantiator, or an image file
+name sans extension (xpm, xbm, gif, jpg, or png) located in
+`widget-glyph-directory'.
+
+Optional arguments DOWN and INACTIVE is used instead of IMAGE when the
+glyph is pressed or inactive, respectively. 
+
+WARNING: If you call this with a glyph, and you want the user to be
+able to invoke the glyph, make sure it is unique.  If you use the
+same glyph for multiple widgets, invoking any of the glyphs will
+cause the last created widget to be invoked.
+
+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."
+  (let ((glyph (widget-glyph-find image tag)))
+    (if glyph 
+	(widget-glyph-insert-glyph widget 
+				   glyph
+				   (widget-glyph-find down tag)
+				   (widget-glyph-find inactive tag))
+      (insert tag))))
 
 (defun widget-glyph-insert-glyph (widget glyph &optional down inactive)
-  "In WIDGET, with alternative text TAG, insert GLYPH."
+  "In WIDGET, insert GLYPH.
+If optional arguments DOWN and INACTIVE are given, they should be
+glyphs used when the widget is pushed and inactive, respectively."
   (set-glyph-property glyph 'widget widget)
   (when down
     (set-glyph-property down 'widget widget))
   (when inactive
     (set-glyph-property inactive 'widget widget))
   (insert "*")
-  (add-text-properties (1- (point)) (point) 
-		       (list 'invisible t
-			     'end-glyph glyph))
+  (let ((ext (make-extent (point) (1- (point))))
+	(help-echo (widget-get widget :help-echo)))
+    (set-extent-property ext 'invisible t)
+    (set-extent-end-glyph ext glyph)
+    (when help-echo
+      (set-extent-property ext 'balloon-help help-echo)
+      (set-extent-property ext 'help-echo help-echo)))
   (widget-put widget :glyph-up glyph)
   (when down (widget-put widget :glyph-down down))
-  (when inactive (widget-put widget :glyph-inactive inactive))
-  (let ((help-echo (widget-get widget :help-echo)))
-    (when help-echo
-      (let ((extent (extent-at (1- (point)) nil 'end-glyph))
-	    (help-property (if (featurep 'balloon-help)
-			       'balloon-help
-			     'help-echo)))
-	(set-extent-property extent help-property (if (stringp help-echo)
-						      help-echo
-						    'widget-mouse-help))))))
+  (when inactive (widget-put widget :glyph-inactive inactive)))
 
 ;;; Buttons.
 
@@ -645,12 +602,12 @@
 (defcustom widget-button-prefix ""
   "String used as prefix for buttons."
   :type 'string
-  :group 'widgets)
+  :group 'widget-button)
 
 (defcustom widget-button-suffix ""
   "String used as suffix for buttons."
   :type 'string
-  :group 'widgets)
+  :group 'widget-button)
 
 (defun widget-button-insert-indirect (widget key)
   "Insert value of WIDGET's KEY property."
@@ -771,9 +728,7 @@
 
 (unless widget-keymap 
   (setq widget-keymap (make-sparse-keymap))
-  (define-key widget-keymap "\C-k" 'widget-kill-line)
   (define-key widget-keymap "\t" 'widget-forward)
-  (define-key widget-keymap "\M-\t" 'widget-backward)
   (define-key widget-keymap [(shift tab)] 'widget-backward)
   (define-key widget-keymap [backtab] 'widget-backward)
   (if (string-match "XEmacs" emacs-version)
@@ -795,6 +750,8 @@
   (setq widget-field-keymap (copy-keymap widget-keymap))
   (unless (string-match "XEmacs" (emacs-version))
     (define-key widget-field-keymap [menu-bar] 'nil))
+  (define-key widget-field-keymap "\C-k" 'widget-kill-line)
+  (define-key widget-field-keymap "\M-\t" 'widget-complete)
   (define-key widget-field-keymap "\C-m" 'widget-field-activate)
   (define-key widget-field-keymap "\C-a" 'widget-beginning-of-line)
   (define-key widget-field-keymap "\C-e" 'widget-end-of-line)
@@ -812,9 +769,9 @@
   (set-keymap-parent widget-text-keymap global-map))
 
 (defun widget-field-activate (pos &optional event)
-  "Activate the ediable field at point."
+  "Invoke the ediable field at point."
   (interactive "@d")
-  (let ((field (get-text-property pos 'field)))
+  (let ((field (get-char-property pos 'field)))
     (if field
 	(widget-apply-action field event)
       (call-interactively
@@ -829,32 +786,30 @@
   :group 'widgets)
 
 (defun widget-button-click (event)
-  "Activate button below mouse pointer."
+  "Invoke button below mouse pointer."
   (interactive "@e")
   (cond ((and (fboundp 'event-glyph)
 	      (event-glyph event))
 	 (widget-glyph-click event))
 	((widget-event-point event)
 	 (let* ((pos (widget-event-point event))
-		(button (get-text-property pos 'button)))
+		(button (get-char-property pos 'button)))
 	   (if button
-	       (let ((begin (previous-single-property-change (1+ pos) 'button))
-		     (end (next-single-property-change pos 'button))
-		     overlay)
+	       (let* ((overlay (widget-get button :button-overlay))
+		      (face (overlay-get overlay 'face))
+		      (mouse-face (overlay-get overlay 'mouse-face)))
 		 (unwind-protect
 		     (let ((track-mouse t))
-		       (setq overlay (make-overlay begin end))
-		       (overlay-put overlay 'face 'widget-button-pressed-face)
+		       (overlay-put overlay
+				    'face 'widget-button-pressed-face)
 		       (overlay-put overlay 
 				    'mouse-face 'widget-button-pressed-face)
 		       (unless (widget-apply button :mouse-down-action event)
 			 (while (not (button-release-event-p event))
-			   (setq event (if (fboundp 'read-event)
-					   (read-event)
-					 (next-event))
+			   (setq event (widget-read-event)
 				 pos (widget-event-point event))
 			   (if (and pos
-				    (eq (get-text-property pos 'button)
+				    (eq (get-char-property pos 'button)
 					button))
 			       (progn 
 				 (overlay-put overlay 
@@ -863,22 +818,37 @@
 				 (overlay-put overlay 
 					      'mouse-face 
 					      'widget-button-pressed-face))
-			     (overlay-put overlay 'face nil)
-			     (overlay-put overlay 'mouse-face nil))))
-		       
+			     (overlay-put overlay 'face face)
+			     (overlay-put overlay 'mouse-face mouse-face))))
 		       (when (and pos 
-				  (eq (get-text-property pos 'button) button))
+				  (eq (get-char-property pos 'button) button))
 			 (widget-apply-action button event)))
-		   (delete-overlay overlay)))
-	     (call-interactively 
-	      (or (lookup-key widget-global-map [ button2 ])
-		  (lookup-key widget-global-map [ down-mouse-2 ])
-		  (lookup-key widget-global-map [ mouse-2]))))))
+		   (overlay-put overlay 'face face)
+		   (overlay-put overlay 'mouse-face mouse-face)))
+	     (let (command up)
+	       ;; 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 command	;down event
+			    (lookup-key widget-global-map [ down-mouse-2 ])))
+		     ((setq command	;up event
+			    (lookup-key widget-global-map [ button2up ]))
+		      (setq up t))
+		     ((setq command	;up event
+			    (lookup-key widget-global-map [ mouse-2]))
+		      (setq up t)))
+	       (when command
+		 ;; Don't execute up events twice.
+		 (when up
+		   (while (not (button-release-event-p event))
+		     (setq event (widget-read-event))))
+		 (call-interactively command))))))
 	(t
 	 (message "You clicked somewhere weird."))))
 
 (defun widget-button1-click (event)
-  "Activate glyph below mouse pointer."
+  "Invoke glyph below mouse pointer."
   (interactive "@e")
   (if (and (fboundp 'event-glyph)
 	   (event-glyph event))
@@ -913,9 +883,9 @@
 	       (widget-apply-action widget event)))))))
 
 (defun widget-button-press (pos &optional event)
-  "Activate button at POS."
+  "Invoke button at POS."
   (interactive "@d")
-  (let ((button (get-text-property pos 'button)))
+  (let ((button (get-char-property pos 'button)))
     (if button
 	(widget-apply-action button event)
       (let ((command (lookup-key widget-global-map (this-command-keys))))
@@ -925,77 +895,47 @@
 (defun widget-move (arg)
   "Move point to the ARG next field or button.
 ARG may be negative to move backward."
-  (while (> arg 0)
-    (setq arg (1- arg))
-    (let ((next (cond ((get-text-property (point) 'button)
-		       (next-single-property-change (point) 'button))
-		      ((get-text-property (point) 'field)
-		       (next-single-property-change (point) 'field))
-		      (t
-		       (point)))))
-      (if (null next)			; Widget extends to end. of buffer
-	  (setq next (point-min)))
-      (let ((button (next-single-property-change next 'button))
-	    (field (next-single-property-change next 'field)))
-	(cond ((or (get-text-property next 'button)
-		   (get-text-property next 'field))
-	       (goto-char next))
-	      ((and button field)
-	       (goto-char (min button field)))
-	      (button (goto-char button))
-	      (field (goto-char field))
-	      (t
-	       (let ((button (next-single-property-change (point-min) 'button))
-		     (field (next-single-property-change (point-min) 'field)))
-		 (cond ((and button field) (goto-char (min button field)))
-		       (button (goto-char button))
-		       (field (goto-char field))
-		       (t
-			(error "No buttons or fields found"))))))
-	(setq button (widget-at (point)))
-	(if (or (and button (widget-get button :tab-order)
-		     (< (widget-get button :tab-order) 0))
-		(and button (not (widget-apply button :active))))
-	    (setq arg (1+ arg))))))
-  (while (< arg 0)
-    (if (= (point-min) (point))
+  (or (bobp) (> arg 0) (backward-char))
+  (let ((pos (point))
+	(number arg)
+	(old (or (get-char-property (point) 'button)
+		 (get-char-property (point) 'field)))
+	new)
+    ;; Forward.
+    (while (> arg 0)
+      (if (eobp)
+	  (goto-char (point-min))
 	(forward-char 1))
-    (setq arg (1+ arg))
-    (let ((previous (cond ((get-text-property (1- (point)) 'button)
-			   (previous-single-property-change (point) 'button))
-			  ((get-text-property (1- (point)) 'field)
-			   (previous-single-property-change (point) 'field))
-			  (t
-			   (point)))))
-      (if (null previous)		; Widget extends to beg. of buffer
-	  (setq previous (point-max)))
-      (let ((button (previous-single-property-change previous 'button))
-	    (field (previous-single-property-change previous 'field)))
-	(cond ((and button field)
-	       (goto-char (max button field)))
-	      (button (goto-char button))
-	      (field (goto-char field))
-	      (t
-	       (let ((button (previous-single-property-change
-			      (point-max) 'button))
-		     (field (previous-single-property-change
-			     (point-max) 'field)))
-		 (cond ((and button field) (goto-char (max button field)))
-		       (button (goto-char button))
-		       (field (goto-char field))
-		       (t
-			(error "No buttons or fields found"))))))))
-    (let ((button (previous-single-property-change (point) 'button))
-	  (field (previous-single-property-change (point) 'field)))
-      (cond ((and button field)
-	     (goto-char (max button field)))
-	    (button (goto-char button))
-	    (field (goto-char field)))
-      (setq button (widget-at (point)))
-      (if (or (and button (widget-get button :tab-order)
-		   (< (widget-get button :tab-order) 0))
-	      (and button (not (widget-apply button :active))))
-	  (setq arg (1- arg)))))
+      (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))))
+	(when new
+	  (unless (eq new old)
+	    (unless (and (widget-get new :tab-order)
+			 (< (widget-get new :tab-order) 0))
+	      (setq arg (1- arg)))
+	    (setq old new)))))
+    ;; Backward.
+    (while (< arg 0)
+      (if (bobp)
+	  (goto-char (point-max))
+	(backward-char 1))
+      (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))))
+	(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))
+    (forward-char))
   (widget-echo-help (point))
   (run-hooks 'widget-move-hook))
 
@@ -1016,27 +956,46 @@
 (defun widget-beginning-of-line ()
   "Go to beginning of field or beginning of line, whichever is first."
   (interactive)
-  (let ((bol (save-excursion (beginning-of-line) (point)))
-	(prev (previous-single-property-change (point) 'field)))
-    (goto-char (max bol (or prev bol)))))
+  (let* ((field (widget-field-find (point)))
+	 (start (and field (widget-field-start field))))
+    (if (and start (not (eq start (point))))
+	(goto-char start)
+      (call-interactively 'beginning-of-line))))
 
 (defun widget-end-of-line ()
   "Go to end of field or end of line, whichever is first."
   (interactive)
-  (let ((bol (save-excursion (end-of-line) (point)))
-	(prev (next-single-property-change (point) 'field)))
-    (goto-char (min bol (or prev bol)))))
+  (let* ((field (widget-field-find (point)))
+	 (end (and field (widget-field-end field))))
+    (if (and end (not (eq end (point))))
+	(goto-char end)
+      (call-interactively 'end-of-line))))
 
 (defun widget-kill-line ()
   "Kill to end of field or end of line, whichever is first."
   (interactive)
-  (let ((field (get-text-property (point) 'field))
-	(newline (save-excursion (search-forward "\n")))
+  (let ((field (get-char-property (point) 'field))
+	(newline (save-excursion (forward-line 1)))
 	(next (next-single-property-change (point) 'field)))
     (if (and field (> newline next))
 	(kill-region (point) next)
       (call-interactively 'kill-line))))
 
+(defcustom widget-complete-field (lookup-key global-map "\M-\t")
+  "Default function to call for completion inside fields."
+  :options '(ispell-complete-word complete-tag lisp-complete-symbol)
+  :type 'function
+  :group 'widgets)
+
+(defun widget-complete ()
+  "Complete content of editable field from point.
+When not inside a field, move to the previous button or field."
+  (interactive)
+  (let ((field (widget-field-find (point))))
+    (if field
+	(widget-apply field :complete)
+      (error "Not in an editable field"))))
+
 ;;; Setting up the buffer.
 
 (defvar widget-field-new nil)
@@ -1056,15 +1015,15 @@
       (setq field (car widget-field-new)
 	    widget-field-new (cdr widget-field-new)
 	    widget-field-list (cons field widget-field-list))
-      (let ((from (widget-get field :value-from))
-	    (to (widget-get field :value-to)))
+      (let ((from (car (widget-get field :field-overlay)))
+	    (to (cdr (widget-get field :field-overlay))))
 	(widget-specify-field field from to)
-	(move-marker from (1- from))
-	(move-marker to (1+ to)))))
+	(set-marker from nil)
+	(set-marker to nil))))
   (widget-clear-undo)
   ;; We need to maintain text properties and size of the editing fields.
   (make-local-variable 'after-change-functions)
-  (if widget-field-list
+  (if (and widget-field-list)
       (setq after-change-functions '(widget-after-change))
     (setq after-change-functions nil)))
 
@@ -1076,63 +1035,67 @@
 ;; The widget data before the change.
 (make-variable-buffer-local 'widget-field-was)
 
+(defun widget-field-buffer (widget)
+  "Return the start of WIDGET's editing field."
+  (overlay-buffer (widget-get widget :field-overlay)))
+
+(defun widget-field-start (widget)
+  "Return the start of WIDGET's editing field."
+  (overlay-start (widget-get widget :field-overlay)))
+
+(defun widget-field-end (widget)
+  "Return the end of WIDGET's editing field."
+  ;; Don't subtract one if local-map works at the end of the overlay.
+  (1- (overlay-end (widget-get widget :field-overlay))))
+
 (defun widget-field-find (pos)
-  ;; Find widget whose editing field is located at POS.
-  ;; Return nil if POS is not inside and editing field.
-  ;; 
-  ;; This is only used in `widget-field-modified', since ordinarily
-  ;; you would just test the field property.
+  "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 ((from (widget-get field :value-from))
-	    (to (widget-get field :value-to)))
-	(if (and from to (< from pos) (> to  pos))
-	    (setq fields nil
-		  found field))))
+      (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-after-change (from to old)
   ;; Adjust field size and text properties.
   (condition-case nil
       (let ((field (widget-field-find from))
-	    (inhibit-read-only t))
-	(cond ((null field))
-	      ((not (eq field (widget-field-find to)))
-	       (debug)
-	       (message "Error: `widget-after-change' called on two fields"))
-	      (t
-	       (let ((size (widget-get field :size)))
-		 (if size 
-		     (let ((begin (1+ (widget-get field :value-from)))
-			   (end (1- (widget-get field :value-to))))
-		       (widget-specify-field-update field begin end)
-		       (cond ((< (- end begin) size)
-			      ;; Field too small.
-			      (save-excursion
-				(goto-char end)
-				(insert-char ?\  (- (+ begin size) end))
-				(widget-specify-field-update field 
-							     begin
-							     (+ begin size))))
-			     ((> (- end begin) size)
-			      ;; Field too large and
-			      (if (or (< (point) (+ begin size))
-				      (> (point) end))
-				  ;; Point is outside extra space.
-				  (setq begin (+ begin size))
-				;; Point is within the extra space.
-				(setq begin (point)))
-			      (save-excursion
-				(goto-char end)
-				(while (and (eq (preceding-char) ?\ )
-					    (> (point) begin))
-				  (delete-backward-char 1))))))
-		   (widget-specify-field-update field from to)))
-	       (widget-apply field :notify field))))
-    (error (debug))))
+	    (other (widget-field-find to)))
+	(when field
+	  (unless (eq field other)
+	    (debug "Change in different fields"))
+	  (let ((size (widget-get field :size)))
+	    (when size 
+	      (let ((begin (widget-field-start field))
+		    (end (widget-field-end field)))
+		(cond ((< (- end begin) size)
+		       ;; Field too small.
+		       (save-excursion
+			 (goto-char end)
+			 (insert-char ?\  (- (+ begin size) end))))
+		      ((> (- end begin) size)
+		       ;; Field too large and
+		       (if (or (< (point) (+ begin size))
+			       (> (point) end))
+			   ;; Point is outside extra space.
+			   (setq begin (+ begin size))
+			 ;; Point is within the extra space.
+			 (setq begin (point)))
+		       (save-excursion
+			 (goto-char end)
+			 (while (and (eq (preceding-char) ?\ )
+				     (> (point) begin))
+			   (delete-backward-char 1))))))))
+	  (widget-apply field :notify field)))
+    (error (debug "After Change"))))
 
 ;;; Widget Functions
 ;;
@@ -1188,6 +1151,7 @@
   :value-to-external (lambda (widget value) value)
   :button-prefix 'widget-button-prefix
   :button-suffix 'widget-button-suffix
+  :complete 'widget-default-complete				       
   :create 'widget-default-create
   :indent nil
   :offset 0
@@ -1207,6 +1171,12 @@
   :notify 'widget-default-notify
   :prompt-value 'widget-default-prompt-value)
 
+(defun widget-default-complete (widget)
+  "Call the value of the :complete-function property of WIDGET.
+If that does not exists, call the value of `widget-complete-field'."
+  (let ((fun (widget-get widget :complete-function)))
+    (call-interactively (or fun widget-complete-field))))
+
 (defun widget-default-create (widget)
   "Create WIDGET at point in the current buffer."
   (widget-specify-insert
@@ -1305,18 +1275,9 @@
 	     ;; Get rid of trailing newlines.
 	     (when (string-match "\n+\\'" doc-text)
 	       (setq doc-text (substring doc-text 0 (match-beginning 0))))
-	     (push (if (string-match "\n." doc-text)
-		       ;; Allow multiline doc to be hiden.
-		       (widget-create-child-and-convert
-			widget 'widget-help 
-			:doc (progn
-			       (string-match "\\`.*" doc-text)
-			       (match-string 0 doc-text))
-			:widget-doc doc-text
-			"?")
-		     ;; A single line is just inserted.
-		     (widget-create-child-and-convert
-		      widget 'item :format "%d" :doc doc-text nil))
+	     (push (widget-create-child-and-convert
+		    widget 'documentation-string
+		    doc-text)
 		   buttons)))
 	  (t 
 	   (error "Unknown escape `%c'" escape)))
@@ -1334,9 +1295,15 @@
   ;; Remove widget from the buffer.
   (let ((from (widget-get widget :from))
 	(to (widget-get widget :to))
-	(inhibit-read-only t)
-	after-change-functions)
+	(inactive-overlay (widget-get widget :inactive))
+	(button-overlay (widget-get widget :button-overlay))
+	after-change-functions
+	(inhibit-read-only t))
     (widget-apply widget :value-delete)
+    (when inactive-overlay
+      (delete-overlay inactive-overlay))
+    (when button-overlay
+      (delete-overlay button-overlay))
     (when (< from to)
       ;; Kludge: this doesn't need to be true for empty formats.
       (delete-region from to))
@@ -1422,9 +1389,21 @@
   (let ((value (widget-get widget :value)))
     (and (listp value)
 	 (<= (length value) (length values))
-	 (let ((head (subseq values 0 (length value))))
+	 (let ((head (widget-sublist values 0 (length value))))
 	   (and (equal head value)
-		(cons head (subseq values (length value))))))))
+		(cons head (widget-sublist values (length value))))))))
+
+(defun widget-sublist (list start &optional end)
+  "Return the sublist of LIST from START to END.
+If END is omitted, it defaults to the length of LIST."
+  (if (> start 0) (setq list (nthcdr start list)))
+  (if end
+      (if (<= end start)
+	  nil
+	(setq list (copy-sequence list))
+	(setcdr (nthcdr (- end start 1) list) nil)
+	list)
+    (copy-sequence list)))
 
 (defun widget-item-action (widget &optional event)
   ;; Just notify itself.
@@ -1492,12 +1471,12 @@
 
 ;;; The `link' Widget.
 
-(defcustom widget-link-prefix "_"
+(defcustom widget-link-prefix "["
   "String used as prefix for links."
   :type 'string
   :group 'widget-button)
 
-(defcustom widget-link-suffix "_"
+(defcustom widget-link-suffix "]"
   "String used as suffix for links."
   :type 'string
   :group 'widget-button)
@@ -1578,8 +1557,8 @@
 		   (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)))
+    (widget-setup)
+    (widget-apply widget :notify widget event)))
 
 (defun widget-field-validate (widget)
   ;; Valid if the content matches `:valid-regexp'.
@@ -1592,59 +1571,54 @@
 
 (defun widget-field-value-create (widget)
   ;; Create an editable text field.
-  (insert " ")
   (let ((size (widget-get widget :size))
 	(value (widget-get widget :value))
-	(from (point)))
+	(from (point))
+	(overlay (cons (make-marker) (make-marker))))
+    (widget-put widget :field-overlay overlay)
     (insert value)
     (and size
 	 (< (length value) size)
 	 (insert-char ?\  (- size (length value))))
     (unless (memq widget widget-field-list)
       (setq widget-field-new (cons widget widget-field-new)))
-    (widget-put widget :value-to (copy-marker (point)))
-    (set-marker-insertion-type (widget-get widget :value-to) nil)
-    (if (null size)
-	(insert ?\n)
-      (insert ?\ ))
-    (widget-put widget :value-from (copy-marker from))
-    (set-marker-insertion-type (widget-get widget :value-from) t)))
+    (move-marker (cdr overlay) (point))
+    (set-marker-insertion-type (cdr overlay) nil)
+    (when (null size)
+      (insert ?\n))
+    (move-marker (car overlay) from)
+    (set-marker-insertion-type (car overlay) t)))
 
 (defun widget-field-value-delete (widget)
   ;; Remove the widget from the list of active editing fields.
   (setq widget-field-list (delq widget widget-field-list))
   ;; These are nil if the :format string doesn't contain `%v'.
-  (when (widget-get widget :value-from)
-    (set-marker (widget-get widget :value-from) nil))
-  (when (widget-get widget :value-from)
-    (set-marker (widget-get widget :value-to) nil)))
+  (let ((overlay (widget-get widget :field-overlay)))
+    (when overlay
+      (delete-overlay overlay))))
 
 (defun widget-field-value-get (widget)
   ;; Return current text in editing field.
-  (let ((from (widget-get widget :value-from))
-	(to (widget-get widget :value-to))
+  (let ((from (widget-field-start widget))
+	(to (widget-field-end widget))
+	(buffer (widget-field-buffer widget))
 	(size (widget-get widget :size))
 	(secret (widget-get widget :secret))
 	(old (current-buffer)))
     (if (and from to)
 	(progn 
-	  (set-buffer (marker-buffer from))
-	  (setq from (1+ from)
-		to (1- 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 (string-match "XEmacs" emacs-version)
-	      ;; XEmacs 20.1 bug: b-s-n-p doesn't clear all properties. 
-	      (setq result (format "%s" result)))
 	    (when secret
 	      (let ((index 0))
 		(while (< (+ from index) to)
 		  (aset result index
-			(get-text-property (+ from index) 'secret))
+			(get-char-property (+ from index) 'secret))
 		  (setq index (1+ index)))))
 	    (set-buffer old)
 	    result))
@@ -1711,7 +1685,7 @@
 (defcustom widget-choice-toggle nil
   "If non-nil, a binary choice will just toggle between the values.
 Otherwise, the user will explicitly have to choose between the values
-when he activate the menu."
+when he invoked the menu."
   :type 'boolean
   :group 'widgets)
 
@@ -1778,8 +1752,8 @@
       (widget-value-set widget 
 			(widget-apply current :value-to-external
 				      (widget-get current :value)))
-      (widget-apply widget :notify widget event)
-      (widget-setup))))
+      (widget-setup)
+      (widget-apply widget :notify widget event))))
 
 (defun widget-choice-validate (widget)
   ;; Valid if we have made a valid choice.
@@ -2328,7 +2302,7 @@
 	    (setq children (cdr children)))
 	  (setcdr children (cons child (cdr children)))))))
   (widget-setup)
- widget (widget-apply widget :notify widget))
+  (widget-apply widget :notify widget))
 
 (defun widget-editable-list-delete-at (widget child)
   ;; Delete child from list of children.
@@ -2460,20 +2434,88 @@
 	(cons found vals)
       nil)))
 
-;;; The `widget-help' Widget.
+;;; The `visibility' Widget.
+
+(define-widget 'visibility 'item
+  "An indicator and manipulator for hidden items."
+  :format "%[%v%]"
+  :button-prefix ""
+  :button-suffix ""
+  :on "hide"
+  :off "show"
+  :value-create 'widget-visibility-value-create
+  :action 'widget-toggle-action
+  :match (lambda (widget value) t))
+
+(defun widget-visibility-value-create (widget)
+  ;; Insert text representing the `on' and `off' states.
+  (let ((on (widget-get widget :on))
+	(off (widget-get widget :off)))
+    (if on
+	(setq on (concat widget-push-button-prefix
+			 on
+			 widget-push-button-suffix))
+      (setq on ""))
+    (if off
+	(setq off (concat widget-push-button-prefix
+			 off
+			 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")
+      (insert "..."))))
+
+;;; The `documentation-string' Widget.
 
-(define-widget 'widget-help 'push-button
-  "The widget documentation button."
-  :format "%[%t%] %d"
-  :help-echo "Toggle display of documentation."
-  :action 'widget-help-action)
+(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 'widgets)
+
+(define-widget 'documentation-string 'item
+  "A documentation string."
+  :format "%v"
+  :action 'widget-documentation-string-action
+  :value-delete 'widget-children-value-delete
+  :value-create 'widget-documentation-string-value-create)
 
-(defun widget-help-action (widget &optional event)
-  "Toggle documentation for WIDGET."
-  (let ((old (widget-get widget :doc))
-	(new (widget-get widget :widget-doc)))
-    (widget-put widget :doc new)
-    (widget-put widget :widget-doc old))
+(defun widget-documentation-string-value-create (widget)
+  ;; Insert documentation string.
+  (let ((doc (widget-value widget))
+	(shown (widget-get (widget-get widget :parent) :documentation-shown)))
+    (if (string-match "\n" doc)
+	(let ((before (substring doc 0 (match-beginning 0)))
+	      (after (substring doc (match-beginning 0)))
+	      (start (point))
+	      buttons)
+	  (insert before " ")
+	  (widget-specify-doc widget start (point))
+	  (push (widget-create-child-and-convert
+		 widget 'visibility
+		 :off nil
+		 :action 'widget-parent-action
+		 shown)
+		buttons)
+	  (when shown
+	    (setq start (point))
+	    (insert after)
+	    (widget-specify-doc widget start (point)))
+	  (widget-put widget :buttons buttons))
+      (insert doc)))
+  (insert "\n"))
+
+(defun widget-documentation-string-action (widget &rest ignore)
+  ;; Toggle documentation.
+  (let ((parent (widget-get widget :parent)))
+    (widget-put parent :documentation-shown 
+		(not (widget-get parent :documentation-shown))))
+  ;; Redraw.
   (widget-value-set widget (widget-value widget)))
 
 ;;; The Sexp Widgets.
@@ -2507,6 +2549,7 @@
   "A string"
   :tag "String"
   :format "%{%t%}: %v"
+  :complete-function 'ispell-complete-word
   :prompt-history 'widget-string-prompt-value-history)
 
 (define-widget 'regexp 'string
@@ -2534,7 +2577,7 @@
 
 (define-widget 'file 'string
   "A file widget.  
-It will read a file name from the minibuffer when activated."
+It will read a file name from the minibuffer when invoked."
   :prompt-value 'widget-file-prompt-value
   :format "%{%t%}: %v"
   :tag "File"
@@ -2561,12 +2604,12 @@
 	 (answer (read-file-name (concat menu-tag ": (default `" value "') ")
 				 dir nil must-match file)))
     (widget-value-set widget (abbreviate-file-name answer))
-    (widget-apply widget :notify widget event)
-    (widget-setup)))
+    (widget-setup)
+    (widget-apply widget :notify widget event)))
 
 (define-widget 'directory 'file
   "A directory widget.  
-It will read a directory name from the minibuffer when activated."
+It will read a directory name from the minibuffer when invoked."
   :tag "Directory")
 
 (defvar widget-symbol-prompt-value-history nil
@@ -2605,6 +2648,7 @@
 
 (define-widget 'function 'sexp
   "A lisp function."
+  :complete-function 'lisp-complete-symbol
   :prompt-value 'widget-field-prompt-value
   :prompt-internal 'widget-symbol-prompt-internal
   :prompt-match 'fboundp
@@ -2636,7 +2680,9 @@
 
 (defun widget-sexp-value-to-internal (widget value)
   ;; Use pp for printer representation.
-  (let ((pp (pp-to-string value)))
+  (let ((pp (if (symbolp value)
+		(prin1-to-string value)
+	      (pp-to-string value))))
     (while (string-match "\n\\'" pp)
       (setq pp (substring pp 0 -1)))
     (if (or (string-match "\n\\'" pp)
@@ -2843,11 +2889,14 @@
   :sample-face-get 'widget-color-item-button-face-get)
 
 (defun widget-color-item-button-face-get (widget)
-  ;; We create a face from the value.
-  (require 'facemenu)
-  (condition-case nil
-      (facemenu-get-face (intern (concat "fg:" (widget-value widget))))
-    (error 'default)))
+  (let ((symbol (intern (concat "fg:" (widget-value widget)))))
+    (if (string-match "XEmacs" emacs-version)
+	(prog1 symbol
+	  (or (find-face symbol)
+	      (set-face-foreground (make-face symbol) (widget-value widget))))
+      (condition-case nil
+	  (facemenu-get-face symbol)
+	(error 'default)))))
 
 (define-widget 'color 'push-button
   "Choose a color name (with sample)."
@@ -2902,8 +2951,8 @@
 			(read-string prompt (widget-value widget))))))
     (unless (zerop (length answer))
       (widget-value-set widget answer)
-      (widget-apply widget :notify widget event)
-      (widget-setup))))
+      (widget-setup)
+      (widget-apply widget :notify widget event))))
 
 ;;; The Help Echo
 
@@ -2941,8 +2990,8 @@
 
 (defun widget-at (pos)
   "The button or field at POS."
-  (or (get-text-property pos 'button)
-      (get-text-property pos 'field)))
+  (or (get-char-property pos 'button)
+      (get-char-property pos 'field)))
 
 (defun widget-echo-help (pos)
   "Display the help echo for widget at POS."