<* PRAGMA LL *>A textport is a
VBT that allows the user to type and edit text.
The methods and procedures in this interface fall into several categories, each dealing with different aspects of the text-editor.
\begin{description}
\item[Appearance] The client can choose the font, colors, margins, and whether long lines should be clipped or wrapped. The fonts and colors can be changed dynamically.
\item[Access to the text] There are procedures to read and write subsequences of the text, to read and set the current ``type-in'' point (cursor position), to get the length of the text, and to make the text read-only.
   \item[Keybindings and Text-Selections] A textport is initialized
   with a {\em model}, an object (defined in the TextPortClass
   interface) that establishes the connection between keystrokes and
   editing operations, and the connection between mouse-gestures, the
   cursor position, local selections (including highlighted regions),
   and global selections such as the ``clipboard'' (VBT.Source).
   Four such models are implemented---Emacs, Ivy, Xterm, and
   Mac---corresponding to different editing paradigms. The choice of
   model can be changed dynamically. The client may override the
   filter method to intercept keystrokes.
\item[Feedback] A textport has callback-methods that are invoked when the text changes, when the user types Return or Tab, when the textport gains or loses the keyboard focus, when the visible region changes, and when errors are detected. All these methods have defaults.
\end{description}
   The locking level for all procedures is LL <= VBT.mu except as noted. 
INTERFACEThe callTextPort ; IMPORT Font, PaintOp, VBT, VText; TYPE T <: Public; Public = VBT.Leaf OBJECT METHODS init (hMargin, vMargin := 0.5; font := Font.BuiltIn; colorScheme: PaintOp.ColorScheme := NIL; wrap := TRUE; readOnly := FALSE; turnMargin := 0.5; model := Model.Default): T; <* LL.sup = VBT.mu *> filter (cd: VBT.KeyRec); getFont (): Font.T; setFont (font: Font.T); getColorScheme (): PaintOp.ColorScheme; setColorScheme (c: PaintOp.ColorScheme); getModel (): SpecificModel; setModel (model: Model); getReadOnly (): BOOLEAN; setReadOnly (flag: BOOLEAN); (* callbacks *) modified (); returnAction (READONLY cd: VBT.KeyRec); tabAction (READONLY cd: VBT.KeyRec); focus (gaining: BOOLEAN; time: VBT.TimeStamp); error (msg: TEXT); END;
v.init(...) initializes v as a TextPort.T and
   returns it.
   The parameters hMargin and vMargin indicate how much whitespace
   to leave around the text, expressed in millimeters.
   colorScheme is used for painting the text.  If the parameter is
   NIL, then PaintOp.bgFg will be used.
   If wrap is TRUE, then text will be wrapped across line
   boundaries; otherwise it will be clipped.  If it is wrapped, then
   turnMargin specifies the width (in millimeters) of the gray bar
   placed at the end of the first line and the beginning of the
   second, indicating that the text has been wrapped.
   If readOnly is TRUE, then the text cannot be changed through
   the user interface (keyboard and mouse).  The procedures Replace,
   Insert, SetText, and PutText bypass the read-only protection,
   but these are not called by internal routines.  In all other
   descriptions in this interface, the words {\it replace}, {\it insert},
   {\it delete}, and so on should be understood as having the
   restriction that v is not read-only.
   If model is Model.Default, then the default model (see below)
   will be used.
   v.getModel() returns the name of the current model; note that the
   return value cannot be Model.Default.  The call v.setModel(...)
   changes the current model; its parameter may be Model.Default, in
   which case the default model (see below) will be used.
   The call v.setFont(font) changes the font used for displaying
   the text.
   The call v.setColorScheme(c) changes the colors used for
   displaying the text.
   The implementation calls v.focus(gaining, time) whenever
   v gains or loses the keyboard focus.  If gaining is TRUE,
   then v is about to gain the keyboard focus (and time is a
   valid event-time); i.e., this method is called {\em before} the
   selection feedback is established, so it is reasonable to call
   Select (below) or put up some other indication.  If gaining is
   FALSE, then v has just lost the keyboard focus (and time
   is {\em not} valid), so it reasonable to take down whatever
   indicated that the focus had been acquired.  It is not within the
   power of the focus method to prevent v from gaining or
   losing the focus.  The default for this method is a no-op.
   The implementation calls v.error(msg) whenever an exception is
   raised for which there is no particular remedy, such as an
   Rd.Failure.  The value of msg will be a short description of
   the error, typically the name of the procedure where the exception
   was raised.  No method or procedure defined in this interface
   raises exceptions, but the client may wish to override this method
   in order to report the error in a popup window, for example. The
   default for this method is a procedure that tests whether the
   environment-variable named TEXTPORTDEBUG\index{TEXTPORTDEBUG} is
   set (to any value); if so, it writes the message to
   Stdio.stderr.
