(*----------------------------------------------------------------------*)

IMPLEMENTATION MODULE Display;

(*----------------------------------------------------------------------*)

FROM Strings IMPORT LengthC;

IMPORT TimeAndDate;

FROM Universe IMPORT BYTE, DebugFlags, Debugging, EndOfLineCh, TabCh, 
		     WindowI, MaxWindows, MaxHorizontal, MaxVertical,
		     MarkerFlagByte;
FROM Windows IMPORT WindowP, EdgeR, PointR, Vertical, Horizontal,
		    CursorOnOff,
		    VerticalSet, WindowStatusF, MaxTabColumn,
		    PendingR, DisplaySegmentR;
FROM Buffers IMPORT AreaR, BufferP, MarkerP, MarkerWithin, MarkerAt;
FROM Characters IMPORT BorderTruncatedChar, BorderTopChar,
                       BorderBottomChar,
                       BorderLeftChar, BorderRightChar,
                       GreyPatternChar,
                       LeftHexBase, RightHexBase,
                       TabBodyChar, TabHeadChar,
		       BorderTopRulerBase,
		       BorderTopLeftChar, BorderTopRightChar,
		       BorderBottomLeftChar, BorderBottomRightChar,
		       LeftBracket, RightBracket,
		       LeftArrowHeadChar, RightArrowHeadChar,
		       BorderFoldedLeftChar, BorderFoldedRightChar;
FROM DisplayPosition IMPORT FindCursorPosition, Indent;

IMPORT 
  Sets, Buffers, Windows, Errors, Fast, Screen, StringData, Edit, Memory;

(*----------------------------------------------------------------------*)

CONST  SizeOfWideChar = 2;

VAR  Pending: PendingR;

(*----------------------------------------------------------------------*)

PROCEDURE  Select (Window: WindowP);
  (* Informs the display manager that all subsequent commands are to
     refer to the given window *)
  BEGIN
    IF Window = NIL THEN
      Errors.Panic ("Display.Select called with NIL Window")
    END (* if *);
    CurrentWindow := Window;
    Fast.SetDisplayableCharacters (Window^.DisplayableCharacters)
  END Select;

(*----------------------------------------------------------------------*)

PROCEDURE Clear;
  (* Clears the currently selected window *)
  BEGIN
    WITH CurrentWindow^.Edge DO
      DisplayCursor.Y := Top;
      WHILE DisplayCursor.Y <= Bottom DO
	Screen.BlankPartOfLine (Left, Right);
	INC (DisplayCursor.Y)
      END (* while *)
    END (* with *);
  END Clear;

(*----------------------------------------------------------------------*)

PROCEDURE  DisplayRuler;
  VAR
    BeforeLength, AfterLength, TotalLength: CARDINAL;
    Position, Division: CARDINAL;
  BEGIN
    WITH CurrentWindow^.Buffer^ DO
      BeforeLength := Before.End - Before.Start;
      AfterLength := After.End - After.Start;
    END (* with *);
    TotalLength := BeforeLength + AfterLength;
    WITH CurrentWindow^ DO
      IF TotalLength = 0 THEN Position := 0;
      ELSE Position := (BeforeLength * Width * 8) DIV TotalLength;
      END (* if *);
      Division := Position MOD 8;
      Position := Position DIV 8 + Edge.Left;
      DisplayCursor.Y := Border.Position.Top;
      IF Border.Left.Visible THEN
	DisplayCursor.X := Border.Position.Left;
	Screen.UpdateCh (BYTE(BorderTopLeftChar));
	INC (DisplayCursor.X);
      ELSE
	DisplayCursor.X := Edge.Left;
      END (* if *);
      WHILE DisplayCursor.X <= Edge.Right DO
	IF DisplayCursor.X = Position THEN
	  Screen.UpdateCh (BYTE(BorderTopRulerBase + Division));
	ELSE
	  Screen.UpdateCh (BYTE(BorderTopChar));
	END (* if *);
	INC (DisplayCursor.X);
      END (* while *);
      IF Border.Right.Visible THEN
	Screen.UpdateCh (BYTE(BorderTopRightChar));
      END (* if *);
    END (* with *);
  END DisplayRuler;

