-- file: IntExceptions.mesa
-- edited by Brotz, December 9, 1980 2:45 PM
-- edited by Levin, August 11, 1980 11:43 AM


DIRECTORY
dsD: FROM "DisplayDefs",
displayCommon: FROM "DisplayCommon",
exD: FROM "ExceptionDefs",
ExceptionTableDefs,
inD: FROM "InteractorDefs",
Inline,
intCommon: FROM "IntCommon",
MiscDefs,
SegmentDefs,
Storage,
String;


IntExceptions: PROGRAM
IMPORTS disC: displayCommon, dsD, ExceptionTableDefs, exD, inD, Inline,
intC: intCommon, MiscDefs, SegmentDefs, Storage, String
EXPORTS exD, inD
SHARES inD = PUBLIC
BEGIN

OPEN exD, inD;

-- Purpose: displays all exception messages. Text of the exceptions are contained within
-- a table that is set up at install time. Exceptions are identified by their exception
-- number (a CARDINAL) defined in ExceptionDefs.

exceptionRegionDirty: PUBLIC BOOLEAN; -- exported variable.

rightX: ARRAY [1 .. 2] OF ScreenXCoord ← [leftMargin, leftMargin];

recursionDepth: CARDINAL ← 0;

SysBugSignal: SIGNAL [systemError: BOOLEAN] = CODE;


SysBug: PROCEDURE [exception: Exception ← nil, message: STRING ← NIL] =
-- Called when Laurel cannot proceed due to internal or user errors. When exception = nil
-- and message = NIL, assumes that an internal error has occurred. Otherwise,
-- assumes that a user error has occurred.
BEGIN
systemError: BOOLEAN = (exception = nil AND message = NIL);
recursionDepth ← recursionDepth + 1;
IF recursionDepth < 2 AND ~systemError THEN
BEGIN
DisplayExceptionOrStringOnLine[exception, message, 1];
DisplayExceptionLine[exD.cannotProceed, 2];
END;
SIGNAL SysBugSignal[systemError];
END; -- of SysBug --


DisplayException: PROCEDURE [exception: Exception] =
-- Displays error messages in the exceptions region.
BEGIN
-- Flashes screen, then displays one-line string in exception region.
IF ~disC.bitMapReady THEN RETURN;
ClearExceptionsRegion[];
DisplayExceptionLine[exception, 1];
FlashExceptionsRegion[];
END; -- of DisplayException --


DisplayExceptionString: PROCEDURE [string: STRING] =
-- Displays error string in the exceptions region.
BEGIN
-- Flashes screen, then displays one-line string in exception region.
IF ~disC.bitMapReady THEN RETURN;
ClearExceptionsRegion[];
DisplayExceptionStringOnLine[string, 1];
FlashExceptionsRegion[];
END; -- of DisplayException --


DisplayExceptionLine: PROCEDURE [exception: Exception,
lineNumber: CARDINAL] =
-- Maps exception to its string and displays it on lineNumber of the exceptions region.
BEGIN
exceptionString: STRING ← [maxExceptionStringLength];
IF exception = exD.nil THEN exceptionString ← NIL
ELSE GetExceptionString[exception, exceptionString];
DisplayExceptionStringOnLine[exceptionString, lineNumber];
END; -- of DisplayExceptionLine --


DisplayExceptionStringOnLine: PROCEDURE [exceptionString: STRING,
lineNumber: CARDINAL] =
-- Displays exceptionString on lineNumber of the exceptions region.
BEGIN
IF ~disC.bitMapReady THEN RETURN;
ClearExceptionLine[lineNumber];
AppendStringToExceptionLine[exceptionString, lineNumber
! ExceptionLineOverflow => CONTINUE];
END; -- of DisplayExceptionStringOnLine --


AppendStringToExceptionLine: PROCEDURE [s: STRING, line: CARDINAL] =
-- Appends s to the text already on line. May signal ExceptionLineOverflow with parameter
-- i the index of the first character in s that doesn’t fit on line.
BEGIN
firstUnusedIndex: CARDINAL;
IF ~disC.bitMapReady THEN RETURN;
IF s # NIL THEN
BEGIN
[firstUnusedIndex, rightX[line]] ← FillScreenWithWords
[rightX[line], intC.exceptionsRegion.topY + line * dsD.lineHeight, rightMargin, 0, s];
exceptionRegionDirty ← TRUE;
IF firstUnusedIndex # s.length THEN ERROR ExceptionLineOverflow[firstUnusedIndex];
END;
END; -- of AppendStringToExceptionLine --