\subsubsection{Access to the text}
   The textport's initial read-only status depends on the readOnly
   parameter to the init method. The getReadOnly method returns
   it; the setReadOnly method sets it.
PROCEDURE GetText (v    : T;
                   begin: CARDINAL := 0;
                   end  : CARDINAL := LAST (CARDINAL)): TEXT;
<* LL.sup = VBT.mu *>
Returns a sub-sequence of the text inv. The result will be empty ifbegin >= Length(v)Otherwise the range of indexes of the subsequence is[begin .. MIN (end, Length (v)) - 1]
PROCEDURE SetText (v: T; t: TEXT);
Replace the current contents ofvwitht. This procedure does not test the read-only status ofv.
PROCEDURE PutText (v: T; t: TEXT);
Appendtto the current contents ofv. This procedure does not test the read-only status ofv.
PROCEDURE Replace (v: T; begin, end: CARDINAL; newText: TEXT);
Replace the text between positionsbeginandendinvwithnewText. Ifbeginandendare beyond the end of the text, they are taken to refer to the end of the text. This procedure does not test the read-only status ofv.
PROCEDURE Insert (v: T; text: TEXT);
If there is a replace-mode selection (see Section~\ref{ReplaceMode}, page~\pageref{ReplaceMode}), replace it withtext; otherwise inserttextat the type-in point. In either case, this is a no-op iftextis the empty string. This procedure does not test the read-only status ofv.
PROCEDURE Index (v: T): CARDINAL;
Return the current ``type-in'' position.
PROCEDURE Seek (v: T; n: CARDINAL);
 Set the ``type-in'' position to n. PROCEDURE Length (v: T): CARDINAL;
 Return the number of characters in v's text. PROCEDURE Newline (v: T);
Insert a newline character at the type-in point.
PROCEDURE NewlineAndIndent (v: T);
Insert a newline character and enough spaces to match the indentation of the previous line. As it leaves a blank line, it will delete all spaces from that line so as to leave it truly empty.
PROCEDURE IsVisible (v: T; pos: CARDINAL): BOOLEAN;
 Test whether the character at position pos is visible. \subsubsection {Models}\index{Model}
TYPE
  Model = {Default, Ivy, Emacs, Mac, Xterm};
  SpecificModel = [Model.Ivy .. Model.Xterm];
VAR
   DefaultModel: SpecificModel;
 The default editing model, DefaultModel, is initialized to the
   environment variable named TEXTPORTMODEL;\index{TEXTPORTMODEL} if that
   variable is not set, or set to something other than emacs, ivy,
   mac, or xterm at startup time, then Model.Emacs will be used.  See
   the EmacsModel, IvyModel, XtermModel, and MacModel interfaces in
   Appendices \ref{EmacsModel}--\ref{XtermModel} for details on
   keybindings, mouse-clicks, and selections.
PROCEDURE ChangeAllTextPorts (v: VBT.T; newModel := Model.Default);
For each textportpthat is a descendent of VBTv, callp.setModel(newModel).
\subsubsection {Keybindings}\label{TextPortKeybindings}
   The TextPort interface allows clients a great deal of flexibility
   in handling keystrokes.  v.key(cd) proceeds in
   three steps:
   In step 1, it tests whether cd.wentDown is true, whether v
   has the keyboard focus, and whether v's domain is non-empty.
   If all three conditions are true, it proceeds to step 2.
   In step 2, it passes cd to the model's keyfilter object, which
   handles low-level tasks such as converting ``Escape + character''
   into ``meta-character'' (in Emacs mode), 8-bit ``compose
   character'' operations, and so on.  The model may actually contain
   a {\em chain} of keyfilters (see the KeyFilter interface), each
   implementing some translation.
   In step 3, the model passes cd (possibly changed by the
   keyfilters) to the textport's filter method. Clients who wish
   to intercept keystrokes usually do so at this point, by overriding
   the filter method, rather than by overriding the key method, so
   that they can take advantage of the low-level conversions.
   In the default filter method, there are several mutually
   exclusive possibilities, tested in this order:
