;;;-*- Mode:LISP; Package:FORMAT; Readtable:ZL; Base:8 -*- ;;; ** (c) Copyright 1980 Massachusetts Institute of Technology ** (DEFPARAMETER Y-OR-N-P-CHOICES '(((T "Yes.") #/Y #/T #/SP #/HAND-UP) ((NIL "No.") #/N #/RUBOUT #/HAND-DOWN))) (DEFPARAMETER YES-OR-NO-P-CHOICES '((T "Yes") (NIL "No"))) (DEFVAR FQUERY-FORMAT-STRING) (DEFVAR FQUERY-FORMAT-ARGS) (DEFVAR FQUERY-LIST-CHOICES) (DEFVAR FQUERY-CHOICES) (DEFVAR FQUERY-HELP-FUNCTION) (DEFVAR FQUERY-STREAM) (DEFUN FQUERY (OPTIONS FQUERY-FORMAT-STRING &REST FQUERY-FORMAT-ARGS) "Ask a multiple-choice question on *QUERY-IO*. FQUERY-FORMAT-STRING and FQUERY-FORMAT-ARGS are used to print the question. Ending the string with /"? /" is often appropriate. OPTIONS is an &key-style list of keywords and values. Defined keys are: :MAKE-COMPLETE boolean. Send a :MAKE-COMPLETE message to the stream if it understands it. :TYPE one of :TYI, :READLINE, :MINI-BUFFER-OR-READLINE. It says how the answer is gathered and echoed. :CHOICES a list of choices. A choice is either the symbol :ANY or a list. If a list, its car is either a possible return value, or a list of a possible return value and how to echo it. The remaining things in the list are input items that select that return value. For a :READLINE type call, they should be strings. For a :TYI type call, they should be characters. Example choice (for :READLINE): ((:foo /"Foo/") #\F #\space) :FRESH-LINE boolean. Send a :FRESH-LINE to the stream initially. :CONDITION symbol. Signalled before asking. :LIST-CHOICES boolean. If T, a list of choices is printed after the question. :BEEP boolean. If T, we beep before printing the message. :CLEAR-INPUT boolean. If T, we discard type-ahead before printing the message. :HELP-FUNCTION specifies a function to be called if the user types Help. It is called with STREAM, CHOICES and TYPE-FUNCTION as arguments. :STREAM specifies the stream to use. Default is to use *QUERY-IO*." (MULTIPLE-VALUE-BIND (MAKE-COMPLETE TYPE FQUERY-CHOICES STREAM BEEP-P CLEAR-INPUT FRESH-LINE CONDITION FQUERY-LIST-CHOICES FQUERY-HELP-FUNCTION) (APPLY (LAMBDA (&KEY (MAKE-COMPLETE T) (TYPE :TYI) (CHOICES Y-OR-N-P-CHOICES) STREAM BEEP CLEAR-INPUT (FRESH-LINE T) (CONDITION 'FQUERY) (LIST-CHOICES T) SELECT SIGNAL-CONDITION ;not used (HELP-FUNCTION 'DEFAULT-FQUERY-HELP)) (DECLARE (IGNORE SIGNAL-CONDITION SELECT)) (VALUES MAKE-COMPLETE TYPE CHOICES STREAM BEEP CLEAR-INPUT FRESH-LINE CONDITION LIST-CHOICES HELP-FUNCTION)) OPTIONS) (LET ((FQUERY-STREAM (OR STREAM *QUERY-IO*)) (TYPE-FUNCTION (OR (GET TYPE 'FQUERY-FUNCTION) (FERROR "~S is not a valid ~S for ~S" TYPE :TYPE 'FQUERY)))) (AND CONDITION (OR (NEQ CONDITION 'FQUERY) (EH:CONDITION-NAME-HANDLED-P CONDITION)) (MULTIPLE-VALUE-BIND (HANDLEDP VAL) (SIGNAL-CONDITION (APPLY #'MAKE-CONDITION CONDITION OPTIONS FQUERY-FORMAT-STRING FQUERY-FORMAT-ARGS) '(:NEW-VALUE)) (IF HANDLEDP (RETURN-FROM FQUERY VAL)))) (FLET ((THUNK () (LOOP (AND BEEP-P (SEND FQUERY-STREAM :BEEP 'FQUERY)) (AND CLEAR-INPUT (SEND FQUERY-STREAM :CLEAR-INPUT)) (AND FRESH-LINE (SEND FQUERY-STREAM :FRESH-LINE)) (LET ((TYPEIN (FUNCALL TYPE-FUNCTION :READ FQUERY-STREAM))) (DOLIST (CHOICE FQUERY-CHOICES) (COND ((EQ CHOICE ':ANY) (FUNCALL TYPE-FUNCTION :ECHO TYPEIN FQUERY-STREAM) (AND MAKE-COMPLETE (SEND-IF-HANDLES FQUERY-STREAM :MAKE-COMPLETE)) (RETURN-FROM FQUERY TYPEIN)) ((FUNCALL TYPE-FUNCTION :MEMBER TYPEIN (CDR CHOICE)) (SETQ CHOICE (CAR CHOICE)) (WHEN (CONSP CHOICE) (FUNCALL TYPE-FUNCTION :ECHO (CADR CHOICE) FQUERY-STREAM) (SETQ CHOICE (CAR CHOICE))) (AND MAKE-COMPLETE (SEND-IF-HANDLES FQUERY-STREAM :MAKE-COMPLETE)) (RETURN-FROM FQUERY CHOICE))))) (SETQ BEEP-P T CLEAR-INPUT T FRESH-LINE T ;User spazzed, will need fresh line FQUERY-LIST-CHOICES T)))) (IF (AND (TYPEP (SI::FOLLOW-SYN-STREAM FQUERY-STREAM) 'TV:SHEET) (OPERATION-HANDLED-P TV:SELECTED-WINDOW :SET-SELECTION-SUBSTITUTE) (OPERATION-HANDLED-P FQUERY-STREAM :SELECT)) (TV:WITH-SELECTION-SUBSTITUTE (FQUERY-STREAM TV:SELECTED-WINDOW) (SEND FQUERY-STREAM :OUTPUT-HOLD-EXCEPTION) (THUNK)) (THUNK)))))) (DEFUN FQUERY-PROMPT (STREAM &REST IGNORE) (AND FQUERY-FORMAT-STRING (APPLY #'FORMAT STREAM FQUERY-FORMAT-STRING FQUERY-FORMAT-ARGS)) (AND FQUERY-LIST-CHOICES (DO ((CHOICES FQUERY-CHOICES (CDR CHOICES)) (FIRST-P T NIL) (MANY (> (LENGTH FQUERY-CHOICES) 2)) (CHOICE)) ((NULL CHOICES) (OR FIRST-P (SEND STREAM :STRING-OUT ") "))) (SEND STREAM :STRING-OUT (COND (FIRST-P "(") ((NOT (NULL (CDR CHOICES))) ", ") (MANY ", or ") (T " or "))) (IF (EQ (CAR CHOICES) :ANY) (SEND STREAM :STRING-OUT "anything else") (SETQ CHOICE (CADAR CHOICES)) ; character lossage (COND ((TYPEP CHOICE '(OR NUMBER CHARACTER)) (FORMAT STREAM "~:@C" CHOICE)) ((EQUAL CHOICE "") (PRINC "nothing" STREAM)) (T (SEND STREAM :STRING-OUT CHOICE))))))) (DEFUN DEFAULT-FQUERY-HELP (STREAM CHOICES TYPE) (DECLARE (IGNORE TYPE)) ;Not relevant (DO ((CHOICES CHOICES (CDR CHOICES)) (FIRST-P T NIL) (CHOICE)) ((NULL CHOICES) (OR FIRST-P (SEND STREAM :STRING-OUT ") "))) (SEND STREAM :STRING-OUT (COND (FIRST-P "(Type ") ((NOT (NULL (CDR CHOICES))) ", ") (T " or "))) (SETQ CHOICE (CAR CHOICES)) (COND ((EQ CHOICE :ANY) (PRINC "anything else" STREAM)) (T ;;Print the first input which selects this choice. ;;Don't confuse the user by mentioning possible alternative inputs. ; character lossage (COND ((TYPEP (CADR CHOICE) '(OR NUMBER CHARACTER)) (FORMAT STREAM "~:@C" (CADR CHOICE))) ((EQUAL (CADR CHOICE) "") (PRINC "nothing" STREAM)) (T (SEND STREAM :STRING-OUT (CADR CHOICE)))) ;; If that would echo as something else, say so (IF (CONSP (CAR CHOICE)) (FORMAT STREAM " (~A)" (CADAR CHOICE))))))) (DEFPROP :TYI TYI-FQUERY-FUNCTION FQUERY-FUNCTION) (DEFSELECT TYI-FQUERY-FUNCTION (:READ (STREAM) (DO ((CH)) (NIL) (FQUERY-PROMPT STREAM) (SETQ CH (READ-CHAR STREAM)) (UNLESS (AND (CHAR= CH #/HELP) FQUERY-HELP-FUNCTION) (RETURN CH)) (SEND FQUERY-HELP-FUNCTION STREAM FQUERY-CHOICES 'TYI-FQUERY-FUNCTION) (SEND STREAM :FRESH-LINE))) (:ECHO (ECHO STREAM) (SEND STREAM :STRING-OUT (STRING ECHO))) (:MEMBER (CHAR LIST) ; character lossage (MEM (LAMBDA (X Y) (CHAR-EQUAL X (COERCE Y 'CHARACTER))) CHAR LIST))) (DEFPROP :READLINE READLINE-FQUERY-FUNCTION FQUERY-FUNCTION) (DEFSELECT READLINE-FQUERY-FUNCTION (:READ (STREAM &AUX STRING) (SETQ STRING (SEND STREAM :RUBOUT-HANDLER '((:EDITING-COMMAND #/HELP) ;Just in case (:PROMPT FQUERY-PROMPT) (:DONT-SAVE T)) #'FQUERY-READLINE-WITH-HELP STREAM)) (STRING-TRIM '(#/SP) STRING)) (:ECHO (ECHO STREAM) ECHO STREAM) (:MEMBER (STRING LIST) (MEM #'STRING-EQUAL STRING LIST))) (DEFUN FQUERY-READLINE-WITH-HELP (STREAM) (DO ((STRING (MAKE-STRING 20. :FILL-POINTER 0)) (CH)) (NIL) (SETQ CH (READ-CHAR STREAM)) (COND ((OR (NULL CH) (CHAR= CH #/CR)) (RETURN STRING)) ((AND (CHAR= CH #/HELP) FQUERY-HELP-FUNCTION) (FRESH-LINE STREAM) (SEND FQUERY-HELP-FUNCTION STREAM FQUERY-CHOICES 'READLINE-FQUERY-FUNCTION) (SEND-IF-HANDLES STREAM :REFRESH-RUBOUT-HANDLER)) ((NOT (ZEROP (CHAR-BITS CH)))) (T (VECTOR-PUSH-EXTEND CH STRING))))) (PROCLAIM '(SPECIAL ZWEI::*MINI-BUFFER-ARG-DOCUMENTER*)) ;DEFVAR is in ZWEI. (DEFPROP :MINI-BUFFER-OR-READLINE MINI-BUFFER-OR-READLINE-FQUERY-FUNCTION FQUERY-FUNCTION) (DEFUN MINI-BUFFER-OR-READLINE-FQUERY-FUNCTION (&REST ARGS &AUX STRING) (COND ((AND (EQ (CAR ARGS) :READ) (EQ (CADR ARGS) 'ZWEI::*TYPEIN-WINDOW*-SYN-STREAM)) (LET ((ZWEI::*MINI-BUFFER-ARG-DOCUMENTER* 'MINI-BUFFER-OR-READLINE-HELP-FUNCTION)) (SEND-IF-HANDLES (CADR ARGS) :MAKE-COMPLETE) (SETQ STRING (APPLY #'ZWEI::TYPEIN-LINE-READLINE FQUERY-FORMAT-STRING FQUERY-FORMAT-ARGS))) (STRING-TRIM '(#/SP) STRING)) (T (APPLY #'READLINE-FQUERY-FUNCTION ARGS)))) (DEFUN MINI-BUFFER-OR-READLINE-HELP-FUNCTION () (FORMAT *TERMINAL-IO* "~&~%You are now typing an answer to a query.~&") (FUNCALL FQUERY-HELP-FUNCTION *TERMINAL-IO* FQUERY-CHOICES 'MINI-BUFFER-OR-READLINE-FQUERY-FUNCTION)) (DEFPARAMETER Y-OR-N-P-OPTIONS `(:FRESH-LINE NIL)) (DEFUN Y-OR-N-P (&OPTIONAL FORMAT-STRING &REST FORMAT-ARGS) "Ask the user a question he can answer with Y or N. Passes the arguments to FORMAT. With no args, asks the question without printing anything but the /"(Y or N)/". Returns T if the answer was yes." (FQUERY Y-OR-N-P-OPTIONS (AND FORMAT-STRING (IF (CHAR= (CHAR FORMAT-STRING (1- (LENGTH FORMAT-STRING))) #/SP) "~&~?" "~&~? ")) FORMAT-STRING FORMAT-ARGS)) (DEFCONST YES-OR-NO-P-OPTIONS `(:FRESH-LINE NIL :BEEP T :TYPE :READLINE :CHOICES ,YES-OR-NO-P-CHOICES)) (DEFUN YES-OR-NO-P (&OPTIONAL FORMAT-STRING &REST FORMAT-ARGS) "Ask the user a question he can answer with Yes or No. Beeps and discards type-ahead. Passes the arguments to FORMAT. With no args, asks the question without printing anything but the /"(Yes or No)/". Returns T if the answer was yes." (FQUERY YES-OR-NO-P-OPTIONS (AND FORMAT-STRING (IF (CHAR= (CHAR FORMAT-STRING (1- (LENGTH FORMAT-STRING))) #/SP) "~&~?" "~&~? ")) FORMAT-STRING FORMAT-ARGS)) (DEFPARAMETER YES-OR-NO-QUIETLY-P-OPTIONS `(:TYPE :READLINE :CHOICES ,YES-OR-NO-P-CHOICES))