%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Prompting.SL - NMODE Prompt Line Manager
% 
% Author:      Alan Snyder
%              Hewlett-Packard/CRC
% Date:        19 August 1982
% Revised:     16 February 1983
%
% Adapted from Will Galway's EMODE.
%
% 16-Feb-83 Alan Snyder
%   Declare -> Declare-Flavor.
% 7-Feb-83 Alan Snyder
%   Use one-window or one-screen refresh.
% 29-Dec-82 Alan Snyder
%   Revised input completion support to run completion characters as commands
%   rather than terminating and resuming.  Added new functions to manipulate the
%   input buffer.
% 22-Dec-82 Jeffrey Soreff
%   Revised to handle control characters on prompt and message lines.
% 21-Dec-82 Alan Snyder
%   Efficiency improvement: Added declarations for virtual screens and buffer
%   windows.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

(CompileTime (load objects extended-char fast-strings fast-int))

% External variables used:

(fluid
 '(nmode-prompt-screen
   nmode-message-screen
   nmode-input-window
   nmode-current-window
   ))

% Global variables defined here:

(fluid
 '(nmode-input-default
   ))

% Internal static variables:

(fluid
 '(nmode-prompt-cursor
   nmode-message-cursor
   nmode-message-string
   nmode-input-level
   nmode-input-special-command-list
   ))

(setf nmode-prompt-cursor 0)
(setf nmode-message-cursor 0)
(setf nmode-message-string "")
(setf nmode-input-level 0)
(setf nmode-input-default NIL)

(declare-flavor virtual-screen nmode-prompt-screen nmode-message-screen)
(declare-flavor buffer-window nmode-input-window nmode-current-window)
(declare-flavor text-buffer input-buffer)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% String input:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

(de prompt-for-string (prompt-string default-string)

  % Prompt for a string (terminated by CR or NL).  Use default-string if an
  % empty string is returned (and default-string is non-NIL).  The original
  % message line is restored, but not refreshed.  Note: if you attempt to use
  % this function recursively, it will automatically throw '$ERROR$.  The effect
  % of this action is that in string-input mode, commands that request string
  % input appear to be undefined.  (This assumes that all such commands do
  % nothing visible before they first request string input.)

  (prompt-for-string-special prompt-string default-string NIL))