\begin{itemize}
   \item{If the key is Return, then if the shift modifier is on, we
   insert a newline; if the option modifier is on, we insert a
   newline but leave the cursor in place; otherwise, we invoke
   v.returnAction(cd), another callback method. Its default
   method calls NewlineAndIndent(v, cd).}
   \item{If the key is Tab, we invoke v.tabAction(cd).  The
   default method inserts 4 spaces.}
   \item{If the key is an ``arrow'' key, we call the model's
   arrowKey method, which moves the cursor one character forward,
   one character backward, one line up, or one line down, as
   appropriate.}
   \item{If the control modifier is on, we call the model's
   controlChord method.}
   \item{If the option modifier is on, we call the model's
   optionChord method.}
\item{If the key is Backspace or Delete, we delete the previous character, or the current primary selection, if that is non-empty and in replace-mode.}
\item{If the key is an ISO Latin-1 graphic character, we insert it into the text.}
\item{Otherwise, we ignore it.}
\end{itemize}
   Finally, we call Normalize(v), except in the controlChord and
   optionChord cases.
   Clients can specialize the handling of keys, therefore, by
   overriding the textport's key, filter, returnAction, or
   tabAction methods, and by overriding the model's controlChord,
   optionChord, or arrowKey methods.
The following procedures give the client access to the keyboard focus:
PROCEDURE TryFocus (v: T; t: VBT.TimeStamp): BOOLEAN;
Try to acquire the keyboard focus and the primary selection, and report whether it succeeded.
PROCEDURE HasFocus (v: T): BOOLEAN; <* LL.sup = VBT.mu *>
 Test whether v has the keyboard focus. \subsubsection{Selections}\label{TextPortSelections}
   With various keyboard and mouse-gestures, the user may delimit a
   range of text, known as a {\em local selection}.  The TextPort
   interface defines two local selections, called {\em primary} and
   {\em secondary}. The mechanism for doing this depends entirely on
   the textport's model. (In fact, only the Ivy model implements
   secondary selection.) The type-in point is always at one end or the
   other of the primary selection.
Primary selections in non-readonly textports may be in {\em replace mode}, also called {\em pending-delete mode}. This means that any text that is inserted will replace the primary selection, and that the Backspace and Delete keys will delete it.
   Independent of the local selections are the two {\em global
   selections} defined by Trestle: VBT.Source and VBT.Target.  On
   X window systems, these are defined by the X server, and are shared
   across applications. The Source selection, for example, is
   effectively the ``clipboard.'' Globals selections are ``owned'' by
   one program at a time; in Trestle programs, they are owned by one
   VBT at a time. While every textport may have a primary and
   secondary local selection, at most one can own Source, and at most
   one can own Target. The {\em contents} of a global selection are
   controlled by its owner.
The correspondence between local and global selections also depends entirely on the model. Every model implements an operation called {\bf Copy}\index{Copy}, which is defined as follows: the textport acquires ownership of Source, and copies the Primary selection so that it is the contents of Source.
Some models establish an {\em alias}\index{alias} between a local and a global selection, which means that when that textport owns the global selection, the contents of the global selection are {\em identical with} the contents of the local selection.
In the Ivy model, for example, Primary is an alias for Target, and Secondary is an alias for Source. In the Xterm model, Primary is an alias for Source. The other models do not use aliasing at all; they implement {\bf Copy} by making a separate copy of the local selection. In those models, the contents of the global selection are not visible; i.e., they are not displayed in the textport.
Local selections are usually highlighted in some way. The highlighting obeys the following conventions, applied in this order:
\begin{enumerate}\index{TextPortHighlighting}\label{TextPortHighlighting}
\item A replace-mode Primary selection is highlighted with black text on a light red background. (On monochrome screens, it is highlighted with ``inverse video'': white text on a dark background.)
\item If a Source selection is visible (i.e., if it is aliased with a local selection), it is highlighted with a thin, green underline. (On monochrome screens, it is a thin, black underline.)
\item A Primary selection that is neither a replace-mode selection nor a Source selection (e.g., a selection in the Emacs model), is underlined with a thick line. On color screens, there is a further distinction: in a read-only text, the underline is blue; otherwise, the underline is red.
\end{enumerate}
   A selection is represented by a pair of inclusive indexes (begin
   and end) into the text.  The current selection-indices can be
   retrieved via the GetSelection procedure.
 