(*----------------------------------------------------------------------*)

PROCEDURE DisplayBottomBorder;
  VAR  X: Horizontal;
  BEGIN
    WITH CurrentWindow^ DO
      DisplayCursor.Y := Border.Position.Bottom;
      IF Border.Left.Visible THEN
	DisplayCursor.X := Border.Position.Left;
	Screen.UpdateCh (BYTE(BorderBottomLeftChar));
      END (* if *);
      FOR X := 0 TO Width - 1 DO
	DisplayCursor.X := Edge.Left + X;
	(* Possibly display buffer information, e.g. name, modified *)
	Screen.UpdateCh (BYTE(BorderBottomChar));
      END (* for *);
      IF Border.Right.Visible THEN
	DisplayCursor.X := Border.Position.Right;
	Screen.UpdateCh (BYTE(BorderBottomRightChar));
      END (* if *);
    END (* with *);
  END DisplayBottomBorder;

(*----------------------------------------------------------------------*)

PROCEDURE DisplayTheCursor;
  VAR  PixelRow: [0..7];  Ch: BYTE;  Index: CARDINAL;
  BEGIN
    WITH CurrentWindow^ DO
      IF NoCursorF IN Status THEN
	RETURN (* No cursor wanted *)
      END (* if *);
      DisplayCursor := Cursor.Position;
      IF (Cursor.Position.X = Border.Position.Left) AND
	 Border.Left.Visible THEN
	Screen.UpdateCh (BYTE(LeftArrowHeadChar));
      ELSIF (Cursor.Position.X = Border.Position.Right) AND
	Border.Right.Visible THEN
	Screen.UpdateCh (BYTE(RightArrowHeadChar));
      ELSE
	WITH Buffer^ DO
	  IF After.Start < After.End THEN
	    Cursor.Text[0] := CHAR(Array.Data^[After.Start]);
	    IF Cursor.Text[0] < ' ' THEN
	      Cursor.Text[0] := ' ';
	    END (* if *);
	  ELSE
	    Cursor.Text[0] := ' ';
	  END (* if *);
	END (* with *);
	Cursor.Width := 1; (* for now *)
	Index := 0;
	WHILE Index < Cursor.Width DO
	  Ch := Cursor.Text[Index];
	  IF WidthDisplayCh (Ch, DisplayCursor.X, 0) = 1 THEN
	    Screen.InverseCh (Ch);
	  ELSE
	    Screen.InverseCh (BYTE('+'));
	  END (* if *);
	  INC (DisplayCursor.X);
	  INC (Index);
	END (* while *);
      END (* if *);
    END (* with *);
  END DisplayTheCursor;

(*----------------------------------------------------------------------*)

PROCEDURE Something;
  BEGIN
    WITH CurrentWindow^ DO
      EXCL (Status, DisplayCompletedF);
      IF NOT (Edit.Idle () OR (DisplayAtLeastOneLineF IN Status)) THEN
	RETURN
      END (* if *);
      IF DisplayCursorValidF IN Status THEN
	(* Disclaimer: The following statements are necessitated by the
	   dubious programming practices of K.D.Rautenbach *)
	(* Tweak cursor line display area to fix problems with:
	    - Moving across Before,After gap
	    - Indentation (?)
	*)
	WITH DisplayAreas[Cursor.Position.Y] DO
	  IF Area.Start <= Buffer^.Before.End THEN
	    Area.End := Buffer^.Before.End
	  END (* if *)
	END (* with *);
	DisplayCursor := Cursor.Position
      ELSE
	FindCursorPosition (CurrentWindow);
	DisplayCursor := Cursor.Position
      END (* if *);
      DisplayLines
    END (* with *);
    IF Edit.Idle () AND (CurrentWindow = Windows.Selected) THEN
      DisplayDormantWindows
    END (* if *);
    Screen.EnableCursor
  END Something;

