-- Author: John Maxwell
-- Last Edited by: Maxwell, November 22, 1983 10:52 am

DIRECTORY
	CursorDefs, 
	Directory USING [Error], 
	-- DisplayDefs USING [DisplayOff], 
	Graphics USING [
	  Box, CopyContext, DrawChar, 
	  Context, DrawRope, DrawBox, FontRef, GetBounds,  
	  MakeFont, Map, NewContext, PaintMode, RopeBox, RopeWidth, 
	  ClipBox, SetCP, SetDefaultFont, SetFat, SetPaintMode, SetStipple,  
	  Translate, WorldToUser], 
	Heap USING [MakeMDSString, FreeMDSString], 
	MusicDefs USING [AnyBug, black, BlueBug, Control, grey, RedBug, Shift, ScorePTR, 
	  white, YellowBug], 
	-- MusicProcess, 
	Piece USING [Free, Overflow],
	Real USING [FixI], 
	-- Score USING [InitializeSynthesizer, StopListening, StopPlaying, Test], 
	Screen USING [commands, CommandProcs], 
	String USING [AppendChar, AppendString, InvalidNumber], 
	TerminalMultiplex USING [RegisterNotifier, TerminalSwapNotifier], 
	TTY USING [CharsAvailable, Create, GetChar, Handle, Rubout, SetEcho], 
	UserTerminal USING [mouse, GetCursorPattern, SetCursorPattern, WaitForScanLine];


ScreenImpl: MONITOR 
  IMPORTS 
     Directory, Graphics, Heap, MusicDefs, -- Music: MusicProcess, -- 
     Piece, Real, Screen, String, TerminalMultiplex, TTY, UserTerminal 
  EXPORTS Screen = 
BEGIN 
OPEN Graphics, CursorDefs, MusicDefs, Screen, UserTerminal;

keyboard: TTY.Handle;
font: Graphics.FontRef;

-- ******************************************************************
-- Multiplexing code
-- ******************************************************************

running: BOOLEAN ← TRUE;

WaitRunning: ENTRY PROCEDURE = INLINE
  {WHILE ~running DO UserTerminal.WaitForScanLine[0]; ENDLOOP};

Notifier: TerminalMultiplex.TerminalSwapNotifier = 
  BEGIN
  SELECT action FROM
     coming => running ← TRUE;
     going => running ← FALSE;
     ENDCASE;
  END;

-- ******************************************************************
-- User Commands
-- ******************************************************************

main: PROCESS ← NIL;

Initialize: PROCEDURE = 
BEGIN
TerminalMultiplex.RegisterNotifier[Notifier];
-- DisplayDefs.DisplayOff[white];
-- DisplayDefs.SetSystemDisplaySize[0, 0];
[] ← TTY.SetEcho[keyboard, FALSE];
keyboard ← TTY.Create[NIL];
screen ← NewContext[NIL];
[] ← SetFat[screen, TRUE];
windows[0] ← [screen, , , command, @commandProcs, 800, 740];
DefaultWindow[1, 740, 0];
font ← MakeFont["TimesRoman12"];
SetDefaultFont[screen, font];
commandProcs.display ← DisplayCommandWindow;
-- FrameDefs.MakeCodeResident[FrameDefs.GlobalFrame[Music]];
-- Score.InitializeSynthesizer[];
DrawScreen[];
-- ReadCommandLine[];
SetCursorPattern[textCursor];
main ← FORK Main[];
END;

ForkMain: PROCEDURE = {main ← FORK Main[]};

Main: PROCEDURE =
BEGIN
ENABLE {
	Piece.Overflow => {DisplayMessage["Piece.Overflow"]; RESUME};
	ANY => DisplayMessage["UNCAUGHT SIGNAL"]};
DisplayMessage["Mockingbird of November 22, 1983"];
DO IF badCommand THEN BEGIN
        WHILE TTY.GetChar[keyboard] # 177C DO NULL; ENDLOOP;
        EraseMessage[]; badCommand ← FALSE; END;
    badCommand ← FALSE;
    WaitRunning[];
    SELECT TRUE FROM
    RedBug[] => HandleRed[];
    BlueBug[] => HandleBlue[FindWindow[ScreenPY]];
    YellowBug[] => HandleYellow[];
    TTY.CharsAvailable[keyboard] # 0 => ReadKeyboard[];
    ENDCASE => ChangeCursor[];
	FOR w: CARDINAL IN [1..noWindows) DO 
		IF windows[w].command.count > 50 THEN BackUp[w];
		ENDLOOP;
    ENDLOOP;