AppendExceptionToExceptionLine: PROCEDURE [exception: Exception, line: CARDINAL] =
-- Appends exception to the text already on line. May signal ExceptionLineOverflow with
-- parameter i being the the index of the first character in exception that doesn’t fit on
-- line.
BEGIN
exceptionString: STRING ← [maxExceptionStringLength];
IF exception = exD.nil THEN exceptionString ← NIL
ELSE GetExceptionString[exception, exceptionString];
AppendStringToExceptionLine[exceptionString, line];
END; -- of AppendExceptionToExceptionLine --


AppendDecimalToExceptionLine: PROCEDURE [n: CARDINAL, line: CARDINAL] =
-- Appends n to the text already on line. May signal ExceptionLineOverflow with parameter
-- i being the index of the first character in n that doesn’t fit on line.
BEGIN
nString: STRING = [6];
String.AppendNumber[nString, n, 10];
AppendStringToExceptionLine[nString, line];
END; -- of AppendDecimalToExceptionLine --


ExceptionLineOverflow: ERROR [i: CARDINAL] = CODE;
-- Raised by AppendStringToExceptionLine (see above).


DisplayExceptionOrStringOnLine: PROCEDURE [exception: Exception, string: STRING,
lineNumber: CARDINAL] =
-- Displays string if non-nil, otherwise exception on lineNumber of the exceptions region.
BEGIN
IF string = NIL OR string.length = 0 THEN DisplayExceptionLine[exception, lineNumber]
ELSE DisplayExceptionStringOnLine[string, lineNumber];
END; -- of DisplayExceptionOrStringOnLine --


DisplayBothExceptionLines: PROCEDURE[string1: STRING, exception1: exD.Exception,
string2: STRING, exception2: exD.Exception, flash: BOOLEAN ← TRUE] =
-- Displays exception messages. Will use string1 if non-NIL and non-zero length, exception1
-- otherwise. Mutatis mutandis for string2. If flash, then will flash exceptions region.
BEGIN
DisplayExceptionOrStringOnLine[exception1, string1, 1];
DisplayExceptionOrStringOnLine[exception2, string2, 2];
IF flash THEN FlashExceptionsRegion[];
END; -- of DisplayBothExceptionLines --


-- Exception Table Code --

tableStart: CARDINAL;
tableLength: CARDINAL;

segments: DESCRIPTOR FOR ARRAY OF SegmentDefs.FileSegmentHandle;

InitializeExceptions: PROCEDURE =
-- Assumed to be called before BringInLaurelState
BEGIN
OPEN SegmentDefs;
seg: FileSegmentHandle;
nSegs, i: CARDINAL;
[seg, tableStart] ← MiscDefs.DestroyFakeModule[LOOPHOLE[ExceptionTableDefs.ExceptionTable]];
nSegs ← seg.pages;
segments ← DESCRIPTOR[Storage.Node[nSegs], nSegs];
FOR i IN [0..nSegs) DO
segments[i] ← NewFileSegment[file: seg.file, base: seg.base+i, pages: 1, access: Read];
ENDLOOP;
DeleteFileSegment[seg];
exceptionRegionDirty ← FALSE;
END; -- of InitializeExceptions --


SetUpExceptionTable: PROCEDURE =
-- Establishes proper table for Get/Append ExceptionString.
BEGIN
tableLength ← GetTableWord[tableStart];
IF intC.exceptionType = LaurelX THEN -- skip over Laurel table
tableLength ← GetTableWord[tableStart ← tableStart+tableLength+1];
tableStart ← tableStart+1;
END; -- of UseExceptionTable --


GetExceptionString: PROCEDURE [exception: Exception, exceptionString: STRING] =
-- Fills in caller supplied exceptionString with text corresponding to exception.
BEGIN
exceptionString.length ← 0;
AppendExceptionString[exception, exceptionString];
END; -- of GetExceptionString --


