Mercurial > hg > xemacs-beta
diff lisp/wid-edit.el @ 4178:e687f1912d5b
[xemacs-hg @ 2007-09-20 21:18:33 by didierv]
User options interactive prompting improvements
author | didierv |
---|---|
date | Thu, 20 Sep 2007 21:18:35 +0000 |
parents | 681d0fbb904e |
children | f00192e1cd49 308d34e9f07d |
line wrap: on
line diff
--- a/lisp/wid-edit.el Wed Sep 19 21:50:57 2007 +0000 +++ b/lisp/wid-edit.el Thu Sep 20 21:18:35 2007 +0000 @@ -1,9 +1,10 @@ ;;; wid-edit.el --- Functions for creating and using widgets. ;; +;; Copyright (C) 2007 Didier Verna ;; Copyright (C) 1996-1997, 1999-2002 Free Software Foundation, Inc. ;; ;; Author: Per Abrahamsen <abraham@dina.kvl.dk> -;; Maintainer: Hrvoje Niksic <hniksic@xemacs.org> +;; Maintainer: Didier Verna <didier@xemacs.org> ;; Keywords: extensions ;; Version: 1.9960-x ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ @@ -330,7 +331,7 @@ (mouse-set-point event) (let ((pos (event-point event))) (if (and pos (get-char-property pos 'button)) - (widget-button-click event)))) + (widget-button-click event)))) ;;; Widget text specifications. ;; @@ -591,15 +592,15 @@ (defun widget-specify-active (widget) "Make WIDGET active for user modifications." (let ((inactive (widget-get widget :inactive)) - (from (widget-get widget :from)) - (to (widget-get widget :to))) + (from (widget-get widget :from)) + (to (widget-get widget :to))) (when (and inactive (not (extent-detached-p inactive))) ;; Reactivate the buttons and fields covered by the extent. (map-extents 'widget-activation-widget-mapper - nil from to :activate nil 'button-or-field) + nil from to :activate nil 'button-or-field) ;; Reactivate the glyphs. (map-extents 'widget-activation-glyph-mapper - nil from to :activate nil 'end-glyph) + nil from to :activate nil 'end-glyph) (delete-extent inactive) (widget-put widget :inactive nil)))) @@ -706,15 +707,44 @@ ;; ;; These are widget specific. +;; #### Note: this should probably be a more general utility -- dvl +(defsubst widget-prompt-spaceify (prompt) + ;; Add a space at the end of PROMPT if needed + (if (or (string= prompt "") (eq ? (aref prompt (1- (length prompt))))) + prompt + (concat prompt " "))) + +(defsubst widget-prompt (widget &optional prompt default-prompt) + ;; Construct a prompt for WIDGET. + ;; - If PROMPT is given, use it. + ;; - Otherwise, use the :tag property, if any. + ;; - Otherwise, use DEFAULT-PROMPT, if given. + ;; - Otherise, use "Value". + ;; - If the result is not the empty string, add a space for later addition + ;; of the widget type by `widget-prompt-value'. + (unless prompt + (setq prompt (or (and (widget-get widget :tag) + (replace-in-string (widget-get widget :tag) + "^[ \t]+" "" t)) + default-prompt + "Value"))) + (widget-prompt-spaceify prompt)) + + ;;;###autoload -(defun widget-prompt-value (widget prompt &optional value unbound) - "Prompt for a value matching WIDGET, using PROMPT. +(defun widget-prompt-value (widget &optional prompt value unbound) + "Prompt for a value matching WIDGET. +Prompt with PROMPT, or WIDGET's :tag otherwise. The current value is assumed to be VALUE, unless UNBOUND is non-nil." (unless (listp widget) (setq widget (list widget))) - (setq prompt (format "[%s] %s" (widget-type widget) prompt)) (setq widget (widget-convert widget)) - (let ((answer (widget-apply widget :prompt-value prompt value unbound))) + (let ((answer (widget-apply widget + :prompt-value + (format "%s[%s]" + (widget-prompt widget prompt) + (widget-type widget)) + value unbound))) (while (not (widget-apply widget :match answer)) (setq answer (signal 'error (list "Answer does not match type" answer (widget-type widget))))) @@ -1783,11 +1813,11 @@ (lambda () ;?\] (setq button-end (point-marker)) (set-marker-insertion-type button-end nil)) - (lambda () ;?\{ + (lambda () ;?\{ (setq sample-begin (point))) (lambda () ;?\} (setq sample-end (point))) - (lambda () ;?n + (lambda () ;?n (when (widget-get widget :indent) (insert ?\n) (insert-char ?\ (widget-get widget :indent)))) @@ -2001,7 +2031,7 @@ ;; It would be nice if we could do a `(cons val 1)' here. ;; (prin1-to-string (custom-quote value)))))) ;; XEmacs: make this use default VALUE. Need to check callers. - (eval-minibuffer prompt)) + (eval-minibuffer (concat prompt ": "))) ;;; The `item' Widget. @@ -2224,7 +2254,7 @@ "Read string for WIDGET prompting with PROMPT. INITIAL is the initial input and HISTORY is a symbol containing the earlier input." - (read-string prompt initial history)) + (read-string (concat prompt ": ") initial history)) (defun widget-field-prompt-value (widget prompt value unbound) "Prompt for a string." @@ -2577,6 +2607,7 @@ :value-create 'widget-checklist-value-create :value-delete 'widget-children-value-delete :value-get 'widget-checklist-value-get + :prompt-value 'widget-checklist-prompt-value :validate 'widget-checklist-validate :match 'widget-checklist-match :match-inline 'widget-checklist-match-inline) @@ -2701,6 +2732,27 @@ (setq result (append result (widget-apply child :value-inline))))) result)) +;; #### FIXME: should handle default value some day -- dvl +(defun widget-checklist-prompt-value (widget prompt value unbound) + ;; Prompt for items to be selected, and the prompt for their value + (let* ((args (widget-get widget :args)) + (choices (mapcar (lambda (elt) + (cons (widget-get elt :tag) elt)) + args)) + (continue t) + value) + (while continue + (setq continue (completing-read + (concat (widget-prompt-spaceify prompt) + "select [ret. when done]: ") + choices nil t)) + (if (string= continue "") + (setq continue nil) + (push (widget-prompt-value (cdr (assoc continue choices)) + prompt nil t) + value))) + (nreverse value))) + (defun widget-checklist-validate (widget) ;; Ticked children must be valid. (let ((children (widget-get widget :children)) @@ -3116,6 +3168,7 @@ :value-delete 'widget-children-value-delete :value-get 'widget-editable-list-value-get :default-get 'widget-group-default-get + :prompt-value 'widget-group-prompt-value :validate 'widget-children-validate :match 'widget-group-match :match-inline 'widget-group-match-inline) @@ -3146,6 +3199,36 @@ ;; Get the default of the components. (mapcar 'widget-default-get (widget-get widget :args))) +(defun widget-group-prompt-value (widget prompt value unbound) + ;; Prompt in turn for every component of the group. + (let ((args (widget-get widget :args))) + (widget-apply + widget :value-to-external + (if unbound + (mapcar #'(lambda (arg) + (widget-prompt-value + arg + (concat (widget-prompt-spaceify prompt) + (widget-prompt arg nil "")) + nil t)) + args) + ;; If VALUE is bound, the situation is a bit more complex because we + ;; have to split it into a list of default values for every child. Oh, + ;; boy, do I miss 'cl here... -- dvl + (let ((children args) + (defaults (widget-apply widget + :value-to-internal value)) + child default result) + (while (setq child (pop children)) + (setq default (pop defaults)) + (push + (widget-prompt-value + child + (concat (widget-prompt-spaceify prompt) + (widget-prompt child nil "")) + default) result)) + (nreverse result)))))) + (defun widget-group-match (widget values) ;; Match if the components match. (and (listp values) @@ -3378,7 +3461,7 @@ (defun widget-sexp-prompt-value (widget prompt value unbound) ;; Read an arbitrary sexp. - (let ((found (read-string prompt + (let ((found (read-string (concat prompt ": ") (if unbound nil (cons (prin1-to-string value) 0)) (widget-get widget :prompt-history)))) (save-excursion @@ -3502,8 +3585,8 @@ ;; Read file from minibuffer. (abbreviate-file-name (if unbound - (read-file-name prompt) - (let ((prompt2 (format "%s (default %s) " prompt value)) + (read-file-name (concat prompt ": ")) + (let ((prompt2 (format "%s: (default %s) " prompt value)) (dir (file-name-directory value)) (file (file-name-nondirectory value)) (must-match (widget-get widget :must-match))) @@ -3552,7 +3635,7 @@ (defun widget-symbol-prompt-internal (widget prompt initial history) ;; Read file from minibuffer. - (let ((answer (completing-read prompt obarray + (let ((answer (completing-read (concat prompt ": ") obarray (widget-get widget :prompt-match) nil initial history))) (if (and (stringp answer) @@ -3824,42 +3907,45 @@ (let ((args (widget-get widget :args)) (completion-ignore-case (widget-get widget :case-fold)) current choices old) - ;; Find the first arg that matches VALUE. - (let ((look args)) - (while look - (if (widget-apply (car look) :match value) - (setq old (car look) - look nil) - (setq look (cdr look))))) - ;; Find new choice. + ;; Find the first choice matching VALUE (if given): + (unless unbound + (let ((look args)) + (while look + (if (widget-apply (car look) :match value) + (setq old (car look) + look nil) + (setq look (cdr look))))) + ;; If VALUE is invalid (it doesn't match any choice), discard it by + ;; considering it unbound: + (unless old + (setq unbound t))) + ;; Now offer the choice, providing the given default value when/where + ;; appropriate: + (while args + (setq current (car args) + args (cdr args)) + (setq choices + (cons (cons (widget-apply current :menu-tag-get) + current) + choices))) (setq current - (cond ((= (length args) 0) - nil) - ((= (length args) 1) - (nth 0 args)) - ((and (= (length args) 2) - (memq old args)) - (if (eq old (nth 0 args)) - (nth 1 args) - (nth 0 args))) - (t - (while args - (setq current (car args) - args (cdr args)) - (setq choices - (cons (cons (widget-apply current :menu-tag-get) - current) - choices))) - (let ((val (completing-read prompt choices nil t))) - (if (stringp val) - (let ((try (try-completion val choices))) - (when (stringp try) - (setq val try)) - (cdr (assoc val choices))) - nil))))) + (let ((val (completing-read (concat prompt ": ") choices nil t + (when old + (widget-apply old :menu-tag-get))))) + (if (stringp val) ;; #### is this really needed ? --dvl + (let ((try (try-completion val choices))) + (when (stringp try) ;; #### and this ? --dvl + (setq val try)) + (cdr (assoc val choices))) + nil))) (if current - (widget-prompt-value current prompt nil t) - value))) + (widget-prompt-value current + (concat (widget-prompt-spaceify prompt) + (widget-get current :tag)) + (unless unbound + (when (eq current old) value)) + (or unbound (not (eq current old)))) + (and (not unbound) value)))) (define-widget 'radio 'radio-button-choice "A set widget, selecting exactly one from many. @@ -3891,7 +3977,7 @@ (defun widget-boolean-prompt-value (widget prompt value unbound) ;; Toggle a boolean. - (y-or-n-p prompt)) + (y-or-n-p (concat prompt ": "))) ;;; The `color' Widget. @@ -4002,8 +4088,8 @@ nil, or a cons-cell containing a sexp and my-lisp. This will not work because the `choice' widget does not allow recursion. -Using the `lazy' widget you can overcome this problem, as in this -example: +Using the `lazy' widget you can overcome this problem, as in this +example: (define-widget 'sexp-list 'lazy \"A list of sexps.\" @@ -4012,7 +4098,7 @@ :format "%{%t%}: %v" ;; We don't convert :type because we want to allow recursive ;; datastructures. This is slow, so we should not create speed - ;; critical widgets by deriving from this. + ;; critical widgets by deriving from this. :convert-widget 'widget-value-convert-widget :value-create 'widget-type-value-create :value-delete 'widget-children-value-delete @@ -4041,10 +4127,10 @@ The value of the :type attribute should be an unconverted widget type." (let ((value (widget-get widget :value)) (type (widget-get widget :type))) - (widget-put widget :children - (list (widget-create-child-value widget - (widget-convert type) - value))))) + (widget-put widget :children + (list (widget-create-child-value widget + (widget-convert type) + value))))) (defun widget-type-default-get (widget) "Get default value from the :type attribute of WIDGET.