(de prompt-for-string-special (prompt-string default-string command-list)

  % This function is similar to PROMPT-FOR-STRING, except that it accepts a
  % command list that specifies a set of additional commands to be defined
  % while the user is typing at the input window.

  (if (> nmode-input-level 0)
    (throw '$error$ NIL)
    % else
    (setf nmode-input-special-command-list command-list)
    (setf nmode-input-default default-string)
    (let ((old-msg nmode-message-string)
	  (old-window nmode-current-window)
	  (nmode-input-level (+ nmode-input-level 1)) % FLUID
	  )
      (if default-string
	(setf prompt-string
	  (string-concat prompt-string " (Default is: '" default-string "')")))

      (=> (=> nmode-input-window buffer) reset)
      (nmode-select-window nmode-input-window)
      (set-message prompt-string)
      (set-prompt "") % avoid old prompt popping back up when we're done

      % Edit the buffer until an "exit" character is typed or the user aborts.

      (cond ((eq (NMODE-reader T) 'abort)
	     (=> nmode-input-window deexpose)
	     (nmode-select-window old-window)
	     (set-message old-msg)
	     (throw 'abort NIL)
	     ))

      % Show the user that his input has been accepted.
      (move-to-start-of-line)
      (nmode-refresh-one-window nmode-input-window)

      % Pick up the string that was typed. 
      (let ((return-string (current-line)))

	% Switch back to old window, etc.
	(=> nmode-input-window deexpose)
	(nmode-select-window old-window)

	% Restore original "message window".
	(set-message old-msg)

	% If an empty string, use default (unless it's NIL).
	(if (and default-string (equal return-string ""))
	  default-string
	  return-string
	  )))))

(de nmode-substitute-default-input ()
  % If the input buffer is empty and there is a default string, then stuff the
  % default string into the input buffer.

  (let ((input-buffer (=> nmode-input-window buffer)))
    (if (and (=> input-buffer at-buffer-start?)
	     (=> input-buffer at-buffer-end?)
	     nmode-input-default
	     (stringp nmode-input-default)
	     )
      (=> input-buffer insert-string nmode-input-default)
      )))

(de nmode-get-input-string ()
  % Return the contents of the input buffer as a string.  If the buffer contains
  % more than one line, only the current line is returned.

  (let ((input-buffer (=> nmode-input-window buffer)))
    (=> input-buffer current-line)
    ))

(de nmode-replace-input-string (s)
  % Replace the contents of the input buffer with the specified string.
  (let ((input-buffer (=> nmode-input-window buffer)))
    (=> input-buffer reset)
    (=> input-buffer insert-string s)
    ))

(de nmode-terminate-input ()
  % A command bound to this function will act to terminate string input.
  (exit-nmode-reader)
  )

(de nmode-yank-default-input ()
  % A command bound to this function will act to insert the default string into
  % the input buffer.
  (if nmode-input-default
    (insert-string nmode-input-default)
    (Ding)
    ))

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Prompt line functions:
%
% NOTE: if your intent is to display a prompt string for user input, you should
% use a function defined in TERMINAL-INPUT rather than one of these.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

(de write-prompt (msg)
  % Write the specified string to the prompt line and refresh the prompt
  % line.  Note: the major windows are not refreshed.
  (set-prompt msg)
  (nmode-refresh-virtual-screen nmode-prompt-screen)
  )

(de set-prompt (msg)
  % Write the specified string to the prompt window, but do not refresh.
  (setf nmode-prompt-cursor 0)
  (=> nmode-prompt-screen clear)
  (prompt-append-string msg)
  )

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Message line functions:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

(de write-message (msg)
  % Display a string to the message window and refresh the message window.
  % Note: the major windows are not refreshed.
  % Return the previous message string.

  (prog1
   (set-message msg)
   (nmode-refresh-virtual-screen nmode-message-screen)
   ))

(de rewrite-message ()
  % Rewrite the existing message (used when the default enhancement changes).
  (set-message nmode-message-string)
  )

(de set-message (msg)
  % Display a string in the "message" window, do not refresh.
  % Message will not appear until a refresh is done.
  % Return the previous message string.

  (let ((old-message nmode-message-string))
    (setf nmode-message-string msg)
    (setf nmode-message-cursor 0)
    (=> nmode-message-screen clear)
    (message-append-string msg)
    old-message
    ))

(de reset-message ()
  % Clear the "message" window, but do not refresh.
  (setf nmode-message-string "")
  (setf nmode-message-cursor 0)
  (=> nmode-message-screen clear)
  )

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Internal functions:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

(de prompt-append-string (s)
  (for (from i 0 (string-upper-bound s))
       (do (prompt-append-character (string-fetch s i)))))

(de prompt-append-character (ch)
  (cond 
   ((or (< ch #\space) (= ch #\rubout)) % Control Characters
    (=> nmode-prompt-screen write #/^ 0 nmode-prompt-cursor)
    (setf nmode-prompt-cursor (+ nmode-prompt-cursor 1))
    (=> nmode-prompt-screen write (^ ch 8#100) 0 nmode-prompt-cursor)
    (setf nmode-prompt-cursor (+ nmode-prompt-cursor 1)))
   (t (=> nmode-prompt-screen write ch 0 nmode-prompt-cursor) % Normal Char
      (setf nmode-prompt-cursor (+ nmode-prompt-cursor 1)))))

(de message-append-string (s)
  (for (from i 0 (string-upper-bound s))
       (do (message-append-character (string-fetch s i)))))

(de message-append-character (ch)
  (cond 
   ((or (< ch #\space) (= ch #\rubout)) % Control Characters
    (=> nmode-message-screen write #/^ 0 nmode-message-cursor)
    (setf nmode-message-cursor (+ nmode-message-cursor 1))
    (=> nmode-message-screen write (^ ch 8#100) 0 nmode-message-cursor)
    (setf nmode-message-cursor (+ nmode-message-cursor 1)))
   (t (=> nmode-message-screen write ch 0 nmode-message-cursor) % Normal Char
      (setf nmode-message-cursor (+ nmode-message-cursor 1)))))

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

(undeclare-flavor nmode-prompt-screen nmode-message-screen)
(undeclare-flavor nmode-input-window nmode-current-window)
(undeclare-flavor input-buffer)