(*----------------------------------------------------------------------*)

PROCEDURE InsertCurrentTime;
  VAR
    timeString: ARRAY [0..30] OF CHAR;
    len, i, timePos: CARDINAL;
      
  PROCEDURE Insert (ch: CHAR; index: CARDINAL);
    BEGIN
      WITH EnclosingWindow^.Buffer^ DO
	IF index < Before.End THEN
	  Array.Data^[index] := ch
	ELSE
	  index := After.Start + (index - Before.End);
	  IF index < After.End THEN Array.Data^[index] := ch
	  ELSE Errors.Panic ("Display.InsertCurrentTime: index OOR")
	  END (* if *);
	END (* if *);
      END (* with *);
    END Insert;
   
  BEGIN
    IF TimeAndDate.TimeAndDate (timeString, len) < 0 THEN
      RETURN (* No time set in BBC ! *)
    END (* if *);
    timePos := EnclosingWindow^.Width - len;
    FOR i := 0 TO len - 1 DO
      Insert (timeString[i], timePos + i)
    END (* for *);
    EXCL (EnclosingWindow^.Status, DisplayCompletedF);
  END InsertCurrentTime;

(*----------------------------------------------------------------------*)

PROCEDURE DisplayDormantWindows;
  VAR  I: WindowI;  Window: WindowP;
  BEGIN
    IF Edit.Idle () THEN
      FOR I := MaxWindows TO 0 BY -1 DO
	Window := Windows.Find (I);
	IF NOT Edit.Idle () THEN
	  CurrentWindow := Windows.Selected;
	  RETURN
	END (* if *);
	IF Window # NIL THEN
	  IF HiddenF IN Window^.Status THEN
	    (* Dont display anything *)
	  ELSE
	    IF Window = EnclosingWindow THEN InsertCurrentTime END;
	    IF NOT (DisplayCompletedF IN Window^.Status) THEN
	      Select (Window);
	      EXCL (Window^.Status, DisplayCursorValidF);
	      Something;
	      IF DisplayCompletedF IN CurrentWindow^.Status THEN
	        DisplayTheCursor
	      END (* if *)
	    END (* if *)
	  END (* if *)
	END (* if *)
      END (* for *)
    END (* if *);
    CurrentWindow := Windows.Selected
  END DisplayDormantWindows;

(*----------------------------------------------------------------------*)

PROCEDURE DisplayLines;
  (* Continue displaying something until not Edit.Idle *)
  VAR Marker: MarkerP;

  (*-------------------------------------------------------------------*)

  PROCEDURE DisplayLeftBorder;
    VAR  Ch: BYTE;
    BEGIN
      WITH CurrentWindow^ DO
	IF Border.Left.Visible THEN
	  DEC (DisplayCursor.X);
	  IF (NOT (NoCursorF IN Status)) AND
	     (Cursor.Position.X = Border.Position.Left) AND
	     (Cursor.Position.Y = DisplayCursor.Y) THEN
	    Ch := BYTE (LeftArrowHeadChar)
	  ELSIF CurrentArea.Pending.Fold THEN
	    Ch := BYTE (BorderFoldedLeftChar)
	  ELSIF Indentation > 0 THEN
	    Ch := BYTE (BorderTruncatedChar)
	  ELSIF CurrentArea.Pending.CountValid THEN
	    Ch := BYTE (BorderTruncatedChar)
	  ELSE
	    Ch := BYTE (BorderLeftChar)
	  END (* if *);
	  Screen.UpdateCh (Ch);
	  INC (DisplayCursor.X)
	END (* if *)
      END (* with *)
    END DisplayLeftBorder;

  (*-------------------------------------------------------------------*)

  VAR
    Junk, Succeeded, MarkerWasOutOfWindow, NewLineChosen: BOOLEAN;
    ThisLine: Screen.LineRecord;
    bufferData: Memory.ArrayByteP;
    ch: CHAR;

  BEGIN
    WITH CurrentWindow^ DO
      IF NOT ((DisplayAtLeastOneLineF IN Status) OR Edit.Idle ()) THEN
	RETURN
      END (* if *);
      EXCL (Status, DisplayVirginF);
      DisplayCursor.Y := Cursor.Position.Y;
      Displayed.MinimumY := DisplayCursor.Y;
      Displayed.MaximumY := DisplayCursor.Y;
      Cursor.Width := 0;
      bufferData := Buffer^.Array.Data;
      Fast.cache.wantArray := Buffer^.Array;
      Fast.cache.haveLast := Edge.Right;
      Fast.cache.wantPriority := CurrentWindow^.Priority;
      REPEAT (* until all lines in this window have been displayed *)
	ValidateRow (CurrentWindow, DisplayCursor.Y);
	CurrentArea := DisplayAreas[DisplayCursor.Y];
	Screen.EnableCursor;
	ThisLine := Screen.Image[DisplayCursor.Y];
	DisplayCursor.X := Edge.Left;
	IF Border.Left.Visible THEN DisplayLeftBorder END;
	Pending := CurrentArea.Pending;
	Fast.cache.wantLimit := CurrentArea.Area.End;
	Fast.cache.haveArray := ThisLine.Chars;
	Fast.cache.havePriorities := ThisLine.Priorities;
	LOOP (* until have displayed this line *)
	  (* Compare want = (buffer, current window id) against
	     have = (screen image, priority map) *)
	  CASE Fast.WhereDifferent ((*VAR*)CurrentArea.Area.Start,
				    (*VAR*)DisplayCursor.X) OF
	    Fast.DifferentWindowHere:
	      Errors.Panic ("Different window ??")

	  | Fast.DifferentHere:
	      ch := bufferData^[CurrentArea.Area.Start];
	      IF (' ' <= ch) AND (ch <= '~') THEN
		Screen.UpdateCh (ch);
		INC (CurrentArea.Area.Start)
	      ELSE
		IF DisplayCh (ch, Pending) THEN
		  INC (CurrentArea.Area.Start)
		END (* if *);
	      END (* if *);
	      IF CollidedWithRightOfWindow (CurrentArea) THEN EXIT END

	  | Fast.EndOfLineHere:
	      Screen.BlankPartOfLine (DisplayCursor.X, Edge.Right);
	      IF Border.Right.Visible THEN
		DisplayCursor.X := Border.Position.Right;
		IF (Cursor.Position.X = Border.Position.Right) AND
		   (Cursor.Position.Y = DisplayCursor.Y) THEN
		  Screen.UpdateCh (BYTE (RightArrowHeadChar))
		ELSE
		  Screen.UpdateCh (BYTE (BorderRightChar))
		END (* if *)
	      END (* if *);
	      CurrentArea.Pending.Fold := FALSE;
	      CurrentArea.Pending.CountValid := FALSE;
	      CurrentArea.Pending.WidePartialCount := 0;
	      INC (CurrentArea.Area.Start);
	      Indent (CurrentArea, Indentation, 0);
	      EXIT

	  | Fast.WantRanOutOfChars:
	      IF CurrentArea.Area.Start > Buffer^.After.End THEN
		(* Blank lines after EndOfText *)
		Screen.BlankPartOfLine (DisplayCursor.X, Edge.Right);
		IF Border.Right.Visible THEN
		  DisplayCursor.X := Border.Position.Right;
		  Screen.UpdateCh (BYTE (BorderRightChar))
		END (* if *);
		CurrentArea.Pending.Fold := FALSE;
		CurrentArea.Pending.CountValid := FALSE;
		CurrentArea.Pending.WidePartialCount := 0;
		EXIT
	      ELSIF CurrentArea.Area.Start = Buffer^.After.End THEN
		(* EndOfText *)
		IF (NOT (DisplayEndF IN Status)) OR
		   DisplayText ("End of Text", Pending) THEN
		  INC (CurrentArea.Area.Start);
		  IF (DisplayEndF IN Status) AND
		     CollidedWithRightOfWindow (CurrentArea) THEN
		    EXIT
		  END (* if *);
		  Screen.BlankPartOfLine (DisplayCursor.X, Edge.Right);
		  IF Border.Right.Visible THEN
		    DisplayCursor.X := Border.Position.Right;
		    Screen.UpdateCh (BYTE (BorderRightChar))
		  END (* if *);
		  CurrentArea.Pending.Fold := FALSE;
		  CurrentArea.Pending.CountValid := FALSE;
		  CurrentArea.Pending.WidePartialCount := 0;
		  EXIT
		END (* if *);
		IF CollidedWithRightOfWindow (CurrentArea) THEN EXIT END
	      ELSIF CurrentArea.Area.Start >= Buffer^.Before.End THEN
		(* Cursor line; transition from before cursor (in Before)
		   to after cursor (in After) *)
		CurrentArea.Area := Buffer^.After;
		Fast.cache.wantLimit := CurrentArea.Area.End;
		CurrentArea.Pending.Fold := FALSE;
		CurrentArea.Pending.CountValid := FALSE;
		CurrentArea.Pending.WidePartialCount := 0;
		IF DisplayCursor.X = Edge.Left THEN
		  DisplayLeftBorder
		END (* if *)
	      ELSE
		(* Hit bottom right of window ?? *)
		CurrentArea.Pending.Fold := FALSE;
		CurrentArea.Pending.CountValid := FALSE;
		CurrentArea.Pending.WidePartialCount := 0;
		Screen.BlankPartOfLine (DisplayCursor.X, Edge.Right);
		IF Border.Right.Visible THEN
		  DisplayCursor.X := Edge.Right + 1;
		  Screen.UpdateCh (BYTE (BorderRightChar))
		END (* if *);
		EXIT
	      END (* if *)

	  | Fast.HaveRanOutOfChars:
	      (* Long line, terminated by screen edge rather than EOLN *)
	      IF CollidedWithRightOfWindow (CurrentArea) THEN EXIT
	      ELSE Errors.Panic ("Display.DisplayLines: Assertion failed")
	      END (* if *)
	  END (* case *);
	  IF (DisplayAbandonBeforeLineCompleteF IN Status) AND
	     (NOT Edit.Idle ()) THEN
	    RETURN
	  END (* if *)
	END (* loop *);
	(* As an optimization, note that line after current one starts
           where this one ended *)
	(* !!! without this, ValidateRow fails below cursor *)
	IF (DisplayCursor.Y >= Cursor.Position.Y) AND
	   (DisplayCursor.Y < Edge.Bottom) THEN
	  CurrentArea.Area.End := Buffer^.After.End;
	  DisplayAreas[DisplayCursor.Y + 1] := CurrentArea;
	END (* if *);
	(* Now pick next line to update. Current ordering is cursor
	   outwards, but any should work (if really wierd, then may need
	   an updated-line flag array) *)
	IF DisplayCursor.Y < Displayed.MinimumY THEN
	  Displayed.MinimumY := DisplayCursor.Y;
	END (* if *);
	IF DisplayCursor.Y >= Displayed.MaximumY THEN
	  Displayed.MaximumY := DisplayCursor.Y;
	END (* if *);
	NewLineChosen := FALSE;
	IF DisplayCursor.Y >= Cursor.Position.Y THEN
	  (* Last was below the cursor, so try next from above *)
	  IF Displayed.MinimumY > Edge.Top THEN
	    DisplayCursor.Y := Displayed.MinimumY - 1;
	    NewLineChosen := TRUE;
	  END (* if *)
	ELSE
	       (* Last was above the cursor, so try next from below *)
	       IF Displayed.MaximumY < Edge.Bottom THEN
	          DisplayCursor.Y := Displayed.MaximumY + 1;
	          NewLineChosen := TRUE
 	       END (* if *)
	    END (* if *);

	    IF NOT NewLineChosen THEN
	       IF Displayed.MaximumY < Edge.Bottom THEN
	          DisplayCursor.Y := Displayed.MaximumY + 1
	       ELSIF Displayed.MinimumY > Edge.Top THEN
	          DisplayCursor.Y := Displayed.MinimumY - 1
	       ELSE
	          IF Edit.Idle () OR (DisplayCompleteWindowF IN Status) THEN
		     IF Border.Bottom.Visible THEN DisplayBottomBorder END;
		     IF Border.Top.Visible THEN DisplayRuler END;
		     INCL (Status, Windows.DisplayCompletedF)
	          END (* if *);
	          RETURN
	       END (* if *)
	    END (* if *);

         UNTIL NOT (Edit.Idle () OR (DisplayCompleteWindowF IN Status))

      END (* with *)

   END DisplayLines;