TYPE SelectionType = {Primary, Secondary};
PROCEDURE Select (v    : T;
                  time : VBT.TimeStamp;
                  begin: CARDINAL        := 0;
                  end  : CARDINAL        := LAST (CARDINAL);
                  sel         := SelectionType.Primary;
                  replaceMode := FALSE;
                  caretEnd    := VText.WhichEnd.Right   );
 Make a selection in v, at event-time time.  If begin and/or
   end are beyond the end of the text, they will be clipped to the
   end of the text.  Acquire ownership of the corresponding
   VBT.Selection; if sel is SelectionType.Primary, acquire
   ownership of the keyboard focus as well.
   The parameters replaceMode and caretEnd are relevant only if
   the value of sel is SelectionType.Primary.  If replaceMode is
   TRUE and the entire selection is writable, then Insert and
   VBT.Write will {\em replace} the selected text; otherwise, they
   cause the new text to be {\em inserted} at whichever end of the
   primary selection is specified by caretEnd. 
PROCEDURE IsReplaceMode (v: T): BOOLEAN;
 Return TRUE if the primary selection is in replace mode. 
TYPE Extent = RECORD l, r: CARDINAL END;
CONST NotFound = Extent {LAST (CARDINAL), LAST (CARDINAL)};
PROCEDURE GetSelection (v: T; sel := SelectionType.Primary):
  Extent;
Return the extent of the most recent selection inv. If there is no such selection, returnNotFound.
PROCEDURE GetSelectedText (v: T; sel := SelectionType.Primary): TEXT; <* LL.sup = VBT.mu *>
 Return the text of the most recent selection in v if there is one, or
   the empty string otherwise. 
PROCEDURE PutSelectedText (v: T;
                           t: TEXT;
                           sel := SelectionType.Primary);
<* LL.sup = VBT.mu *>
Replace the text of the most recent selection inv, if there is one, witht. If there is no such selection, this is a no-op.
\subsubsection{Feedback}
   A textport maintains a ``modified'' flag.  Any operation that
   changes the text will cause this flag to be set to TRUE.  If it
   was previously FALSE, then the implementation calls
   v.modified() {\it after} the change has already happened to v.
   The default is a no-op.  The IsModified and SetModified
   procedures set and test this flag, respectively. 
PROCEDURE IsModified (v: T): BOOLEAN;
Return the value of the ``modified'' flag forv. Any change to the text will cause the flag to be set toTRUE.
PROCEDURE SetModified (v: T; value: BOOLEAN);
Set the value of the ``modified'' flag forv. This will not invokev.modified, even ifvalueisTRUE.
A textport also maintains a scrollbar (optional). See the
TextEditVBT interface in Section~\ref{TextEditVBTSection}. 
PROCEDURE Normalize (v: T; to := -1);
Scrollvif necessary to ensure that positiontois visible. Ifto < 0, it refers to the current type-in point. Iftois larger than the length of the text, normalizes to the end of the text.
\subsubsection{Direct access to the text}
PROCEDURE GetVText (v: T): VText.T;
For wizards only: extract the underlyingVText. It is legal to create and manipulate highlighting intervals on it. It is legal to run readers on it, provided you can be sure that you are locking out concurrent change (for example, by holdingVBT.mu). It is not legal to modify it directly. It is not legal to scroll it directly either, because that will leave the scrollbar incorrect.
END TextPort.