]> code.delx.au - gnu-emacs/blobdiff - lisp/wid-edit.el
(compose-string, encode-composition-rule, compose-last-chars):
[gnu-emacs] / lisp / wid-edit.el
index 05ef4b95658831b0956d90fc62f5a12982ac34ab..d861429e212c4dad698b6c85914340b5434627fd 100644 (file)
@@ -1,6 +1,6 @@
 ;;; wid-edit.el --- Functions for creating and using widgets -*-byte-compile-dynamic: t;-*-
 ;;
-;; Copyright (C) 1996,97,1999,2000,01,02,2003, 2004  Free Software Foundation, Inc.
+;; Copyright (C) 1996,97,1999,2000,01,02,2003, 2004, 2005  Free Software Foundation, Inc.
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Maintainer: FSF
@@ -20,8 +20,8 @@
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Wishlist items (from widget.texi):
 
   :group 'widgets
   :group 'faces)
 
-(defvar widget-documentation-face 'widget-documentation-face
+(defvar widget-documentation-face 'widget-documentation
   "Face used for documentation strings in widgets.
 This exists as a variable so it can be set locally in certain buffers.")
 
-(defface widget-documentation-face '((((class color)
-                                      (background dark))
-                                     (:foreground "lime green"))
-                                    (((class color)
-                                      (background light))
-                                     (:foreground "dark green"))
-                                    (t nil))
+(defface widget-documentation '((((class color)
+                                 (background dark))
+                                (:foreground "lime green"))
+                               (((class color)
+                                 (background light))
+                                (:foreground "dark green"))
+                               (t nil))
   "Face used for documentation text."
   :group 'widget-documentation
   :group 'widget-faces)
+;; backward compatibility alias
+(put 'widget-documentation-face 'face-alias 'widget-documentation)
 
-(defvar widget-button-face 'widget-button-face
+(defvar widget-button-face 'widget-button
   "Face used for buttons in widgets.
 This exists as a variable so it can be set locally in certain buffers.")
 
-(defface widget-button-face '((t (:weight bold)))
+(defface widget-button '((t (:weight bold)))
   "Face used for widget buttons."
   :group 'widget-faces)
+;; backward compatibility alias
+(put 'widget-button-face 'face-alias 'widget-button)
 
 (defcustom widget-mouse-face 'highlight
   "Face used for widget buttons when the mouse is above them."
@@ -120,33 +124,37 @@ This exists as a variable so it can be set locally in certain buffers.")
 ;; 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"
-                             :foreground "black")
-                            (((class grayscale color)
-                              (background light))
-                             :background "gray85")
-                            (((class grayscale color)
-                              (background dark))
-                             :background "dim gray")
-                            (t
-                             :slant italic))
+(defface widget-field '((((type tty))
+                        :background "yellow3"
+                        :foreground "black")
+                       (((class grayscale color)
+                         (background light))
+                        :background "gray85")
+                       (((class grayscale color)
+                         (background dark))
+                        :background "dim gray")
+                       (t
+                        :slant italic))
   "Face used for editable fields."
   :group 'widget-faces)
-
-(defface widget-single-line-field-face '((((type tty))
-                                         :background "green3"
-                                         :foreground "black")
-                                        (((class grayscale color)
-                                          (background light))
-                                         :background "gray85")
-                                        (((class grayscale color)
-                                          (background dark))
-                                         :background "dim gray")
-                                        (t
-                                         :slant italic))
+;; backward-compatibility alias
+(put 'widget-field-face 'face-alias 'widget-field)
+
+(defface widget-single-line-field '((((type tty))
+                                    :background "green3"
+                                    :foreground "black")
+                                   (((class grayscale color)
+                                     (background light))
+                                    :background "gray85")
+                                   (((class grayscale color)
+                                     (background dark))
+                                    :background "dim gray")
+                                   (t
+                                    :slant italic))
   "Face used for editable fields spanning only a single line."
   :group 'widget-faces)
+;; backward-compatibility alias
+(put 'widget-single-line-field-face 'face-alias 'widget-single-line-field)
 
 ;;; This causes display-table to be loaded, and not usefully.
 ;;;(defvar widget-single-line-display-table
@@ -325,8 +333,9 @@ new value.")
           (insert-and-inherit " ")))
     (setq to (point)))
   (let ((keymap (widget-get widget :keymap))
-       (face (or (widget-get widget :value-face) 'widget-field-face))
+       (face (or (widget-get widget :value-face) 'widget-field))
        (help-echo (widget-get widget :help-echo))
+       (follow-link (widget-get widget :follow-link))
        (rear-sticky
         (or (not widget-field-add-space) (widget-get widget :size))))
     (if (functionp help-echo)
@@ -339,10 +348,13 @@ new value.")
       ;; one character.
       (let ((overlay (make-overlay (1- to) to nil t nil)))
        (overlay-put overlay 'field 'boundary)
+        ;; We need the real field for tabbing.
+       (overlay-put overlay 'real-field widget)
        ;; Use `local-map' here, not `keymap', so that normal editing
        ;; works in the field when, say, Custom uses `suppress-keymap'.
        (overlay-put overlay 'local-map keymap)
        (overlay-put overlay 'face face)
+       (overlay-put overlay 'follow-link follow-link)
        (overlay-put overlay 'help-echo help-echo))
       (setq to (1- to))
       (setq rear-sticky t))
@@ -352,6 +364,7 @@ new value.")
       (overlay-put overlay 'field widget)
       (overlay-put overlay 'local-map keymap)
       (overlay-put overlay 'face face)
+      (overlay-put overlay 'follow-link follow-link)
       (overlay-put overlay 'help-echo help-echo)))
   (widget-specify-secret widget))
 
@@ -376,6 +389,7 @@ 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))
+       (follow-link (widget-get widget :follow-link))
        (help-echo (widget-get widget :help-echo)))
     (widget-put widget :button-overlay overlay)
     (if (functionp help-echo)
@@ -385,8 +399,13 @@ new value.")
     (overlay-put overlay 'evaporate t)
     ;; 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 'face (widget-apply widget :button-face-get))
+      ; Text terminals cannot change mouse pointer shape, so use mouse
+      ; face instead.
+      (or (display-graphic-p)
+         (overlay-put overlay 'mouse-face widget-mouse-face)))
     (overlay-put overlay 'pointer 'hand)
+    (overlay-put overlay 'follow-link follow-link)
     (overlay-put overlay 'help-echo help-echo)))
 
 (defun widget-mouse-help (window overlay point)
@@ -422,24 +441,20 @@ new value.")
       (prog1 (progn ,@form)
        (goto-char (point-max))))))
 
-(defface widget-inactive-face '((((class grayscale color)
-                                 (background dark))
-                                (:foreground "light gray"))
-                               (((class grayscale color)
-                                 (background light))
-                                (:foreground "dim gray"))
-                               (t
-                                (:slant italic)))
+(defface widget-inactive
+  '((t :inherit shadow))
   "Face used for inactive widgets."
   :group 'widget-faces)
+;; backward-compatibility alias
+(put 'widget-inactive-face 'face-alias 'widget-inactive)
 
 (defun widget-specify-inactive (widget from to)
   "Make WIDGET inactive for user modifications."
   (unless (widget-get widget :inactive)
     (let ((overlay (make-overlay from to nil t nil)))
-      (overlay-put overlay 'face 'widget-inactive-face)
+      (overlay-put overlay 'face 'widget-inactive)
       ;; This is disabled, as it makes the mouse cursor change shape.
-      ;; (overlay-put overlay 'mouse-face 'widget-inactive-face)
+      ;; (overlay-put overlay 'mouse-face 'widget-inactive)
       (overlay-put overlay 'evaporate t)
       (overlay-put overlay 'priority 100)
       (overlay-put overlay 'modification-hooks '(widget-overlay-inactive))
@@ -622,7 +637,7 @@ extension (xpm, xbm, gif, jpg, or png) located in
         ;; Oh well.
         nil)))
 
-(defvar widget-button-pressed-face 'widget-button-pressed-face
+(defvar widget-button-pressed-face 'widget-button-pressed
   "Face used for pressed buttons in widgets.
 This exists as a variable so it can be set locally in certain
 buffers.")
@@ -871,13 +886,17 @@ Recommended as a parent keymap for modes using widgets.")
       (call-interactively
        (lookup-key widget-global-map (this-command-keys))))))
 
-(defface widget-button-pressed-face
-  '((((class color))
+(defface widget-button-pressed
+  '((((min-colors 88) (class color))
+     (:foreground "red1"))
+    (((class color))
      (:foreground "red"))
     (t
      (:weight bold :underline t)))
   "Face used for pressed buttons."
   :group 'widget-faces)
+;; backward-compatibility alias
+(put 'widget-button-pressed-face 'face-alias 'widget-button-pressed)
 
 (defun widget-button-click (event)
   "Invoke the button that the mouse is pointing at."
@@ -904,14 +923,14 @@ Recommended as a parent keymap for modes using widgets.")
                      ;; 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)
+                     (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)
+                         (let ((track-mouse t))
                            (while (not (widget-button-release-event-p event))
                              (setq event (read-event)
                                    pos (widget-event-point event))
@@ -926,13 +945,13 @@ Recommended as a parent keymap for modes using widgets.")
                                                 'mouse-face
                                                 widget-button-pressed-face))
                                (overlay-put overlay 'face face)
-                               (overlay-put overlay 'mouse-face mouse-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))))
+                       ;; 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))))
 
@@ -942,28 +961,28 @@ Recommended as a parent keymap for modes using widgets.")
                (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]))
-                        (setq up nil))
-                       ((setq command  ;up event
-                              (lookup-key widget-global-map [mouse-1]))))
+         (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-2]))
+                            (lookup-key widget-global-map [down-mouse-1]))
                       (setq up nil))
                      ((setq command    ;up event
-                            (lookup-key widget-global-map [mouse-2])))))
-             (when up
-               ;; Don't execute up events twice.
-               (while (not (widget-button-release-event-p event))
-                 (setq event (read-event))))
-             (when command
-               (call-interactively command)))))
+                            (lookup-key widget-global-map [mouse-1]))))
+             (cond ((setq command      ;down event
+                          (lookup-key widget-global-map [down-mouse-2]))
+                    (setq up nil))
+                   ((setq command      ;up event
+                          (lookup-key widget-global-map [mouse-2])))))
+           (when up
+             ;; Don't execute up events twice.
+             (while (not (widget-button-release-event-p event))
+               (setq event (read-event))))
+           (when command
+             (call-interactively command)))))
     (message "You clicked somewhere weird.")))
 
 (defun widget-button-press (pos &optional event)
@@ -1085,14 +1104,23 @@ the field."
   :type 'function
   :group 'widgets)
 
+(defun widget-narrow-to-field ()
+  "Narrow to field"
+  (interactive)
+  (let ((field (widget-field-find (point))))
+    (if field
+       (narrow-to-region (line-beginning-position) (line-end-position)))))
+
 (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"))))
+       (save-restriction
+         (widget-narrow-to-field)
+         (widget-apply field :complete))
+         (error "Not in an editable field"))))
 
 ;;; Setting up the buffer.
 
@@ -1140,7 +1168,7 @@ When not inside a field, move to the previous button or field."
   "Return the widget field at POS, or nil if none."
   (let ((field (get-char-property (or pos (point)) 'field)))
     (if (eq field 'boundary)
-       nil
+       (get-char-property (or pos (point)) 'real-field)
       field)))
 
 (defun widget-field-buffer (widget)
@@ -1165,9 +1193,17 @@ When not inside a field, move to the previous button or field."
     ;; or if a special `boundary' field has been added after the widget
     ;; field.
     (if (overlayp overlay)
-       (if (and (not (eq (get-char-property (overlay-end overlay)
-                                            'field
-                                            (widget-field-buffer widget))
+       (if (and (not (eq (with-current-buffer
+                             (widget-field-buffer widget)
+                           (save-restriction
+                             ;; `widget-narrow-to-field' can be
+                             ;; active when this function is called
+                             ;; from an change-functions hook. So
+                             ;; temporarily remove field narrowing
+                             ;; before to call `get-char-property'.
+                             (widen)
+                             (get-char-property (overlay-end overlay)
+                                                'field)))
                          'boundary))
                 (or widget-field-add-space
                     (null (widget-get widget :size))))
@@ -1694,6 +1730,7 @@ If END is omitted, it defaults to the length of LIST."
   "An embedded link."
   :button-prefix 'widget-link-prefix
   :button-suffix 'widget-link-suffix
+  :follow-link "\C-m"
   :help-echo "Follow the link."
   :format "%[%t%]")
 
@@ -2922,7 +2959,7 @@ as the value."
   :match 'widget-regexp-match
   :validate 'widget-regexp-validate
   ;; Doesn't work well with terminating newline.
-  ;; :value-face 'widget-single-line-field-face
+  ;; :value-face 'widget-single-line-field
   :tag "Regexp")
 
 (defun widget-regexp-match (widget value)
@@ -2948,7 +2985,7 @@ It will read a file name from the minibuffer when invoked."
   :prompt-value 'widget-file-prompt-value
   :format "%{%t%}: %v"
   ;; Doesn't work well with terminating newline.
-  ;; :value-face 'widget-single-line-field-face
+  ;; :value-face 'widget-single-line-field
   :tag "File")
 
 (defun widget-file-complete ()
@@ -3042,7 +3079,7 @@ It will read a directory name from the minibuffer when invoked."
 (defvar widget-function-prompt-value-history nil
   "History of input to `widget-function-prompt-value'.")
 
-(define-widget 'function 'sexp
+(define-widget 'function 'restricted-sexp
   "A Lisp function."
   :complete-function (lambda ()
                       (interactive)