(*----------------------------------------------------------------------*)

PROCEDURE CollidedWithRightOfWindow (VAR currentArea: DisplaySegmentR): BOOLEAN;

   VAR ch: BYTE;

   BEGIN
      WITH CurrentWindow^ DO

         IF DisplayCursor.X < Edge.Right THEN
  	    INC (DisplayCursor.X);
	    RETURN FALSE;
         END (* if *);

         IF Border.Right.Visible THEN
	    INC (DisplayCursor.X);
	    IF (NOT (NoCursorF IN Status)) AND (Cursor.Position.X = Border.Position.Right) AND (Cursor.Position.Y = DisplayCursor.Y) THEN
	       ch := BYTE (RightArrowHeadChar);
	    ELSIF DisplayFoldedF IN Status THEN
	       ch := BYTE (BorderFoldedRightChar);
	    ELSE
	       ch := BYTE (BorderTruncatedChar);
	    END (* if *);
	    Screen.UpdateCh (ch);
         END (* if *);

         IF DisplayFoldedF IN Status THEN

	    currentArea.Pending.Fold := TRUE;
	    currentArea.Pending.WidePartialCount := Pending.WidePartialCount;
	    currentArea.Pending.CountValid := (Pending.WidePartialCount > 0);
	    IF Pending.WidePartialCount > Indentation THEN
	       DEC (Pending.WidePartialCount, Indentation);
	    ELSE
               Indent (currentArea, Indentation - Pending.WidePartialCount, 0);
	    END (* if *);

         ELSE

	    currentArea.Pending.CountValid := FALSE;
	    currentArea.Pending.WidePartialCount := 0;
	    Pending := currentArea.Pending;
	    IF Fast.SkipUntilEndOfLineCh (Buffer^.Array, currentArea.Area) THEN
	       INC (currentArea.Area.Start);
	    ELSE
	       IF currentArea.Area.Start = Buffer^.Before.End THEN
	          (* Area change to after *)
	          currentArea.Area := Buffer^.After;
	          IF Fast.SkipUntilEndOfLineCh (Buffer^.Array, currentArea.Area) THEN
		     INC (currentArea.Area.Start);
	          END (* if *);
	       ELSE (* currentArea.Area.Start = Buffer^.After.End *)
	          INC (currentArea.Area.Start);
	       END (* if *);
	    END (* if *);
	    Indent (currentArea, Indentation, 0);

         END (* if *);

      END (* with *);

      RETURN TRUE;

   END CollidedWithRightOfWindow;

(*----------------------------------------------------------------------*)

PROCEDURE DisplayCharacter (Ch: BYTE; VAR Pending: PendingR): BOOLEAN;

   (* Displays character and returns TRUE when character completely displayed *)

   VAR
      CurrentX   : CARDINAL;
      Marker	 : MarkerP;

   BEGIN
      IF (BYTE (' ') <= Ch) AND (Ch <= BYTE ('~')) THEN
         Screen.UpdateCh (Ch);
      ELSE
         IF Ch = EndOfLineCh THEN
	    Errors.Panic ("Display: DisplayCh (EndOfLineCh) unexpected");
         ELSIF Ch = TabCh THEN
	    IF Pending.CountValid THEN
	       IF Pending.WidePartialCount > 0 THEN
	          IF DisplayTabsAsArrowsF IN CurrentWindow^.Status THEN
		     IF Pending.WidePartialCount > 1 THEN
		        Screen.UpdateCh (BYTE (TabBodyChar));
		     ELSE
		        Screen.UpdateCh (BYTE (TabHeadChar));
		     END (* if *);
	          ELSE
		     Screen.UpdateCh (BYTE (' '));
	          END (* if *);
	          DEC (Pending.WidePartialCount);
	       END (* if *);
	       IF Pending.WidePartialCount < 1 THEN
	          Pending.WidePartialCount := 0;
	          Pending.CountValid := FALSE;
	          RETURN TRUE;
	       END (* if *);
	       RETURN FALSE;
	    ELSE
	       WITH CurrentWindow^ DO
	          CurrentX := Indentation + DisplayCursor.X - Edge.Left;
	          Pending.WidePartialCount := WidthDisplayCh (Ch, CurrentX, 0);
	          IF DisplayTabsAsArrowsF IN Status THEN
		     IF Pending.WidePartialCount > 1 THEN
		        Screen.UpdateCh (BYTE (TabBodyChar));
		     ELSE
		        Screen.UpdateCh (BYTE (TabHeadChar));
		     END (* if *);
	          ELSE
		     Screen.UpdateCh (BYTE (' '));
	          END (* if *);
	       END (* with *);
	       DEC (Pending.WidePartialCount);
	       Pending.CountValid := Pending.WidePartialCount > 0;
	       RETURN NOT Pending.CountValid;
	    END (* if *);
         ELSE
	    IF Ch = MarkerFlagByte THEN
	       Marker := NIL;
	       WHILE MarkerAt (CurrentWindow^.Buffer, CurrentArea.Area.Start, Marker) DO
	          IF Marker^.DisplaySize > 0 THEN
		     RETURN CurrentWindow^.DisplayMarker (Marker, Pending);
	          END (* if *);
	       END (* while *);
	    END (* if *);
	    IF Pending.CountValid THEN
	       CASE Pending.WidePartialCount OF
	         1: Screen.UpdateCh (BYTE (RightHexBase + (CARDINAL (Ch) MOD 16)));
	       | 0: (* nowt *)
 	       END (* case *);
	       Pending.WidePartialCount := 0;
	       Pending.CountValid := FALSE;
	       RETURN TRUE;
	    ELSE
	       Screen.UpdateCh (BYTE (LeftHexBase + (CARDINAL (Ch) DIV 16)));
	       Pending.WidePartialCount := SizeOfWideChar - 1;
	       Pending.CountValid := TRUE;
	       RETURN FALSE;
	    END (* if *);
         END (* if *);
      END (* if *);
      RETURN TRUE;
   END DisplayCharacter;

(*----------------------------------------------------------------------*)

PROCEDURE DisplayWidth (Ch: BYTE; X, ArrayIndex: CARDINAL): CARDINAL;

   VAR Marker: MarkerP;

   BEGIN
      IF (BYTE (' ') <= Ch) AND (Ch <= BYTE ('~')) THEN RETURN 1
      ELSIF Ch = EndOfLineCh THEN RETURN 0
      ELSIF Ch = TabCh THEN RETURN TabWidth (X)
      ELSE
         IF Ch = MarkerFlagByte THEN
 	    Marker := NIL;
	    WHILE MarkerAt (CurrentWindow^.Buffer, ArrayIndex, Marker) DO
	       IF Marker^.DisplaySize > 0 THEN
                  RETURN Marker^.DisplaySize
  	       END (* if *)
	    END (* while *)
         END (* if *);
         RETURN SizeOfWideChar (* Displays as "[xx]" *)
      END (* if *)
   END DisplayWidth;

(*----------------------------------------------------------------------*)

PROCEDURE TabWidth (PresentColumn: CARDINAL): CARDINAL;

   VAR Width: CARDINAL;

   BEGIN
      Width := 1;
      LOOP
         INC (PresentColumn);
         IF PresentColumn > MaxTabColumn THEN RETURN Width END;
         IF Sets.In (PresentColumn, CurrentWindow^.TabStops) THEN
	    RETURN Width
         END (* if *);
         INC (Width)
      END (* loop *)
   END TabWidth;

(*----------------------------------------------------------------------*)

PROCEDURE DisplayText (text: ARRAY OF CHAR; VAR Pending: PendingR): BOOLEAN;

   (* Displays the text and returns TRUE when completely displayed *)

   VAR len: CARDINAL;

   BEGIN
      len := LengthC (text);
      IF Pending.CountValid THEN
         IF Pending.WidePartialCount > 1 THEN
	    Screen.InverseCh (BYTE (text[(len + 1) - Pending.WidePartialCount]));
	    DEC (Pending.WidePartialCount);
	    RETURN FALSE;
         ELSE
	    IF Pending.WidePartialCount = 1 THEN
	       Screen.UpdateCh (BYTE (RightBracket));
	    END (* if *);
	    Pending.CountValid := FALSE;
	    Pending.WidePartialCount := 0;
	    RETURN TRUE;
         END (* if *)
      ELSE
         Screen.UpdateCh (BYTE (LeftBracket));
         Pending.WidePartialCount := len + 1;
         Pending.CountValid := TRUE;
         RETURN FALSE;
      END (* if *)
   END DisplayText;

(*----------------------------------------------------------------------*)

PROCEDURE DisplayMarker (Marker: MarkerP; VAR Pending: PendingR): BOOLEAN;
   BEGIN
      IF Marker^.Overlayed THEN
         Screen.UpdateCh (BYTE (GreyPatternChar));
         RETURN TRUE
      ELSE
         IF Marker^.DisplaySize > 0 THEN
	    RETURN DisplayText (Marker^.Text, Pending)
         ELSE
	    RETURN TRUE (* Nothing to display for this marker !! *)
         END (* if *)
      END (* if *)
   END DisplayMarker;

(*----------------------------------------------------------------------*)

PROCEDURE Initialise;

   VAR
      i  : CARDINAL;
      x  : CARDINAL;
      y  : CARDINAL;

   BEGIN
      LOOP
         IF Windows.New (EnclosingWindow, 0, 0, MaxHorizontal + 1, MaxVertical + 1) THEN
            IF Buffers.New (EnclosingWindow^.Buffer, MaxHorizontal + 1 + 3, EnclosingWindow^.Identity) THEN
(*
               IF StringData.CopyCD ("<<<Background>>>", EnclosingWindow^.Buffer^.Name) THEN
*)
                  EXIT;
(*
               END (* if *);
*)
            END (* if *);
         END (* if *);
         Errors.Panic ("Display: insufficient memory to create background window")
      END (* loop *);

      WITH EnclosingWindow^.Buffer^ DO
         Before.End := Array.Size;
         FOR i := Before.Start TO Before.End - 1 DO
            Array.Data^[i] := ' ';
         END (* for *);
      END (* with *);

      EnclosingWindow^.Cursor.Position.Y := 0;
      INCL (EnclosingWindow^.Status, NoCursorF);
      EXCL (EnclosingWindow^.Status, DisplayEndF);
      Windows.Select (EnclosingWindow, TRUE);
      Buffers.Select (EnclosingWindow^.Buffer);
      EnclosingWindow^.Margins.Real := EnclosingWindow^.Edge;
      EnclosingWindow^.Margins.Current := EnclosingWindow^.Edge;
      EnclosingWindow^.Cursor.Width := 0;

   END Initialise;

(*----------------------------------------------------------------------*)

PROCEDURE Terminate;

   BEGIN
   END Terminate;

(*----------------------------------------------------------------------*)

END Display.