AppendExceptionString: PROCEDURE [exception: Exception, exceptionString: STRING] =
-- Appends text corresponding to exception to end of caller supplied exceptionString.
BEGIN
OPEN SegmentDefs;
segNo: CARDINAL;
byte: [0..512];
seg: FileSegmentHandle;
chars: POINTER TO PACKED ARRAY [0..512) OF CHARACTER;

SetUpSegment: PROCEDURE =
BEGIN
seg ← segments[segNo];
SwapIn[seg];
chars ← FileSegmentAddress[seg];
END;

IF exception >= tableLength THEN RETURN;
-- bogus Exception number; treat as empty string
[segNo, byte] ← Inline.DIVMOD[GetTableWord[tableStart+exception], 512];
SetUpSegment[];
UNTIL exceptionString.length = exceptionString.maxlength DO
IF byte = 512 THEN BEGIN Unlock[seg]; segNo ← segNo+1; SetUpSegment[]; byte ← 0; END;
IF chars[byte] = ExceptionTableDefs.endMarker THEN EXIT
ELSE String.AppendChar[exceptionString, chars[byte]];
byte ← byte+1;
ENDLOOP;
Unlock[seg];
END; -- of AppendExceptionString --


GetTableWord: PRIVATE PROCEDURE[word: CARDINAL] RETURNS[val: CARDINAL] =
BEGIN
OPEN SegmentDefs;
segNo: CARDINAL;
seg: FileSegmentHandle;
wordOffset: [0..256);
[segNo, wordOffset] ← Inline.DIVMOD[word, 256];
seg ← segments[segNo];
SwapIn[seg];
val ← (FileSegmentAddress[seg]+wordOffset)↑;
Unlock[seg];
END;


RealClearExceptionsRegion: PROCEDURE =
-- Really clears the entire text area of the exceptions region.
BEGIN
IF ~disC.bitMapReady THEN RETURN;
ClearExceptionLine[1];
ClearExceptionLine[2];
exceptionRegionDirty ← FALSE;
END; -- of RealClearExceptionsRegion --


ClearExceptionLine: PROCEDURE [line: CARDINAL] =
BEGIN
IF ~disC.bitMapReady THEN RETURN;
dsD.ClearRectangle
[leftMargin, rightX[line], intC.exceptionsRegion.topY + line * dsD.lineHeight,
intC.exceptionsRegion.topY + (line + 1) * dsD.lineHeight];
rightX[line] ← leftMargin;
END; -- of ClearExceptionLine --


FlashExceptionsRegion: PROCEDURE =
-- Flashes a rectangle within the exceptions region.
BEGIN
IF ~disC.bitMapReady THEN RETURN;
THROUGH [1 .. 4] DO
IF MouseButton[any, down] THEN RETURN;
THROUGH [1 .. 2] DO
dsD.InvertRectangle[leftMargin, rightMargin,
intC.exceptionsRegion.topY+ dsD.lineHeight,
intC.exceptionsRegion.topY + 3*dsD.lineHeight];
BusyWait[3];
ENDLOOP;
ENDLOOP;
END; -- of FlashExceptionsRegion --


BusyWait: PROCEDURE [duration: CARDINAL] =
-- Waits for a period of (duration/26) seconds.
-- Imperative that this procedure not WAIT or Yield.
BEGIN
startTime: CARDINAL = realTimeClock↑;
UNTIL LOOPHOLE[realTimeClock↑ - startTime, CARDINAL] >= duration DO
ENDLOOP;
END; -- of BusyWait --


ExceptionsTracker: PROCEDURE [mnp: MessageTextNbrPtr, trackerType: TrackerType] =
-- Tracks cursor within the Exceptions neighborhood.
BEGIN -- ## of course this is not in a final state --
x: ScreenXCoord;
y: ScreenYCoord;
xOffset, yOffset: INTEGER;
dsD.ChangeCursor[charArrow];
[ , xOffset, yOffset] ← dsD.GetCursor[];
DO
x ← cursorX↑ + xOffset;
y ← cursorY↑ + yOffset;
IF ~(y IN [mnp.topY .. mnp.bottomY)) THEN RETURN;
IdleLoop[];
AcceptKeyboardInput[];
ENDLOOP;
END; -- of ExceptionsTracker --


END. -- of IntExceptions --