END;

badCommand: BOOLEAN ← FALSE;

Command: PROCEDURE RETURNS[BOOLEAN] = 
BEGIN
image: Image;
EraseMessage[];
MousePoint[];
[ScreenPX, ScreenPY] ← WorldToUser[screen, MousePX, 808 - MousePY];
image ← FindCommand[ScreenPX, ScreenPY];
IF image.command # NullProc THEN {
	image.command[image.rect]; 
	WHILE AnyBug[] DO NULL; ENDLOOP;
	RETURN[TRUE]};
RETURN[FALSE];
END;

HandleRed: PROCEDURE = 
BEGIN
w: CARDINAL;
top: REAL;
IF Command[] THEN RETURN;
w ← FindWindow[ScreenPY];
IF w = 0 THEN RETURN;
Select[w];
top ← windows[w].top-header-40;
IF ScreenPX > 606 THEN RETURN;
SELECT TRUE FROM
    Control[] => windows[w].command.redbug[windows[w].score];
    Shift[] => windows[w].command.redbug[windows[w].score];
    ScreenPX < leftMargin => Scroll[w, Real.FixI[top-ScreenPY]];
    ScreenPX > rightMargin => NULL; -- MoveWindow[ScreenPY];
    ENDCASE => windows[w].command.redbug[windows[w].score];
END;

HandleBlue: PROCEDURE[w: CARDINAL] = 
BEGIN ENABLE Piece.Overflow => IF windows[w].score = old THEN windows[w].score ← new;
top: REAL;
IF Command[] THEN RETURN;
IF w = 0 THEN RETURN;
Select[w];
IF ScreenPX > 606 THEN RETURN;
top ← windows[w].top-header-40;
SELECT TRUE FROM
    Control[] => windows[w].command.bluebug[windows[w].score];
    Shift[] => windows[w].command.bluebug[windows[w].score];
    ScreenPX < leftMargin => Scroll[w, Real.FixI[ScreenPY-top]];
    ScreenPX > rightMargin => NULL; -- NewWindow[w, ScreenPY];
    ENDCASE => windows[w].command.bluebug[windows[w].score];
END;

HandleYellow: PROCEDURE = 
BEGIN
w: CARDINAL;
IF Command[] THEN RETURN;
w ← FindWindow[ScreenPY];
IF w = 0 THEN RETURN;
Select[w];
IF ScreenPX > 606 THEN RETURN;
SELECT TRUE FROM
    Control[] => windows[w].command.yellowbug[windows[w].score];
    Shift[] => windows[w].command.yellowbug[windows[w].score];
    ScreenPX < leftMargin => windows[w].command.thumb[windows[w].score];
    ScreenPX > rightMargin => NULL; -- SubWindow[w, ScreenPY];
    ENDCASE => windows[w].command.yellowbug[windows[w].score];
END;

ReadKeyboard: PROCEDURE = 
BEGIN
ENABLE BEGIN
	String.InvalidNumber => BEGIN 
		DisplayMessage["illegal number- type DEL to continue"];
		badCommand ← TRUE; 
		CONTINUE; END;
	TTY.Rubout => BEGIN 
		DisplayMessage["command terminated"];
		CONTINUE; END;
	Piece.Overflow => IF windows[selectedWindow].score = old 
		THEN windows[selectedWindow].score ← new;
	END;
EraseMessage[];
windows[selectedWindow].command.keyboard[windows[selectedWindow].score];
END;

ChangeCursor: PROCEDURE = 
BEGIN
newState: CursorState;
MousePoint[];
[ScreenPX, ScreenPY] ← WorldToUser[screen, MousePX, 808 - MousePY];
IF ScreenPX > 606 THEN RETURN;
SELECT TRUE FROM 
   FindCommand[ScreenPX, ScreenPY].command # NullProc => newState ← command;
   ScreenPY > windows[0].bottom => newState ← commandwindow;
   ScreenPX > rightMargin => newState ← window;
   ScreenPX < leftMargin => newState ← scroll;
   ENDCASE => newState ← screen;
IF newState = currentState THEN RETURN;
IF currentState = screen THEN oldCursor ← GetCursorPattern[];
SELECT newState FROM 
   window => SetCursorPattern[windowCursor];
   command => SetCursorPattern[bullseyeCursor];
   scroll => SetCursorPattern[scrollUpDownCursor];
   commandwindow => SetCursorPattern[textCursor];
   ENDCASE => SetCursorPattern[oldCursor];
currentState ← newState;
END;

oldCursor: Cursor ← textCursor;
CursorState: TYPE = {window, scroll, command, screen, commandwindow};
currentState: CursorState ← screen;

Scroll: PROCEDURE[w: CARDINAL, y: INTEGER] = 
BEGIN
IF y > 0 THEN SetCursorPattern[scrollUpCursor];
IF y < 0 THEN SetCursorPattern[scrollDownCursor];
windows[w].command.scroll[windows[w].score, y];
SetCursorPattern[scrollUpDownCursor];
END;

MousePoint: PROCEDURE = 
BEGIN
x, y: REAL = 1;
MousePX ← x*mouse.x;
MousePY ← y*mouse.y;
END;

MousePX, MousePY: REAL;
ScreenPX, ScreenPY: REAL;


-- ******************************************************************
-- Window & Subwindow procedures
-- ******************************************************************

DrawScreen: PROCEDURE = 
BEGIN
i: CARDINAL;
FOR i IN [0..noWindows) DO windows[i].command.display[windows[i].score, TRUE] ENDLOOP;
END;

MoveWindow: PROCEDURE[y: REAL] = 
BEGIN
i, a, w: CARDINAL ← 0;
dist, last: REAL ← 1000;
SetCursorPattern[moveWindowCursor];
FOR i IN [1..noWindows) DO 
    IF (windows[i].top-y < dist OR y-windows[i].top < dist)
       AND FindWindow[windows[i].top +1] # 0
       THEN BEGIN dist ← ABS[windows[i].top-y]; w ← i; END;
    ENDLOOP;
IF w = 0 THEN RETURN;
a ← FindWindow[windows[w].top +1];
DisplayHeader[white, w];
WHILE RedBug[] DO NULL; ENDLOOP;
MousePoint[];
[ScreenPX, ScreenPY] ← WorldToUser[screen, MousePX, 808 - MousePY];
y ← ScreenPY;
last ← windows[w].top;
IF y > windows[a].top - 16 THEN y ← windows[a].top - 16;
IF y < windows[w].bottom + 16 THEN y ← windows[w].bottom + 16;
MoveContext[w, y];
windows[a].bottom ← y;
windows[w].top ← y;
SetClipper[a];
SetClipper[w];
DisplayHeader[black, w];
IF last > windows[w].top THEN windows[a].command.display[windows[a].score, TRUE];
windows[w].command.display[windows[w].score, TRUE];
Select[w];
END;

NewWindow: PROCEDURE[w: CARDINAL, y: REAL] = 
BEGIN  -- called by bluebug in the righthand margin
SetCursorPattern[newWindowCursor];
WHILE BlueBug[] AND NOT YellowBug[] DO NULL; ENDLOOP;
IF YellowBug[] THEN BEGIN DeleteWindow[w]; RETURN; END;
DefaultWindow[noWindows, y, windows[w].bottom];
windows[w].bottom ← y;
Select[noWindows];
DisplayHeader[black, noWindows];
SetClipper[w];
SetClipper[noWindows];
noWindows ← noWindows + 1;
END;

SubWindow: PROCEDURE[w: CARDINAL, y: REAL] = 
BEGIN -- yellowbug in the righthand margin
SetCursorPattern[subWindowCursor];
WHILE NOT BlueBug[] AND YellowBug[] DO NULL; ENDLOOP;
IF BlueBug[] THEN BEGIN DeleteWindow[w]; RETURN; END;
Flash[NIL];
END;

FindWindow: PROCEDURE[y: REAL] RETURNS[CARDINAL] = 
BEGIN
i: CARDINAL;
FOR i IN [0..noWindows) DO 
    IF y < windows[i].top AND y > windows[i].bottom THEN RETURN[i];
    ENDLOOP;
RETURN[0];
END;

DeleteWindow: PROCEDURE[w: CARDINAL] = 
BEGIN
neighbor: CARDINAL;
SetCursorPattern[windowCursor];
Invert[windows[w].context];
WHILE BlueBug[] AND YellowBug[] DO NULL; ENDLOOP;
Wait[1];
Invert[windows[w].context];
IF BlueBug[] OR YellowBug[] THEN RETURN;
neighbor ← FindWindow[windows[w].top + 1];
IF neighbor # 0 
    THEN BEGIN
	windows[neighbor].bottom ← windows[w].bottom;
	DisplayHeader[white, w];
	END
    ELSE BEGIN
	neighbor ← FindWindow[windows[w].bottom - 1];
	IF neighbor # 0 THEN BEGIN
	   MoveContext[neighbor, windows[w].top];
	   DisplayHeader[white, neighbor];
	   windows[neighbor].top ← windows[w].top;
	   DisplayHeader[black, neighbor];
	   END;
	END;
IF neighbor = 0 
    THEN BEGIN
	DisplayMessage["cannot delete that window"];
	RETURN;
	END;
noWindows ← noWindows - 1;
Heap.FreeMDSString[, windows[w].file];
windows[w] ← windows[noWindows];
SetClipper[neighbor];
Select[neighbor];
windows[neighbor].command.display[windows[neighbor].score, TRUE];
END;

Invert: PROCEDURE[dc: Context] = 
BEGIN
SetStipple[dc, black];
[] ← SetPaintMode[dc, invert];
DrawBox[dc, [0, 0, 1024, 808]];
[] ← SetPaintMode[dc, opaque];
END;

MoveContext: PROCEDURE[w: CARDINAL, y: REAL] = 
BEGIN
siy, soy, tiy, toy: REAL;
siy ← y;
tiy ← windows[w].top;
[, soy] ← Map[screen, windows[w].context, 0, siy];
[, toy] ← Map[screen, windows[w].context, 0, tiy];
Translate[windows[w].context, 0, soy- toy];
END;

Select: PROCEDURE[w: CARDINAL] = 
BEGIN
SetBrush[black, transparent];
DrawBox[screen, [20, windows[selectedWindow].top- header- 10, 
   28, windows[selectedWindow].top- header]];
SetBrush[grey, opaque];
DrawBox[screen, [20, windows[w].top- header- 10, 28, windows[w].top- header]];
selectedWindow ← w;
END;

selectedWindow: CARDINAL ← 1;
listenWindow: CARDINAL ← 1;

DefaultWindow: PROCEDURE[w: CARDINAL, top, bottom: REAL] = 
BEGIN
windows[w] ← [, , , unknown, @unknownProcs, top, bottom];
windows[w].context ← CopyContext[screen];
windows[w].file ← Heap.MakeMDSString[, 40];
Translate[windows[w].context, 30, top - header - 40]; -- 30 regular, 45 for videotaping
SetClipper[w];
SetStipple[windows[w].context, black];
[] ← SetPaintMode[windows[w].context, opaque];
DrawBox[windows[w].context, GetBounds[windows[w].context]];
windows[w].type ← logical;
windows[w].command ← @Screen.commands;
windows[w].score ← windows[w].command.initialize[windows[w].context];
END;


-- ******************************************************************
-- Command Window procedures
-- ******************************************************************

DisplayCommandWindow: PROCEDURE[score: ScorePTR, erase: BOOLEAN] = 
BEGIN
i: CARDINAL;
Erase[NIL, TRUE];
SetBrush[black, opaque];
ClearManager[];
DisplayCommand["FileIn", 30, 750, FileIn];
DisplayCommand["PlayBack", 110, 750, Play];
DisplayCommand["Print", 215, 750, Hardcopy];
DisplayCommand["Quit", 300, 750, Quit];
DisplayCommand["Record", 385, 750, Listen];
DisplayCommand["FileOut", 480, 750, FileOut];
FOR i IN [1..noWindows) DO DisplayHeader[black, i] ENDLOOP;
Select[1];
END;

DisplayCommand: PROCEDURE[s: LONG STRING, x, y: REAL, proc: PROCEDURE[r: Box]] = 
BEGIN
box: Box;
SetCP[screen, x, y];
SetBrush[black, invert];
DrawRope[screen, LOOPHOLE[s]];
[box.xmin, box.ymin, box.xmax, box.ymax] ← RopeBox[font, LOOPHOLE[s]];
box.xmin ← box.xmin + x;
box.ymin ← box.ymin + y;
box.xmax ← box.xmax + x;
box.ymax ← box.ymax + y;
IF box.xmax - box.xmin < 30 THEN box.xmax ← box.xmin + 30;
IF box.ymax - box.ymin < 10 THEN box.ymax ← box.ymin + 10;
AddCommand[box, proc];
END;

DisplayHeader: PROCEDURE[color: CARDINAL, w: CARDINAL] = 
BEGIN
top: REAL ← windows[w].top;
px, py: REAL;
px ← 30;
py ← windows[w].top - 16;
SetBrush[color, opaque];
DrawBox[screen, [0, top-header, 620, top]];
DeleteCommand[px, py];
IF color = grey  THEN SetBrush[grey, invert];
IF color = black THEN SetBrush[black, invert];
IF color = white THEN RETURN;
DisplayCommand[windows[w].file, 30, top-12, EditFileName];
END;

EraseMessage: PROCEDURE = 
BEGIN
SetBrush[white, opaque];
DrawBox[screen, [0, 763, 620, 780]];
SetBrush[black, invert];
END;

DisplayMessage: PUBLIC PROCEDURE[s: STRING] = 
BEGIN
ls: LONG STRING ← s;
IF s = NIL THEN {EraseMessage[]; RETURN};
SetBrush[black, opaque];
DrawBox[screen, [0, 765, 620, 780]];
SetBrush[black, invert];
SetCP[screen, 300 - RopeWidth[font, LOOPHOLE[ls]].xw/2, 767];
DrawRope[screen, LOOPHOLE[ls]];
END;

AddCommand: PROCEDURE[r: Box, proc: PROCEDURE[r: Box]] = 
BEGIN
FOR i: CARDINAL IN [0..mIndex) DO
	IF manager[i].command = proc THEN RETURN;
	ENDLOOP;
manager[mIndex] ← [r, proc];
IF proc = Play THEN playIndex ← mIndex;
IF proc = Listen THEN listenIndex ← mIndex;
mIndex ← mIndex + 1;
END;

DeleteCommand: PROCEDURE[x, y: REAL] = 
BEGIN
i: CARDINAL;
FOR i IN [0..mIndex) DO
    IF manager[i].rect.xmin = x THEN
	BEGIN
	mIndex ← mIndex - 1;
	manager[i] ← manager[mIndex];
	END; ENDLOOP;
END;

FindCommand: PROCEDURE[x, y: REAL] RETURNS[m: Image] = 
BEGIN
i: CARDINAL;
FOR i IN [0..mIndex) DO
    BEGIN
    m ← manager[i];
    IF x > m.rect.xmin AND x < m.rect.xmax AND
       y > m.rect.ymin AND y < m.rect.ymax  
       THEN RETURN[m];
    END; ENDLOOP;
m.command ← NullProc;
RETURN[m];
END;

Flash: PROCEDURE[score: ScorePTR] = 
BEGIN
Invert[windows[selectedWindow].context];
Invert[windows[selectedWindow].context];
END;

Erase: PROCEDURE[score: ScorePTR, erase: BOOLEAN] = 
BEGIN
SetStipple[screen, white];
[] ← SetPaintMode[screen, opaque];
DrawBox[screen, GetBounds[screen]];
SetStipple[screen, black];
[] ← SetPaintMode[screen, transparent];
END;

-- ******************************************************************
-- Commands
-- ******************************************************************

Confirm: PROCEDURE[w: CARDINAL, s: STRING] RETURNS[BOOLEAN] = 
BEGIN
confirm: BOOLEAN;
IF windows[w].command.count = 0 THEN RETURN[TRUE];
DisplayMessage[s];
confirm ← TTY.GetChar[keyboard] = CR;
EraseMessage[];
RETURN[confirm];
END;

FileIn: PROCEDURE[r: Box] = 
BEGIN
ENABLE Directory.Error => BEGIN 
	DisplayMessage["no such file"]; 
	DrawBox[screen, r]; 
	CONTINUE; END;
s: CARDINAL ← selectedWindow;
SetBrush[black, invert];
DrawBox[screen, r];
IF Confirm[s, "the current file has not been filed out-- type CR to confirm"] THEN
	BEGIN
	IF windows[s].score # NIL THEN Piece.Free[windows[s].score];
	windows[s].score ← windows[s].command.fileIn[windows[s].file];
	EraseMessage[];
	windows[s].command.display[windows[s].score, TRUE];
	windows[s].command.count ← 0;
	END;
DrawBox[screen, r];
END;

FileOut: PROCEDURE[r: Box] = 
BEGIN
ENABLE Directory.Error => BEGIN 
	DisplayMessage["illegal file name"]; 
	DrawBox[screen, r];
	CONTINUE; END;
s: CARDINAL ← selectedWindow;
SetBrush[black, invert];
DrawBox[screen, r];
windows[s].command.fileOut[windows[s].score, windows[s].file];
windows[s].command.count ← 0;
DrawBox[screen, r];
END;

BackUp: PROCEDURE[w: CARDINAL] = 
BEGIN
DisplayMessage["Please wait... storing file on backup.music"];
windows[w].command.fileOut[windows[w].score, "backup.music"];
windows[w].command.count ← 1; -- so it will look dirty
EraseMessage[];
END;

Play: PROCEDURE[r: Box] = 
BEGIN ENABLE Piece.Overflow => IF windows[selectedWindow].score = old 
	THEN windows[selectedWindow].score ← new;
windows[selectedWindow].command.play[windows[selectedWindow].score];
END;

InvertPlay: PUBLIC PROCEDURE = 
BEGIN
r: Box = manager[playIndex].rect;
SetBrush[black, invert];
DrawBox[screen, r];
END;

Listen: PROCEDURE[r: Box] = 
BEGIN ENABLE Piece.Overflow => IF windows[listenWindow].score = old 
	THEN windows[listenWindow].score ← new;
windows[listenWindow].command.listen[windows[listenWindow].score];
listenWindow ← selectedWindow;
END;

InvertListen: PUBLIC PROCEDURE = 
BEGIN
r: Box = manager[listenIndex].rect;
SetBrush[black, invert];
DrawBox[screen, r];
END;

playIndex: CARDINAL ← 0;
listenIndex: CARDINAL ← 0;

Hardcopy: PROCEDURE[r: Box] = 
BEGIN
SetBrush[black, invert];
DrawBox[screen, r];
DisplayMessage["Please wait... printing screen on music.press"];
windows[selectedWindow].command.hardcopy[windows[selectedWindow].score, "music.press"];
DrawBox[screen, r];
EraseMessage[];
END;

EditFileName: PROCEDURE[r: Box] = 
BEGIN
w: CARDINAL ← FindWindow[ScreenPY];
c, last: CHARACTER ← 040C;
CR: CHARACTER ← 015C;
IF w = 0 THEN RETURN;
windows[w].file.length ← 0;
DisplayMessage["Please enter filename."];
DisplayHeader[white, w];
DisplayHeader[black, w];
DO BEGIN
	c ← TTY.GetChar[keyboard];
	IF c = 177C OR c = 010C THEN 
		BEGIN
		IF windows[w].file.length = 0 THEN LOOP;
		windows[w].file.length ← windows[w].file.length- 1; 
		DisplayHeader[black, w];
		LOOP; END;
	IF c = ' THEN BEGIN 
		windows[w].file.length ← 0; 
		DisplayHeader[black, w];
		LOOP; END;
	IF c # CR AND c # '. AND c # 033C THEN BEGIN 
		String.AppendChar[windows[w].file, c];
		DrawChar[screen, c]; LOOP;
		END;
	String.AppendString[windows[w].file, ".music"];
	windows[w].type ← logical;
	EXIT; END; 
	ENDLOOP;
DisplayHeader[black, w];
EraseMessage[];
Select[w];
END;

Quit: PROCEDURE[r: Box] = 
BEGIN
IF NOT Confirm[1, "type CR to confirm Quit"] THEN RETURN;
-- Score.StopPlaying[]; 
-- Score.StopListening[]; 
END;

ClearManager: PROCEDURE = 
BEGIN
mIndex ← 0;
END;   

manager: ARRAY [0..20) OF Image;
Image: TYPE = RECORD[rect: Box, command: PROCEDURE[r: Box]];
mIndex: CARDINAL ← 0;
mMax: CARDINAL = 20;

-- ******************************************************************
-- Assorted
-- ******************************************************************

SetBrush: PROCEDURE[tex: CARDINAL, pnt: Graphics.PaintMode] = INLINE
   {Graphics.SetStipple[screen, tex]; [] ← Graphics.SetPaintMode[screen, pnt]};

SetClipper: PROCEDURE[w: CARDINAL] = 
BEGIN
box: Box;
[box.xmin, box.ymin] ← Map[screen, windows[w].context, 0, windows[w].bottom];
[box.xmax, box.ymax] ← Map[screen, windows[w].context, 650, windows[w].top - header];
ClipBox[windows[w].context, box];
END;

Wait: PROCEDURE[t: CARDINAL] = 
BEGIN
-- now: LONG CARDINAL ← MiscDefs.CurrentTime[];
-- WHILE MiscDefs.CurrentTime[] < now + t DO NULL; ENDLOOP;
END;

screen: PUBLIC Context;
leftMargin: CARDINAL = 5;
rightMargin: CARDINAL = 585;
header: CARDINAL = 15;
CR: CHARACTER = 015C;

windows: ARRAY [0..10) OF Window;
noWindows: CARDINAL ← 2;
Window: TYPE = RECORD[context: Context, file: STRING, 
          score: ScorePTR ← NIL,
		   type: WType, command: POINTER TO CommandProcs, 
		   top, bottom: REAL];
WType: TYPE = {physical, graphical, logical, command, unknown};
commandProcs: CommandProcs;
unknownProcs: CommandProcs ← [Flash, Flash, Flash, Flash, Flash, Flash, , Flash, Erase, , , , , 0];
NullProc: PROCEDURE[r: Box] = BEGIN END;

Initialize[];

END.




ReadCommandLine: PROCEDURE = 
BEGIN
ENABLE Directory.Error => BEGIN 
	DisplayMessage["no such file"]; 
	CONTINUE; END;
char: CHARACTER;
CR: CHARACTER = 015C;
filename: STRING ← [40];
ending: STRING ← [12];
test, error: BOOLEAN ← FALSE;
hardcopy: BOOLEAN ← FALSE;
command, second, period: BOOLEAN ← FALSE;
inputStream: DiskHandle;
inputStream ← NewByteStream["com.cm", Read];
inputStream.reset[inputStream];
DO IF inputStream.endof[inputStream] THEN EXIT;
	char ← inputStream.get[inputStream];
	-- command processing
	IF command THEN SELECT char FROM
		'/ => EXIT;
		'n => NULL;
		't => test ← TRUE;
		'h => hardcopy ← TRUE;
		'  => command ← FALSE;
		ENDCASE => NULL;
	IF char = '/ THEN command ← TRUE;
	IF command THEN LOOP;
	-- filename processing
	SELECT TRUE FROM
		char = CR OR char = ' => NULL;
		inputStream.endof[inputStream] => NULL;
		char = '. => IF period THEN EXIT ELSE {period ← TRUE; LOOP};
		period => {AppendChar[ending, char]; LOOP};
		ENDCASE => {AppendChar[filename, char]; LOOP};
	-- we have a complete filename 
	IF filename.length # 0 
	AND ~EqualString[ending, "image"] 
	AND ~EqualString[ending, "bcd"] THEN {
		AppendString[filename, ".music"];
		windows[1].file.length ← 0;
		AppendString[windows[1].file, filename];
		DisplayHeader[black, 1];
		DisplayMessage["Please wait-- retrieving file"];
		-- IODefs.WriteLine[filename];
		windows[1].command.fileIn[filename, second];
		EraseMessage[];
		windows[1].command.display[windows[1].score, TRUE];
		-- command line switches (per file)
		IF test AND Score.Test[] THEN {error ← TRUE; EXIT};
		IF hardcopy THEN Hardcopy[[[215, 747], [249, 761]]];
		second ← TRUE};
	filename.length ← 0;
	ending.length ← 0;
	period ← FALSE;
	ENDLOOP;
inputStream.destroy[inputStream];
-- command line switches (at completion)
IF test AND ~error THEN Quit[[[0, 0], [0, 0]]]; 
IF hardcopy THEN Quit[[[0, 0], [0, 0]]]; 
END;