Fix un-doubled backslashes.
[bpt/emacs.git] / lisp / wid-edit.el
index 355d3da..c9b962e 100644 (file)
@@ -1,6 +1,6 @@
 ;;; wid-edit.el --- Functions for creating and using widgets -*-byte-compile-dynamic: t;-*-
 ;;
-;; Copyright (C) 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
+;; Copyright (C) 1996, 1997, 1999, 2000, 2001 Free Software Foundation, Inc.
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Maintainer: FSF
@@ -120,7 +120,12 @@ This exists as a variable so it can be set locally in certain buffers.")
   :type 'face
   :group 'widget-faces)
 
-(defface widget-field-face '((((class grayscale color)
+;; TTY gets special definitions here and in the next defface, because
+;; the gray colors defined for other displays cause black text on a black
+;; background, at least on light-background TTYs.
+(defface widget-field-face '((((type tty))
+                             (:background "yellow3"))
+                            (((class grayscale color)
                               (background light))
                              (:background "gray85"))
                             (((class grayscale color)
@@ -131,7 +136,9 @@ This exists as a variable so it can be set locally in certain buffers.")
   "Face used for editable fields."
   :group 'widget-faces)
 
-(defface widget-single-line-field-face '((((class grayscale color)
+(defface widget-single-line-field-face '((((type tty))
+                                         (:background "green3"))
+                                        (((class grayscale color)
                                           (background light))
                                          (:background "gray85"))
                                         (((class grayscale color)
@@ -204,7 +211,7 @@ mouse event, and the number of elements in items is less than
 `widget-menu-max-size', a popup menu will be used, otherwise the
 minibuffer."
   (cond ((and (< (length items) widget-menu-max-size)
-             event (display-mouse-p))
+             event (display-popup-menus-p))
         ;; Mouse click.
         (x-popup-menu event
                       (list title (cons "" items))))
@@ -254,7 +261,7 @@ minibuffer."
           ;; that corresponds to it.
           (save-window-excursion
             (let ((buf (get-buffer " widget-choose")))
-              (display-buffer buf)
+              (fit-window-to-buffer (display-buffer buf))
               (let ((cursor-in-echo-area t)
                     keys
                     (char 0)
@@ -323,6 +330,8 @@ new value.")
        (help-echo (widget-get widget :help-echo))
        (rear-sticky
         (or (not widget-field-add-space) (widget-get widget :size))))
+    (if (functionp help-echo)
+      (setq help-echo 'widget-mouse-help))    
     (when (= (char-before to) ?\n)
       ;; When the last character in the field is a newline, we want to
       ;; give it a `field' char-property of `boundary', which helps the
@@ -367,15 +376,27 @@ new value.")
 
 (defun widget-specify-button (widget from to)
   "Specify button for WIDGET between FROM and TO."
-  (let ((overlay (make-overlay from to nil t nil)))
+  (let ((overlay (make-overlay from to nil t nil))
+       (help-echo (widget-get widget :help-echo)))
     (widget-put widget :button-overlay overlay)
+    (if (functionp help-echo)
+      (setq help-echo 'widget-mouse-help))
     (overlay-put overlay 'button widget)
     (overlay-put overlay 'keymap (widget-get widget :keymap))
     ;; We want to avoid the face with image buttons.
     (unless (widget-get widget :suppress-face)
       (overlay-put overlay 'face (widget-apply widget :button-face-get))
       (overlay-put overlay 'mouse-face widget-mouse-face))
-    (overlay-put overlay 'help-echo (widget-get widget :help-echo))))
+    (overlay-put overlay 'help-echo help-echo)))
+
+(defun widget-mouse-help (window overlay point)
+  "Help-echo callback for widgets whose :help-echo is a function."
+  (with-current-buffer (overlay-buffer overlay)
+    (let* ((widget (widget-at (overlay-start overlay)))
+          (help-echo (if widget (widget-get widget :help-echo))))
+      (if (functionp help-echo)
+         (funcall help-echo widget)
+       help-echo))))
 
 (defun widget-specify-sample (widget from to)
   "Specify sample for WIDGET between FROM and TO."
@@ -817,7 +838,7 @@ Recommended as a parent keymap for modes using widgets.")
   "Keymap used inside a text field.")
 
 (defun widget-field-activate (pos &optional event)
-  "Invoke the ediable field at point."
+  "Invoke the editable field at point."
   (interactive "@d")
   (let ((field (widget-field-at pos)))
     (if field
@@ -835,49 +856,72 @@ Recommended as a parent keymap for modes using widgets.")
 
 (defun widget-button-click (event)
   "Invoke the button that the mouse is pointing at."
-  (interactive "@e")
+  (interactive "e")
   (if (widget-event-point event)
-      (save-excursion
-       (mouse-set-point event)
-       (let* ((pos (widget-event-point event))
-              (button (get-char-property pos 'button)))
-         (if button
-             (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))
-                     (save-excursion
-                       (when face      ; avoid changing around image
-                         (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 (widget-button-release-event-p event))
-                           (setq event (read-event)
-                                 pos (widget-event-point event))
-                           (if (and pos
-                                    (eq (get-char-property pos 'button)
-                                        button))
-                               (when face
-                                 (overlay-put overlay
-                                              'face
-                                              widget-button-pressed-face)
-                                 (overlay-put overlay
-                                              'mouse-face
-                                              widget-button-pressed-face))
-                             (overlay-put overlay 'face face)
-                             (overlay-put overlay 'mouse-face mouse-face))))
-                       (when (and pos
-                                  (eq (get-char-property pos 'button) button))
-                         (widget-apply-action button event))))
-                 (overlay-put overlay 'face face)
-                 (overlay-put overlay 'mouse-face mouse-face)))
-           (let ((up t)
-                 command)
-             ;; Find the global command to run, and check whether it
-             ;; is bound to an up event.
+      (let* ((pos (widget-event-point event))
+            (start (event-start event))
+            (button (get-char-property 
+                     pos 'button (and (windowp (posn-window start))
+                                      (window-buffer (posn-window start))))))
+       (if button
+           ;; Mouse click on a widget button.  Do the following
+           ;; in a save-excursion so that the click on the button
+           ;; doesn't change point.
+           (save-selected-window
+             (select-window (posn-window (event-start event)))
+             (save-excursion
+               (goto-char (posn-point (event-start event)))
+               (let* ((overlay (widget-get button :button-overlay))
+                      (face (overlay-get overlay 'face))
+                      (mouse-face (overlay-get overlay 'mouse-face)))
+                 (unwind-protect
+                     ;; Read events, including mouse-movement events
+                     ;; until we receive a release event.  Highlight/
+                     ;; unhighlight the button the mouse was initially
+                     ;; on when we move over it.
+                     (let ((track-mouse t))
+                       (save-excursion
+                         (when face    ; avoid changing around image
+                           (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 (widget-button-release-event-p event))
+                             (setq event (read-event)
+                                   pos (widget-event-point event))
+                             (if (and pos
+                                      (eq (get-char-property pos 'button)
+                                          button))
+                                 (when face
+                                   (overlay-put overlay
+                                                'face
+                                                widget-button-pressed-face)
+                                   (overlay-put overlay
+                                                'mouse-face
+                                                widget-button-pressed-face))
+                               (overlay-put overlay 'face face)
+                               (overlay-put overlay 'mouse-face mouse-face))))
+
+                         ;; When mouse is released over the button, run
+                         ;; its action function.
+                         (when (and pos
+                                    (eq (get-char-property pos 'button) button))
+                           (widget-apply-action button event))))
+                   (overlay-put overlay 'face face)
+                   (overlay-put overlay 'mouse-face mouse-face))))
+
+             (unless (pos-visible-in-window-p (widget-event-point event))
+               (mouse-set-point event)
+               (beginning-of-line)
+               (recenter))
+             )
+
+           (let ((up t) command)
+             ;; Mouse click not on a widget button.  Find the global
+             ;; command to run, and check whether it is bound to an
+             ;; up event.
+             (mouse-set-point event)
              (if (memq (event-basic-type event) '(mouse-1 down-mouse-1))
                  (cond ((setq command  ;down event
                               (lookup-key widget-global-map [down-mouse-1]))
@@ -895,10 +939,6 @@ Recommended as a parent keymap for modes using widgets.")
                  (setq event (read-event))))
              (when command
                (call-interactively command)))))
-         (unless (pos-visible-in-window-p (widget-event-point event))
-           (mouse-set-point event)
-           (beginning-of-line)
-           (recenter)))
     (message "You clicked somewhere weird.")))
 
 (defun widget-button-press (pos &optional event)
@@ -1079,12 +1119,17 @@ When not inside a field, move to the previous button or field."
 (defun widget-field-buffer (widget)
   "Return the start of WIDGET's editing field."
   (let ((overlay (widget-get widget :field-overlay)))
-    (and overlay (overlay-buffer overlay))))
+    (cond ((overlayp overlay)
+          (overlay-buffer overlay))
+         ((consp overlay)
+          (marker-buffer (car overlay))))))
 
 (defun widget-field-start (widget)
   "Return the start of WIDGET's editing field."
   (let ((overlay (widget-get widget :field-overlay)))
-    (and overlay (overlay-start overlay))))
+    (if (overlayp overlay)
+       (overlay-start overlay)
+      (car overlay))))
 
 (defun widget-field-end (widget)
   "Return the end of WIDGET's editing field."
@@ -1092,15 +1137,16 @@ When not inside a field, move to the previous button or field."
     ;; Don't subtract one if local-map works at the end of the overlay,
     ;; or if a special `boundary' field has been added after the widget
     ;; field.
-    (and overlay
-        (if (and (not (eq (get-char-property (overlay-end overlay)
-                                             'field
-                                             (widget-field-buffer widget))
-                          'boundary))
-                 (or widget-field-add-space
-                     (null (widget-get widget :size))))
-            (1- (overlay-end overlay))
-          (overlay-end overlay)))))
+    (if (overlayp overlay)
+       (if (and (not (eq (get-char-property (overlay-end overlay)
+                                            'field
+                                            (widget-field-buffer widget))
+                         'boundary))
+                (or widget-field-add-space
+                    (null (widget-get widget :size))))
+           (1- (overlay-end overlay))
+         (overlay-end overlay))
+      (cdr overlay))))
 
 (defun widget-field-find (pos)
   "Return the field at POS.
@@ -1731,9 +1777,10 @@ the earlier input."
 (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))
+  (setq widget-field-new (delq widget widget-field-new))
   ;; These are nil if the :format string doesn't contain `%v'.
   (let ((overlay (widget-get widget :field-overlay)))
-    (when overlay
+    (when (overlayp overlay)
       (delete-overlay overlay))))
 
 (defun widget-field-value-get (widget)
@@ -1770,8 +1817,8 @@ the earlier input."
 ;;; The `text' Widget.
 
 (define-widget 'text 'editable-field
-  :keymap widget-text-keymap
-  "A multiline text area.")
+  "A multiline text area."
+  :keymap widget-text-keymap)
 
 ;;; The `menu-choice' Widget.
 
@@ -1954,9 +2001,18 @@ when he invoked the menu."
 (defun widget-toggle-value-create (widget)
   "Insert text representing the `on' and `off' states."
   (if (widget-value widget)
-      (widget-image-insert widget
-                          (widget-get widget :on)
-                          (widget-get widget :on-glyph))
+      (progn
+       (and (display-graphic-p)
+            (listp (widget-get widget :on-glyph))
+            (widget-put widget :on-glyph
+                        (eval (widget-get widget :on-glyph))))
+       (widget-image-insert widget
+                            (widget-get widget :on)
+                            (widget-get widget :on-glyph)))
+    (and (display-graphic-p)
+        (listp (widget-get widget :off-glyph))
+        (widget-put widget :off-glyph
+                    (eval (widget-get widget :off-glyph))))
     (widget-image-insert widget
                         (widget-get widget :off)
                         (widget-get widget :off-glyph))))
@@ -1978,15 +2034,19 @@ when he invoked the menu."
   ;; We could probably do the same job as the images using single
   ;; space characters in a boxed face with a stretch specification to
   ;; make them square.
-  :on-glyph (create-image (make-bool-vector 49 1)
-                         'xbm t :width 7 :height 7
-                         :foreground "grey75" ; like default mode line
-                         :relief -3 :ascent 'center)
-  :off "[ ]"
-  :off-glyph (create-image (make-bool-vector 49 1)
+  :on-glyph '(create-image "\000\066\076\034\076\066\000"
                           'xbm t :width 7 :height 7
-                          :foreground "grey75"
-                          :relief 3 :ascent 'center)
+                          :background "grey75" ; like default mode line
+                          :foreground "black"
+                          :relief -3
+                          :ascent 'center)
+  :off "[ ]"
+  :off-glyph '(create-image (make-string 7 0)
+                           'xbm t :width 7 :height 7
+                           :background "grey75"
+                           :foreground "black"
+                           :relief 3
+                           :ascent 'center)
   :help-echo "Toggle this item."
   :action 'widget-checkbox-action)
 
@@ -2007,7 +2067,6 @@ when he invoked the menu."
   :format "%v"
   :offset 4
   :entry-format "%b %v"
-  :menu-tag "checklist"
   :greedy nil
   :value-create 'widget-checklist-value-create
   :value-delete 'widget-children-value-delete
@@ -2185,7 +2244,6 @@ Return an alist of (TYPE MATCH)."
   :offset 4
   :format "%v"
   :entry-format "%b %v"
-  :menu-tag "radio"
   :value-create 'widget-radio-value-create
   :value-delete 'widget-children-value-delete
   :value-get 'widget-radio-value-get
@@ -2375,7 +2433,6 @@ Return an alist of (TYPE MATCH)."
   :format "%v%i\n"
   :format-handler 'widget-editable-list-format-handler
   :entry-format "%i %d %v"
-  :menu-tag "editable-list"
   :value-create 'widget-editable-list-value-create
   :value-delete 'widget-children-value-delete
   :value-get 'widget-editable-list-value-get
@@ -2886,6 +2943,7 @@ It will read a file name from the minibuffer when invoked."
 ;;;    (widget-setup)
 ;;;    (widget-apply widget :notify widget event)))
 
+;; Fixme: use file-name-as-directory.
 (define-widget 'directory 'file
   "A directory widget.
 It will read a directory name from the minibuffer when invoked."
@@ -2936,6 +2994,7 @@ It will read a directory name from the minibuffer when invoked."
   :prompt-match 'fboundp
   :prompt-history 'widget-function-prompt-value-history
   :action 'widget-field-action
+  :match-alternatives '(functionp)
   :validate (lambda (widget)
              (unless (functionp (widget-value widget))
                (widget-put widget :error (format "Invalid function: %S"
@@ -2948,7 +3007,6 @@ It will read a directory name from the minibuffer when invoked."
   "History of input to `widget-variable-prompt-value'.")
 
 (define-widget 'variable 'symbol
-  ;; Should complete on variables.
   "A Lisp variable."
   :prompt-match 'boundp
   :prompt-history 'widget-variable-prompt-value-history
@@ -3169,10 +3227,11 @@ To use this type, you must define :match or :match-alternatives."
 (defun widget-plist-convert-widget (widget)
   ;; Handle `:options'.
   (let* ((options (widget-get widget :options))
+        (widget-plist-value-type (widget-get widget :value-type))
         (other `(editable-list :inline t
                                (group :inline t
                                       ,(widget-get widget :key-type)
-                                      ,(widget-get widget :value-type))))
+                                      ,widget-plist-value-type)))
         (args (if options
                   (list `(checklist :inline t
                                     :greedy t
@@ -3213,10 +3272,11 @@ To use this type, you must define :match or :match-alternatives."
 (defun widget-alist-convert-widget (widget)
   ;; Handle `:options'.
   (let* ((options (widget-get widget :options))
+        (widget-alist-value-type (widget-get widget :value-type))
         (other `(editable-list :inline t
                                (cons :format "%v"
                                      ,(widget-get widget :key-type)
-                                     ,(widget-get widget :value-type))))
+                                     ,widget-alist-value-type)))
         (args (if options
                   (list `(checklist :inline t
                                     :greedy t
@@ -3322,6 +3382,7 @@ To use this type, you must define :match or :match-alternatives."
 \f
 ;;; The `color' Widget.
 
+;; Fixme: match 
 (define-widget 'color 'editable-field
   "Choose a color name (with sample)."
   :format "%t: %v (%{sample%})\n"
@@ -3356,14 +3417,13 @@ To use this type, you must define :match or :match-alternatives."
 (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))))
-    (condition-case nil
-       (facemenu-get-face symbol)
-      (error 'default))))
+                 (error (widget-get widget :value)))))
+    (if (color-defined-p value)
+       (list (cons 'foreground-color value))
+      'default)))
 
 (defun widget-color-action (widget &optional event)
-  ;; Prompt for a color.
+  "Prompt for a color."
   (let* ((tag (widget-apply widget :menu-tag-get))
         (prompt (concat tag ": "))
         (value (widget-value widget))
@@ -3389,26 +3449,12 @@ To use this type, you must define :match or :match-alternatives."
 ;;; The Help Echo
 
 (defun widget-echo-help (pos)
-  "Display the help echo for widget at POS."
+  "Display help-echo text for widget at POS."
   (let* ((widget (widget-at pos))
         (help-echo (and widget (widget-get widget :help-echo))))
-    (if (or (stringp help-echo)
-           (and (functionp help-echo)
-                ;; Kluge: help-echo originally could be a function of
-                ;; one arg -- the widget.  It is more useful in Emacs
-                ;; 21 to have it as a function usable also as a
-                ;; help-echo property, when it can sort out its own
-                ;; widget if necessary.  Try both calling sequences
-                ;; (rather than messing around to get the function's
-                ;; arity).
-                (stringp
-                 (setq help-echo
-                       (condition-case nil
-                           (funcall help-echo
-                                    (selected-window) (current-buffer)
-                                    (point))
-                         (error (funcall help-echo widget))))))
-           (stringp (eval help-echo)))
+    (if (functionp help-echo)
+       (setq help-echo (funcall help-echo widget)))
+    (if (stringp help-echo)
        (message "%s" help-echo))))
 
 ;;; The End: