aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPer Abrahamsen1997-05-14 17:31:13 +0000
committerPer Abrahamsen1997-05-14 17:31:13 +0000
commita3c88c59875e24341f55af4e0b40da96558c655e (patch)
tree42d379391d4fbe6c7b3390640bfda6bc8cfc941d
parent86bd10bcd8c1c2c189d8599287daa7d2bb3d4c70 (diff)
downloademacs-a3c88c59875e24341f55af4e0b40da96558c655e.tar.gz
emacs-a3c88c59875e24341f55af4e0b40da96558c655e.zip
Synched with 1.97.
-rw-r--r--lisp/custom.el2
-rw-r--r--lisp/wid-browse.el19
-rw-r--r--lisp/wid-edit.el527
-rw-r--r--lisp/widget.el7
4 files changed, 385 insertions, 170 deletions
diff --git a/lisp/custom.el b/lisp/custom.el
index d49265d0c12..aa03886ac67 100644
--- a/lisp/custom.el
+++ b/lisp/custom.el
@@ -4,7 +4,7 @@
4;; 4;;
5;; Author: Per Abrahamsen <abraham@dina.kvl.dk> 5;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6;; Keywords: help, faces 6;; Keywords: help, faces
7;; Version: 1.90 7;; Version: 1.97
8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ 8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
9 9
10;; This file is part of GNU Emacs. 10;; This file is part of GNU Emacs.
diff --git a/lisp/wid-browse.el b/lisp/wid-browse.el
index 984d802f75b..f8e309a1a3b 100644
--- a/lisp/wid-browse.el
+++ b/lisp/wid-browse.el
@@ -4,9 +4,26 @@
4;; 4;;
5;; Author: Per Abrahamsen <abraham@dina.kvl.dk> 5;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6;; Keywords: extensions 6;; Keywords: extensions
7;; Version: 1.90 7;; Version: 1.97
8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ 8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
9 9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
26
10;;; Commentary: 27;;; Commentary:
11;; 28;;
12;; Widget browser. See `widget.el'. 29;; Widget browser. See `widget.el'.
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index 555ab181f1a..9542df9089e 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -4,7 +4,7 @@
4;; 4;;
5;; Author: Per Abrahamsen <abraham@dina.kvl.dk> 5;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6;; Keywords: extensions 6;; Keywords: extensions
7;; Version: 1.90 7;; Version: 1.97
8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ 8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
9 9
10;; This file is part of GNU Emacs. 10;; This file is part of GNU Emacs.
@@ -65,6 +65,16 @@ and `end-open' if it should sticky to the front."
65 "Make text between FROM and TO intangible." 65 "Make text between FROM and TO intangible."
66 (put-text-property from to 'intangible 'front))) 66 (put-text-property from to 'intangible 'front)))
67 67
68 (if (string-match "XEmacs" emacs-version)
69 (defun widget-event-point (event)
70 "Character position of the end of event if that exists, or nil."
71 (if (mouse-event-p event)
72 (event-point event)
73 nil))
74 (defun widget-event-point (event)
75 "Character position of the end of event if that exists, or nil."
76 (posn-point (event-end event))))
77
68;; The following should go away when bundled with Emacs. 78;; The following should go away when bundled with Emacs.
69 (condition-case () 79 (condition-case ()
70 (require 'custom) 80 (require 'custom)
@@ -82,14 +92,14 @@ and `end-open' if it should sticky to the front."
82 (copy-face 'bold 'widget-button-face) 92 (copy-face 'bold 'widget-button-face)
83 (copy-face 'italic 'widget-field-face))) 93 (copy-face 'italic 'widget-field-face)))
84 94
85 (unless (fboundp 'event-point) 95 (unless (fboundp 'button-release-event-p)
86 ;; XEmacs function missing in Emacs. 96 ;; XEmacs function missing from Emacs.
87 (defun event-point (event) 97 (defun button-release-event-p (event)
88 "Return the character position of the given mouse-motion, button-press, 98 "Non-nil if EVENT is a mouse-button-release event object."
89or button-release event. If the event did not occur over a window, or did 99 (and (eventp event)
90not occur over text, then this returns nil. Otherwise, it returns an index 100 (memq (event-basic-type event) '(mouse-1 mouse-2 mouse-3))
91into the buffer visible in the event's window." 101 (or (memq 'click (event-modifiers event))
92 (posn-point (event-start event)))) 102 (memq 'drag (event-modifiers event))))))
93 103
94 (unless (fboundp 'error-message-string) 104 (unless (fboundp 'error-message-string)
95 ;; Emacs function missing in XEmacs. 105 ;; Emacs function missing in XEmacs.
@@ -142,12 +152,6 @@ into the buffer visible in the event's window."
142 "Face used for editable fields." 152 "Face used for editable fields."
143 :group 'widgets) 153 :group 'widgets)
144 154
145(defcustom widget-menu-max-size 40
146 "Largest number of items allowed in a popup-menu.
147Larger menus are read through the minibuffer."
148 :group 'widgets
149 :type 'integer)
150
151;;; Utility functions. 155;;; Utility functions.
152;; 156;;
153;; These are not really widget specific. 157;; These are not really widget specific.
@@ -179,6 +183,12 @@ Larger menus are read through the minibuffer."
179 (buffer-disable-undo (current-buffer)) 183 (buffer-disable-undo (current-buffer))
180 (buffer-enable-undo)) 184 (buffer-enable-undo))
181 185
186(defcustom widget-menu-max-size 40
187 "Largest number of items allowed in a popup-menu.
188Larger menus are read through the minibuffer."
189 :group 'widgets
190 :type 'integer)
191
182(defun widget-choose (title items &optional event) 192(defun widget-choose (title items &optional event)
183 "Choose an item from a list. 193 "Choose an item from a list.
184 194
@@ -225,36 +235,6 @@ minibuffer."
225 (cdr (assoc val items))) 235 (cdr (assoc val items)))
226 nil))))) 236 nil)))))
227 237
228(defun widget-get-sibling (widget)
229 "Get the item WIDGET is assumed to toggle.
230This is only meaningful for radio buttons or checkboxes in a list."
231 (let* ((parent (widget-get widget :parent))
232 (children (widget-get parent :children))
233 child)
234 (catch 'child
235 (while children
236 (setq child (car children)
237 children (cdr children))
238 (when (eq (widget-get child :button) widget)
239 (throw 'child child)))
240 nil)))
241
242;;; Helper functions.
243;;
244;; These are widget specific.
245
246;;;###autoload
247(defun widget-prompt-value (widget prompt &optional value unbound)
248 "Prompt for a value matching WIDGET, using PROMPT.
249The current value is assumed to be VALUE, unless UNBOUND is non-nil."
250 (unless (listp widget)
251 (setq widget (list widget)))
252 (setq widget (widget-convert widget))
253 (let ((answer (widget-apply widget :prompt-value prompt value unbound)))
254 (unless (widget-apply widget :match answer)
255 (error "Value does not match %S type." (car widget)))
256 answer))
257
258;;; Widget text specifications. 238;;; Widget text specifications.
259;; 239;;
260;; These functions are for specifying text properties. 240;; These functions are for specifying text properties.
@@ -526,6 +506,37 @@ ARGS are passed as extra arguments to the function."
526 (widget-apply widget :action event) 506 (widget-apply widget :action event)
527 (error "Attempt to perform action on inactive widget"))) 507 (error "Attempt to perform action on inactive widget")))
528 508
509;;; Helper functions.
510;;
511;; These are widget specific.
512
513;;;###autoload
514(defun widget-prompt-value (widget prompt &optional value unbound)
515 "Prompt for a value matching WIDGET, using PROMPT.
516The current value is assumed to be VALUE, unless UNBOUND is non-nil."
517 (unless (listp widget)
518 (setq widget (list widget)))
519 (setq prompt (format "[%s] %s" (widget-type widget) prompt))
520 (setq widget (widget-convert widget))
521 (let ((answer (widget-apply widget :prompt-value prompt value unbound)))
522 (unless (widget-apply widget :match answer)
523 (error "Value does not match %S type." (car widget)))
524 answer))
525
526(defun widget-get-sibling (widget)
527 "Get the item WIDGET is assumed to toggle.
528This is only meaningful for radio buttons or checkboxes in a list."
529 (let* ((parent (widget-get widget :parent))
530 (children (widget-get parent :children))
531 child)
532 (catch 'child
533 (while children
534 (setq child (car children)
535 children (cdr children))
536 (when (eq (widget-get child :button) widget)
537 (throw 'child child)))
538 nil)))
539
529;;; Glyphs. 540;;; Glyphs.
530 541
531(defcustom widget-glyph-directory (concat data-directory "custom/") 542(defcustom widget-glyph-directory (concat data-directory "custom/")
@@ -572,14 +583,23 @@ cause the last created widget to be activated."
572 ;; File not readable, give up. 583 ;; File not readable, give up.
573 (insert tag)))))) 584 (insert tag))))))
574 585
575(defun widget-glyph-insert-glyph (widget tag glyph) 586(defun widget-glyph-insert-glyph (widget tag glyph &optional down inactive)
576 "In WIDGET, with alternative text TAG, insert GLYPH." 587 "In WIDGET, with alternative text TAG, insert GLYPH."
577 (set-glyph-image glyph (cons 'tty tag)) 588 (set-glyph-image glyph (cons 'tty tag))
578 (set-glyph-property glyph 'widget widget) 589 (set-glyph-property glyph 'widget widget)
590 (when down
591 (set-glyph-image down (cons 'tty tag))
592 (set-glyph-property down 'widget widget))
593 (when inactive
594 (set-glyph-image inactive (cons 'tty tag))
595 (set-glyph-property inactive 'widget widget))
579 (insert "*") 596 (insert "*")
580 (add-text-properties (1- (point)) (point) 597 (add-text-properties (1- (point)) (point)
581 (list 'invisible t 598 (list 'invisible t
582 'end-glyph glyph)) 599 'end-glyph glyph))
600 (widget-put widget :glyph-up glyph)
601 (when down (widget-put widget :glyph-down down))
602 (when inactive (widget-put widget :glyph-inactive inactive))
583 (let ((help-echo (widget-get widget :help-echo))) 603 (let ((help-echo (widget-get widget :help-echo)))
584 (when help-echo 604 (when help-echo
585 (let ((extent (extent-at (1- (point)) nil 'end-glyph)) 605 (let ((extent (extent-at (1- (point)) nil 'end-glyph))
@@ -706,11 +726,11 @@ Recommended as a parent keymap for modes using widgets.")
706 (define-key widget-keymap "\M-\t" 'widget-backward) 726 (define-key widget-keymap "\M-\t" 'widget-backward)
707 (define-key widget-keymap [(shift tab)] 'widget-backward) 727 (define-key widget-keymap [(shift tab)] 'widget-backward)
708 (define-key widget-keymap [backtab] 'widget-backward) 728 (define-key widget-keymap [backtab] 'widget-backward)
709 (if (string-match "XEmacs" (emacs-version)) 729 (if (string-match "XEmacs" emacs-version)
710 (progn 730 (progn
711 (define-key widget-keymap [button2] 'widget-button-click) 731 ;;Glyph support.
712 (define-key widget-keymap [button1] 'widget-button1-click)) 732 (define-key widget-keymap [button1] 'widget-button1-click)
713 (define-key widget-keymap [mouse-2] 'ignore) 733 (define-key widget-keymap [button2] 'widget-button-click))
714 (define-key widget-keymap [down-mouse-2] 'widget-button-click)) 734 (define-key widget-keymap [down-mouse-2] 'widget-button-click))
715 (define-key widget-keymap "\C-m" 'widget-button-press)) 735 (define-key widget-keymap "\C-m" 'widget-button-press))
716 736
@@ -750,19 +770,56 @@ Recommended as a parent keymap for modes using widgets.")
750 (call-interactively 770 (call-interactively
751 (lookup-key widget-global-map (this-command-keys)))))) 771 (lookup-key widget-global-map (this-command-keys))))))
752 772
773(defface widget-button-pressed-face
774 '((((class color))
775 (:foreground "red"))
776 (t
777 (:bold t :underline t)))
778 "Face used for pressed buttons."
779 :group 'widgets)
780
753(defun widget-button-click (event) 781(defun widget-button-click (event)
754 "Activate button below mouse pointer." 782 "Activate button below mouse pointer."
755 (interactive "@e") 783 (interactive "@e")
756 (cond ((and (fboundp 'event-glyph) 784 (cond ((and (fboundp 'event-glyph)
757 (event-glyph event)) 785 (event-glyph event))
758 (let ((widget (glyph-property (event-glyph event) 'widget))) 786 (widget-glyph-click event))
759 (if widget 787 ((widget-event-point event)
760 (widget-apply-action widget event) 788 (let* ((pos (widget-event-point event))
761 (message "You clicked on a glyph.")))) 789 (button (get-text-property pos 'button)))
762 ((event-point event)
763 (let ((button (get-text-property (event-point event) 'button)))
764 (if button 790 (if button
765 (widget-apply-action button event) 791 (let ((begin (previous-single-property-change (1+ pos) 'button))
792 (end (next-single-property-change pos 'button))
793 overlay)
794 (unwind-protect
795 (let ((track-mouse t))
796 (setq overlay (make-overlay begin end))
797 (overlay-put overlay 'face 'widget-button-pressed-face)
798 (overlay-put overlay
799 'mouse-face 'widget-button-pressed-face)
800 (unless (widget-apply button :mouse-down-action event)
801 (while (not (button-release-event-p event))
802 (setq event (if (fboundp 'read-event)
803 (read-event)
804 (next-event))
805 pos (widget-event-point event))
806 (if (and pos
807 (eq (get-text-property pos 'button)
808 button))
809 (progn
810 (overlay-put overlay
811 'face
812 'widget-button-pressed-face)
813 (overlay-put overlay
814 'mouse-face
815 'widget-button-pressed-face))
816 (overlay-put overlay 'face nil)
817 (overlay-put overlay 'mouse-face nil))))
818
819 (when (and pos
820 (eq (get-text-property pos 'button) button))
821 (widget-apply-action button event)))
822 (delete-overlay overlay)))
766 (call-interactively 823 (call-interactively
767 (or (lookup-key widget-global-map [ button2 ]) 824 (or (lookup-key widget-global-map [ button2 ])
768 (lookup-key widget-global-map [ down-mouse-2 ]) 825 (lookup-key widget-global-map [ down-mouse-2 ])
@@ -775,12 +832,36 @@ Recommended as a parent keymap for modes using widgets.")
775 (interactive "@e") 832 (interactive "@e")
776 (if (and (fboundp 'event-glyph) 833 (if (and (fboundp 'event-glyph)
777 (event-glyph event)) 834 (event-glyph event))
778 (let ((widget (glyph-property (event-glyph event) 'widget))) 835 (widget-glyph-click event)
779 (if widget
780 (widget-apply-action widget event)
781 (message "You clicked on a glyph.")))
782 (call-interactively (lookup-key widget-global-map (this-command-keys))))) 836 (call-interactively (lookup-key widget-global-map (this-command-keys)))))
783 837
838(defun widget-glyph-click (event)
839 "Handle click on a glyph."
840 (let* ((glyph (event-glyph event))
841 (widget (glyph-property glyph 'widget))
842 (extent (event-glyph-extent event))
843 (down-glyph (or (and widget (widget-get widget :glyph-down)) glyph))
844 (up-glyph (or (and widget (widget-get widget :glyph-up)) glyph))
845 (last event))
846 ;; Wait for the release.
847 (while (not (button-release-event-p last))
848 (if (eq extent (event-glyph-extent last))
849 (set-extent-property extent 'end-glyph down-glyph)
850 (set-extent-property extent 'end-glyph up-glyph))
851 (setq last (next-event event)))
852 ;; Release glyph.
853 (when down-glyph
854 (set-extent-property extent 'end-glyph up-glyph))
855 ;; Apply widget action.
856 (when (eq extent (event-glyph-extent last))
857 (let ((widget (glyph-property (event-glyph event) 'widget)))
858 (cond ((null widget)
859 (message "You clicked on a glyph."))
860 ((not (widget-apply widget :active))
861 (message "This glyph is inactive."))
862 (t
863 (widget-apply-action widget event)))))))
864
784(defun widget-button-press (pos &optional event) 865(defun widget-button-press (pos &optional event)
785 "Activate button at POS." 866 "Activate button at POS."
786 (interactive "@d") 867 (interactive "@d")
@@ -1007,6 +1088,11 @@ With optional ARG, move across that many fields."
1007;; 1088;;
1008;; These functions are used in the definition of multiple widgets. 1089;; These functions are used in the definition of multiple widgets.
1009 1090
1091(defun widget-parent-action (widget &optional event)
1092 "Tell :parent of WIDGET to handle the :action.
1093Optional EVENT is the event that triggered the action."
1094 (widget-apply (widget-get widget :parent) :action event))
1095
1010(defun widget-children-value-delete (widget) 1096(defun widget-children-value-delete (widget)
1011 "Delete all :children and :buttons in WIDGET." 1097 "Delete all :children and :buttons in WIDGET."
1012 (mapcar 'widget-delete (widget-get widget :children)) 1098 (mapcar 'widget-delete (widget-get widget :children))
@@ -1014,11 +1100,36 @@ With optional ARG, move across that many fields."
1014 (mapcar 'widget-delete (widget-get widget :buttons)) 1100 (mapcar 'widget-delete (widget-get widget :buttons))
1015 (widget-put widget :buttons nil)) 1101 (widget-put widget :buttons nil))
1016 1102
1103(defun widget-children-validate (widget)
1104 "All the :children must be valid."
1105 (let ((children (widget-get widget :children))
1106 child found)
1107 (while (and children (not found))
1108 (setq child (car children)
1109 children (cdr children)
1110 found (widget-apply child :validate)))
1111 found))
1112
1017(defun widget-types-convert-widget (widget) 1113(defun widget-types-convert-widget (widget)
1018 "Convert :args as widget types in WIDGET." 1114 "Convert :args as widget types in WIDGET."
1019 (widget-put widget :args (mapcar 'widget-convert (widget-get widget :args))) 1115 (widget-put widget :args (mapcar 'widget-convert (widget-get widget :args)))
1020 widget) 1116 widget)
1021 1117
1118(defun widget-value-convert-widget (widget)
1119 "Initialize :value from :args in WIDGET."
1120 (let ((args (widget-get widget :args)))
1121 (when args
1122 (widget-put widget :value (car args))
1123 ;; Don't convert :value here, as this is done in `widget-convert'.
1124 ;; (widget-put widget :value (widget-apply widget
1125 ;; :value-to-internal (car args)))
1126 (widget-put widget :args nil)))
1127 widget)
1128
1129(defun widget-value-value-get (widget)
1130 "Return the :value property of WIDGET."
1131 (widget-get widget :value))
1132
1022;;; The `default' Widget. 1133;;; The `default' Widget.
1023 1134
1024(define-widget 'default nil 1135(define-widget 'default nil
@@ -1039,6 +1150,7 @@ With optional ARG, move across that many fields."
1039 :active 'widget-default-active 1150 :active 'widget-default-active
1040 :activate 'widget-specify-active 1151 :activate 'widget-specify-active
1041 :deactivate 'widget-default-deactivate 1152 :deactivate 'widget-default-deactivate
1153 :mouse-down-action (lambda (widget event) nil)
1042 :action 'widget-default-action 1154 :action 'widget-default-action
1043 :notify 'widget-default-notify 1155 :notify 'widget-default-notify
1044 :prompt-value 'widget-default-prompt-value) 1156 :prompt-value 'widget-default-prompt-value)
@@ -1233,24 +1345,15 @@ With optional ARG, move across that many fields."
1233 1345
1234(define-widget 'item 'default 1346(define-widget 'item 'default
1235 "Constant items for inclusion in other widgets." 1347 "Constant items for inclusion in other widgets."
1236 :convert-widget 'widget-item-convert-widget 1348 :convert-widget 'widget-value-convert-widget
1237 :value-create 'widget-item-value-create 1349 :value-create 'widget-item-value-create
1238 :value-delete 'ignore 1350 :value-delete 'ignore
1239 :value-get 'widget-item-value-get 1351 :value-get 'widget-value-value-get
1240 :match 'widget-item-match 1352 :match 'widget-item-match
1241 :match-inline 'widget-item-match-inline 1353 :match-inline 'widget-item-match-inline
1242 :action 'widget-item-action 1354 :action 'widget-item-action
1243 :format "%t\n") 1355 :format "%t\n")
1244 1356
1245(defun widget-item-convert-widget (widget)
1246 ;; Initialize :value from :args in WIDGET.
1247 (let ((args (widget-get widget :args)))
1248 (when args
1249 (widget-put widget :value (widget-apply widget
1250 :value-to-internal (car args)))
1251 (widget-put widget :args nil)))
1252 widget)
1253
1254(defun widget-item-value-create (widget) 1357(defun widget-item-value-create (widget)
1255 ;; Insert the printed representation of the value. 1358 ;; Insert the printed representation of the value.
1256 (let ((standard-output (current-buffer))) 1359 (let ((standard-output (current-buffer)))
@@ -1273,10 +1376,6 @@ With optional ARG, move across that many fields."
1273 ;; Just notify itself. 1376 ;; Just notify itself.
1274 (widget-apply widget :notify widget event)) 1377 (widget-apply widget :notify widget event))
1275 1378
1276(defun widget-item-value-get (widget)
1277 ;; Items are simple.
1278 (widget-get widget :value))
1279
1280;;; The `push-button' Widget. 1379;;; The `push-button' Widget.
1281 1380
1282(defcustom widget-push-button-gui t 1381(defcustom widget-push-button-gui t
@@ -1310,7 +1409,9 @@ With optional ARG, move across that many fields."
1310 (setq gui (make-gui-button tag 'widget-gui-action widget)) 1409 (setq gui (make-gui-button tag 'widget-gui-action widget))
1311 (push (cons tag gui) widget-push-button-cache)) 1410 (push (cons tag gui) widget-push-button-cache))
1312 (widget-glyph-insert-glyph widget text 1411 (widget-glyph-insert-glyph widget text
1313 (make-glyph (car (aref gui 1))))) 1412 (make-glyph (nth 0 (aref gui 1)))
1413 (make-glyph (nth 1 (aref gui 1)))
1414 (make-glyph (nth 2 (aref gui 1)))))
1314 (insert text)))) 1415 (insert text))))
1315 1416
1316(defun widget-gui-action (widget) 1417(defun widget-gui-action (widget)
@@ -1332,17 +1433,7 @@ With optional ARG, move across that many fields."
1332 1433
1333(defun widget-info-link-action (widget &optional event) 1434(defun widget-info-link-action (widget &optional event)
1334 "Open the info node specified by WIDGET." 1435 "Open the info node specified by WIDGET."
1335 (Info-goto-node (widget-value widget)) 1436 (Info-goto-node (widget-value widget)))
1336 ;; Steal button release event.
1337 (if (and (fboundp 'button-press-event-p)
1338 (fboundp 'next-command-event))
1339 ;; XEmacs
1340 (and event
1341 (button-press-event-p event)
1342 (next-command-event))
1343 ;; Emacs
1344 (when (memq 'down (event-modifiers event))
1345 (read-event))))
1346 1437
1347;;; The `url-link' Widget. 1438;;; The `url-link' Widget.
1348 1439
@@ -1359,10 +1450,13 @@ With optional ARG, move across that many fields."
1359 1450
1360(define-widget 'editable-field 'default 1451(define-widget 'editable-field 'default
1361 "An editable text field." 1452 "An editable text field."
1362 :convert-widget 'widget-item-convert-widget 1453 :convert-widget 'widget-value-convert-widget
1363 :keymap widget-field-keymap 1454 :keymap widget-field-keymap
1364 :format "%v" 1455 :format "%v"
1365 :value "" 1456 :value ""
1457 :prompt-internal 'widget-field-prompt-internal
1458 :prompt-history 'widget-field-history
1459 :prompt-value 'widget-field-prompt-value
1366 :action 'widget-field-action 1460 :action 'widget-field-action
1367 :validate 'widget-field-validate 1461 :validate 'widget-field-validate
1368 :valid-regexp "" 1462 :valid-regexp ""
@@ -1372,24 +1466,34 @@ With optional ARG, move across that many fields."
1372 :value-get 'widget-field-value-get 1466 :value-get 'widget-field-value-get
1373 :match 'widget-field-match) 1467 :match 'widget-field-match)
1374 1468
1375;; History of field minibuffer edits. 1469(defvar widget-field-history nil
1376(defvar widget-field-history nil) 1470 "History of field minibuffer edits.")
1471
1472(defun widget-field-prompt-internal (widget prompt initial history)
1473 ;; Read string for WIDGET promptinhg with PROMPT.
1474 ;; INITIAL is the initial input and HISTORY is a symbol containing
1475 ;; the earlier input.
1476 (read-string prompt initial history))
1477
1478(defun widget-field-prompt-value (widget prompt value unbound)
1479 ;; Prompt for a string.
1480 (let ((initial (if unbound
1481 nil
1482 (cons (widget-apply widget :value-to-internal
1483 value) 0)))
1484 (history (widget-get widget :prompt-history)))
1485 (let ((answer (widget-apply widget
1486 :prompt-internal prompt initial history)))
1487 (widget-apply widget :value-to-external answer))))
1377 1488
1378(defun widget-field-action (widget &optional event) 1489(defun widget-field-action (widget &optional event)
1379 ;; Edit the value in the minibuffer. 1490 ;; Edit the value in the minibuffer.
1380 (let ((tag (widget-apply widget :menu-tag-get)) 1491 (let ((invalid (widget-apply widget :validate)))
1381 (invalid (widget-apply widget :validate))) 1492 (let ((prompt (concat (widget-apply widget :menu-tag-get) ": "))
1382 (when invalid 1493 (value (unless invalid
1383 (error (widget-get invalid :error))) 1494 (widget-value widget))))
1384 (widget-value-set widget 1495 (let ((answer (widget-apply widget :prompt-value prompt value invalid) ))
1385 (widget-apply widget 1496 (widget-value-set widget answer)))
1386 :value-to-external
1387 (read-string (concat tag ": ")
1388 (widget-apply
1389 widget
1390 :value-to-internal
1391 (widget-value widget))
1392 'widget-field-history)))
1393 (widget-apply widget :notify widget event) 1497 (widget-apply widget :notify widget event)
1394 (widget-setup))) 1498 (widget-setup)))
1395 1499
@@ -1449,6 +1553,9 @@ With optional ARG, move across that many fields."
1449 (eq (char-after (1- to)) ?\ )) 1553 (eq (char-after (1- to)) ?\ ))
1450 (setq to (1- to))) 1554 (setq to (1- to)))
1451 (let ((result (buffer-substring-no-properties from to))) 1555 (let ((result (buffer-substring-no-properties from to)))
1556 (when (string-match "XEmacs" emacs-version)
1557 ;; XEmacs 20.1 bug: b-s-n-p doesn't clear all properties.
1558 (setq result (format "%s" result)))
1452 (when secret 1559 (when secret
1453 (let ((index 0)) 1560 (let ((index 0))
1454 (while (< (+ from index) to) 1561 (while (< (+ from index) to)
@@ -1482,6 +1589,7 @@ With optional ARG, move across that many fields."
1482 :value-delete 'widget-children-value-delete 1589 :value-delete 'widget-children-value-delete
1483 :value-get 'widget-choice-value-get 1590 :value-get 'widget-choice-value-get
1484 :value-inline 'widget-choice-value-inline 1591 :value-inline 'widget-choice-value-inline
1592 :mouse-down-action 'widget-choice-mouse-down-action
1485 :action 'widget-choice-action 1593 :action 'widget-choice-action
1486 :error "Make a choice" 1594 :error "Make a choice"
1487 :validate 'widget-choice-validate 1595 :validate 'widget-choice-validate
@@ -1516,6 +1624,39 @@ With optional ARG, move across that many fields."
1516 ;; Get value of the child widget. 1624 ;; Get value of the child widget.
1517 (widget-apply (car (widget-get widget :children)) :value-inline)) 1625 (widget-apply (car (widget-get widget :children)) :value-inline))
1518 1626
1627(defcustom widget-choice-toggle nil
1628 "If non-nil, a binary choice will just toggle between the values.
1629Otherwise, the user will explicitly have to choose between the values
1630when he activate the menu."
1631 :type 'boolean
1632 :group 'widgets)
1633
1634(defun widget-choice-mouse-down-action (widget &optional event)
1635 ;; Return non-nil if we need a menu.
1636 (let ((args (widget-get widget :args))
1637 (old (widget-get widget :choice)))
1638 (cond ((not window-system)
1639 ;; No place to pop up a menu.
1640 nil)
1641 ((not (or (fboundp 'x-popup-menu) (fboundp 'popup-menu)))
1642 ;; No way to pop up a menu.
1643 nil)
1644 ((< (length args) 2)
1645 ;; Empty or singleton list, just return the value.
1646 nil)
1647 ((> (length args) widget-menu-max-size)
1648 ;; Too long, prompt.
1649 nil)
1650 ((> (length args) 2)
1651 ;; Reasonable sized list, use menu.
1652 t)
1653 ((and widget-choice-toggle (memq old args))
1654 ;; We toggle.
1655 nil)
1656 (t
1657 ;; Ask which of the two.
1658 t))))
1659
1519(defun widget-choice-action (widget &optional event) 1660(defun widget-choice-action (widget &optional event)
1520 ;; Make a choice. 1661 ;; Make a choice.
1521 (let ((args (widget-get widget :args)) 1662 (let ((args (widget-get widget :args))
@@ -1534,7 +1675,8 @@ With optional ARG, move across that many fields."
1534 nil) 1675 nil)
1535 ((= (length args) 1) 1676 ((= (length args) 1)
1536 (nth 0 args)) 1677 (nth 0 args))
1537 ((and (= (length args) 2) 1678 ((and widget-choice-toggle
1679 (= (length args) 2)
1538 (memq old args)) 1680 (memq old args))
1539 (if (eq old (nth 0 args)) 1681 (if (eq old (nth 0 args))
1540 (nth 1 args) 1682 (nth 1 args)
@@ -1789,13 +1931,9 @@ With optional ARG, move across that many fields."
1789 1931
1790(define-widget 'choice-item 'item 1932(define-widget 'choice-item 'item
1791 "Button items that delegate action events to their parents." 1933 "Button items that delegate action events to their parents."
1792 :action 'widget-choice-item-action 1934 :action 'widget-parent-action
1793 :format "%[%t%] \n") 1935 :format "%[%t%] \n")
1794 1936
1795(defun widget-choice-item-action (widget &optional event)
1796 ;; Tell parent what happened.
1797 (widget-apply (widget-get widget :parent) :action event))
1798
1799;;; The `radio-button' Widget. 1937;;; The `radio-button' Widget.
1800 1938
1801(define-widget 'radio-button 'toggle 1939(define-widget 'radio-button 'toggle
@@ -2017,7 +2155,7 @@ With optional ARG, move across that many fields."
2017 :value-create 'widget-editable-list-value-create 2155 :value-create 'widget-editable-list-value-create
2018 :value-delete 'widget-children-value-delete 2156 :value-delete 'widget-children-value-delete
2019 :value-get 'widget-editable-list-value-get 2157 :value-get 'widget-editable-list-value-get
2020 :validate 'widget-editable-list-validate 2158 :validate 'widget-children-validate
2021 :match 'widget-editable-list-match 2159 :match 'widget-editable-list-match
2022 :match-inline 'widget-editable-list-match-inline 2160 :match-inline 'widget-editable-list-match-inline
2023 :insert-before 'widget-editable-list-insert-before 2161 :insert-before 'widget-editable-list-insert-before
@@ -2062,16 +2200,6 @@ With optional ARG, move across that many fields."
2062 (apply 'append (mapcar (lambda (child) (widget-apply child :value-inline)) 2200 (apply 'append (mapcar (lambda (child) (widget-apply child :value-inline))
2063 (widget-get widget :children)))) 2201 (widget-get widget :children))))
2064 2202
2065(defun widget-editable-list-validate (widget)
2066 ;; All the chilren must be valid.
2067 (let ((children (widget-get widget :children))
2068 child found)
2069 (while (and children (not found))
2070 (setq child (car children)
2071 children (cdr children)
2072 found (widget-apply child :validate)))
2073 found))
2074
2075(defun widget-editable-list-match (widget value) 2203(defun widget-editable-list-match (widget value)
2076 ;; Value must be a list and all the members must match the type. 2204 ;; Value must be a list and all the members must match the type.
2077 (and (listp value) 2205 (and (listp value)
@@ -2195,7 +2323,7 @@ With optional ARG, move across that many fields."
2195 :value-create 'widget-group-value-create 2323 :value-create 'widget-group-value-create
2196 :value-delete 'widget-children-value-delete 2324 :value-delete 'widget-children-value-delete
2197 :value-get 'widget-editable-list-value-get 2325 :value-get 'widget-editable-list-value-get
2198 :validate 'widget-editable-list-validate 2326 :validate 'widget-children-validate
2199 :match 'widget-group-match 2327 :match 'widget-group-match
2200 :match-inline 'widget-group-match-inline) 2328 :match-inline 'widget-group-match-inline)
2201 2329
@@ -2284,19 +2412,14 @@ With optional ARG, move across that many fields."
2284 :format "%v\n%h" 2412 :format "%v\n%h"
2285 :documentation-property 'variable-documentation) 2413 :documentation-property 'variable-documentation)
2286 2414
2287(define-widget 'string 'editable-field
2288 "A string"
2289 :prompt-value 'widget-string-prompt-value
2290 :tag "String"
2291 :format "%[%t%]: %v")
2292
2293(defvar widget-string-prompt-value-history nil 2415(defvar widget-string-prompt-value-history nil
2294 "History of input to `widget-string-prompt-value'.") 2416 "History of input to `widget-string-prompt-value'.")
2295 2417
2296(defun widget-string-prompt-value (widget prompt value unbound) 2418(define-widget 'string 'editable-field
2297 ;; Read a string. 2419 "A string"
2298 (read-string prompt (if unbound nil (cons value 1)) 2420 :tag "String"
2299 'widget-string-prompt-value-history)) 2421 :format "%{%t%}: %v"
2422 :prompt-history 'widget-string-prompt-value-history)
2300 2423
2301(define-widget 'regexp 'string 2424(define-widget 'regexp 'string
2302 "A regular expression." 2425 "A regular expression."
@@ -2307,7 +2430,7 @@ With optional ARG, move across that many fields."
2307(defun widget-regexp-match (widget value) 2430(defun widget-regexp-match (widget value)
2308 ;; Match valid regexps. 2431 ;; Match valid regexps.
2309 (and (stringp value) 2432 (and (stringp value)
2310 (condition-case data 2433 (condition-case nil
2311 (prog1 t 2434 (prog1 t
2312 (string-match value "")) 2435 (string-match value ""))
2313 (error nil)))) 2436 (error nil))))
@@ -2325,7 +2448,7 @@ With optional ARG, move across that many fields."
2325 "A file widget. 2448 "A file widget.
2326It will read a file name from the minibuffer when activated." 2449It will read a file name from the minibuffer when activated."
2327 :prompt-value 'widget-file-prompt-value 2450 :prompt-value 'widget-file-prompt-value
2328 :format "%[%t%]: %v" 2451 :format "%{%t%}: %v"
2329 :tag "File" 2452 :tag "File"
2330 :action 'widget-file-action) 2453 :action 'widget-file-action)
2331 2454
@@ -2334,7 +2457,7 @@ It will read a file name from the minibuffer when activated."
2334 (abbreviate-file-name 2457 (abbreviate-file-name
2335 (if unbound 2458 (if unbound
2336 (read-file-name prompt) 2459 (read-file-name prompt)
2337 (let ((prompt2 (concat prompt "(default `" value "') ")) 2460 (let ((prompt2 (format "%s (default %s) " prompt value))
2338 (dir (file-name-directory value)) 2461 (dir (file-name-directory value))
2339 (file (file-name-nondirectory value)) 2462 (file (file-name-nondirectory value))
2340 (must-match (widget-get widget :must-match))) 2463 (must-match (widget-get widget :must-match)))
@@ -2358,11 +2481,18 @@ It will read a file name from the minibuffer when activated."
2358It will read a directory name from the minibuffer when activated." 2481It will read a directory name from the minibuffer when activated."
2359 :tag "Directory") 2482 :tag "Directory")
2360 2483
2361(define-widget 'symbol 'string 2484(defvar widget-symbol-prompt-value-history nil
2485 "History of input to `widget-symbol-prompt-value'.")
2486
2487(define-widget 'symbol 'editable-field
2362 "A lisp symbol." 2488 "A lisp symbol."
2363 :value nil 2489 :value nil
2364 :tag "Symbol" 2490 :tag "Symbol"
2491 :format "%{%t%}: %v"
2365 :match (lambda (widget value) (symbolp value)) 2492 :match (lambda (widget value) (symbolp value))
2493 :prompt-internal 'widget-symbol-prompt-internal
2494 :prompt-match 'symbolp
2495 :prompt-history 'widget-symbol-prompt-value-history
2366 :value-to-internal (lambda (widget value) 2496 :value-to-internal (lambda (widget value)
2367 (if (symbolp value) 2497 (if (symbolp value)
2368 (symbol-name value) 2498 (symbol-name value)
@@ -2372,24 +2502,48 @@ It will read a directory name from the minibuffer when activated."
2372 (intern value) 2502 (intern value)
2373 value))) 2503 value)))
2374 2504
2505(defun widget-symbol-prompt-internal (widget prompt initial history)
2506 ;; Read file from minibuffer.
2507 (let ((answer (completing-read prompt obarray
2508 (widget-get widget :prompt-match)
2509 nil initial history)))
2510 (if (and (stringp answer)
2511 (not (zerop (length answer))))
2512 answer
2513 (error "No value"))))
2514
2515(defvar widget-function-prompt-value-history nil
2516 "History of input to `widget-function-prompt-value'.")
2517
2375(define-widget 'function 'sexp 2518(define-widget 'function 'sexp
2376 ;; Should complete on functions.
2377 "A lisp function." 2519 "A lisp function."
2520 :prompt-value 'widget-field-prompt-value
2521 :prompt-internal 'widget-symbol-prompt-internal
2522 :prompt-match 'fboundp
2523 :prompt-history 'widget-function-prompt-value-history
2524 :action 'widget-field-action
2378 :tag "Function") 2525 :tag "Function")
2379 2526
2527(defvar widget-variable-prompt-value-history nil
2528 "History of input to `widget-variable-prompt-value'.")
2529
2380(define-widget 'variable 'symbol 2530(define-widget 'variable 'symbol
2381 ;; Should complete on variables. 2531 ;; Should complete on variables.
2382 "A lisp variable." 2532 "A lisp variable."
2533 :prompt-match 'boundp
2534 :prompt-history 'widget-variable-prompt-value-history
2383 :tag "Variable") 2535 :tag "Variable")
2384 2536
2385(define-widget 'sexp 'string 2537(define-widget 'sexp 'editable-field
2386 "An arbitrary lisp expression." 2538 "An arbitrary lisp expression."
2387 :tag "Lisp expression" 2539 :tag "Lisp expression"
2540 :format "%{%t%}: %v"
2388 :value nil 2541 :value nil
2389 :validate 'widget-sexp-validate 2542 :validate 'widget-sexp-validate
2390 :match (lambda (widget value) t) 2543 :match (lambda (widget value) t)
2391 :value-to-internal 'widget-sexp-value-to-internal 2544 :value-to-internal 'widget-sexp-value-to-internal
2392 :value-to-external (lambda (widget value) (read value)) 2545 :value-to-external (lambda (widget value) (read value))
2546 :prompt-history 'widget-sexp-prompt-value-history
2393 :prompt-value 'widget-sexp-prompt-value) 2547 :prompt-value 'widget-sexp-prompt-value)
2394 2548
2395(defun widget-sexp-value-to-internal (widget value) 2549(defun widget-sexp-value-to-internal (widget value)
@@ -2430,18 +2584,19 @@ It will read a directory name from the minibuffer when activated."
2430(defun widget-sexp-prompt-value (widget prompt value unbound) 2584(defun widget-sexp-prompt-value (widget prompt value unbound)
2431 ;; Read an arbitrary sexp. 2585 ;; Read an arbitrary sexp.
2432 (let ((found (read-string prompt 2586 (let ((found (read-string prompt
2433 (if unbound nil (cons (prin1-to-string value) 1)) 2587 (if unbound nil (cons (prin1-to-string value) 0))
2434 'widget-sexp-prompt-value))) 2588 (widget-get widget :prompt-history))))
2435 (let ((buffer (set-buffer (get-buffer-create " *Widget Scratch*")))) 2589 (save-excursion
2436 (erase-buffer) 2590 (let ((buffer (set-buffer (get-buffer-create " *Widget Scratch*"))))
2437 (insert found) 2591 (erase-buffer)
2438 (goto-char (point-min)) 2592 (insert found)
2439 (let ((answer (read buffer))) 2593 (goto-char (point-min))
2440 (unless (eobp) 2594 (let ((answer (read buffer)))
2441 (error "Junk at end of expression: %s" 2595 (unless (eobp)
2442 (buffer-substring (point) (point-max)))) 2596 (error "Junk at end of expression: %s"
2443 answer)))) 2597 (buffer-substring (point) (point-max))))
2444 2598 answer)))))
2599
2445(define-widget 'integer 'sexp 2600(define-widget 'integer 'sexp
2446 "An integer." 2601 "An integer."
2447 :tag "Integer" 2602 :tag "Integer"
@@ -2453,7 +2608,7 @@ It will read a directory name from the minibuffer when activated."
2453 value)) 2608 value))
2454 :match (lambda (widget value) (integerp value))) 2609 :match (lambda (widget value) (integerp value)))
2455 2610
2456(define-widget 'character 'string 2611(define-widget 'character 'editable-field
2457 "An character." 2612 "An character."
2458 :tag "Character" 2613 :tag "Character"
2459 :value 0 2614 :value 0
@@ -2462,14 +2617,17 @@ It will read a directory name from the minibuffer when activated."
2462 :valid-regexp "\\`.\\'" 2617 :valid-regexp "\\`.\\'"
2463 :error "This field should contain a single character" 2618 :error "This field should contain a single character"
2464 :value-to-internal (lambda (widget value) 2619 :value-to-internal (lambda (widget value)
2465 (if (integerp value) 2620 (if (stringp value)
2466 (char-to-string value) 2621 value
2467 value)) 2622 (char-to-string value)))
2468 :value-to-external (lambda (widget value) 2623 :value-to-external (lambda (widget value)
2469 (if (stringp value) 2624 (if (stringp value)
2470 (aref value 0) 2625 (aref value 0)
2471 value)) 2626 value))
2472 :match (lambda (widget value) (integerp value))) 2627 :match (lambda (widget value)
2628 (if (fboundp 'characterp)
2629 (characterp value)
2630 (integerp value))))
2473 2631
2474(define-widget 'number 'sexp 2632(define-widget 'number 'sexp
2475 "A floating point number." 2633 "A floating point number."
@@ -2518,12 +2676,56 @@ It will read a directory name from the minibuffer when activated."
2518(define-widget 'choice 'menu-choice 2676(define-widget 'choice 'menu-choice
2519 "A union of several sexp types." 2677 "A union of several sexp types."
2520 :tag "Choice" 2678 :tag "Choice"
2521 :format "%[%t%]: %v") 2679 :format "%[%t%]: %v"
2680 :prompt-value 'widget-choice-prompt-value)
2681
2682(defun widget-choice-prompt-value (widget prompt value unbound)
2683 "Make a choice."
2684 (let ((args (widget-get widget :args))
2685 (completion-ignore-case (widget-get widget :case-fold))
2686 current choices old)
2687 ;; Find the first arg that match VALUE.
2688 (let ((look args))
2689 (while look
2690 (if (widget-apply (car look) :match value)
2691 (setq old (car look)
2692 look nil)
2693 (setq look (cdr look)))))
2694 ;; Find new choice.
2695 (setq current
2696 (cond ((= (length args) 0)
2697 nil)
2698 ((= (length args) 1)
2699 (nth 0 args))
2700 ((and (= (length args) 2)
2701 (memq old args))
2702 (if (eq old (nth 0 args))
2703 (nth 1 args)
2704 (nth 0 args)))
2705 (t
2706 (while args
2707 (setq current (car args)
2708 args (cdr args))
2709 (setq choices
2710 (cons (cons (widget-apply current :menu-tag-get)
2711 current)
2712 choices)))
2713 (let ((val (completing-read prompt choices nil t)))
2714 (if (stringp val)
2715 (let ((try (try-completion val choices)))
2716 (when (stringp try)
2717 (setq val try))
2718 (cdr (assoc val choices)))
2719 nil)))))
2720 (if current
2721 (widget-prompt-value current prompt nil t)
2722 value)))
2522 2723
2523(define-widget 'radio 'radio-button-choice 2724(define-widget 'radio 'radio-button-choice
2524 "A union of several sexp types." 2725 "A union of several sexp types."
2525 :tag "Choice" 2726 :tag "Choice"
2526 :format "%{%t%}:\n%v") 2727 :format "%{%t%}:\n%v"
2728 :prompt-value 'widget-choice-prompt-value)
2527 2729
2528(define-widget 'repeat 'editable-list 2730(define-widget 'repeat 'editable-list
2529 "A variable length homogeneous list." 2731 "A variable length homogeneous list."
@@ -2539,18 +2741,11 @@ It will read a directory name from the minibuffer when activated."
2539 "To be nil or non-nil, that is the question." 2741 "To be nil or non-nil, that is the question."
2540 :tag "Boolean" 2742 :tag "Boolean"
2541 :prompt-value 'widget-boolean-prompt-value 2743 :prompt-value 'widget-boolean-prompt-value
2542 :format "%{%t%}: %[%v%]\n") 2744 :format "%[%t%]: %v\n")
2543 2745
2544(defun widget-boolean-prompt-value (widget prompt value unbound) 2746(defun widget-boolean-prompt-value (widget prompt value unbound)
2545 ;; Toggle a boolean. 2747 ;; Toggle a boolean.
2546 (cond (unbound 2748 (y-or-n-p prompt))
2547 (y-or-n-p prompt))
2548 (value
2549 (message "Off")
2550 nil)
2551 (t
2552 (message "On")
2553 t)))
2554 2749
2555;;; The `color' Widget. 2750;;; The `color' Widget.
2556 2751
diff --git a/lisp/widget.el b/lisp/widget.el
index 4905c06b70a..f65b6603615 100644
--- a/lisp/widget.el
+++ b/lisp/widget.el
@@ -4,7 +4,7 @@
4;; 4;;
5;; Author: Per Abrahamsen <abraham@dina.kvl.dk> 5;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6;; Keywords: help, extensions, faces, hypermedia 6;; Keywords: help, extensions, faces, hypermedia
7;; Version: 1.90 7;; Version: 1.97
8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ 8;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
9 9
10;; This file is part of GNU Emacs. 10;; This file is part of GNU Emacs.
@@ -44,7 +44,10 @@
44 (set (car keywords) (car keywords))) 44 (set (car keywords) (car keywords)))
45 (setq keywords (cdr keywords))))))) 45 (setq keywords (cdr keywords)))))))
46 46
47(define-widget-keywords :prompt-value :text-format :deactivate :active 47(define-widget-keywords :mouse-down-action :glyph-up :glyph-down
48 :glyph-inactive
49 :prompt-internal :prompt-history :prompt-match
50 :prompt-value :text-format :deactivate :active
48 :inactive :activate :sibling-args :delete-button-args 51 :inactive :activate :sibling-args :delete-button-args
49 :insert-button-args :append-button-args :button-args 52 :insert-button-args :append-button-args :button-args
50 :tag-glyph :off-glyph :on-glyph :valid-regexp 53 :tag-glyph :off-glyph :on-glyph :valid-regexp