InterfaceImplA.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 22, 1983 10:14 am
Edited by Doug Wyatt, June 15, 1984 10:32:50 am PDT
DIRECTORY
CursorDefs,
Graphics USING [DrawChar, Context, DrawRope, Map, SetCP, WorldToUser, SetDefaultFont, SetPaintMode, SetStipple],
Heuristic USING [MakeBeams, MakeChords, MakeSyncs, SetNoteValues],
Interface USING [count, DeleteGraphical, Flash, MoveNote, MoveGraphical, Object, Wait],
MusicDefs,
Piece USING [AddEvent, CleanUpNotes, Copy, Merge, NearestNote, New, Overflow, Replace],
Real USING [Fix, FixI],
Score USING [
Draw, FileIn, FileOut, Initialize, Look, Print, Redraw,
SetKey, SetMetrenome, SetStyle, SetTimeSignature, ShowLogical,
StartListening, StartPlaying, StopListening, StopPlaying, Test],
Screen USING [CommandProcs, DisplayMessage, screen],
Selection, -- USING everything
Sheet USING [
FindLine, LineNumber, Map, NearestTime,
Reset, ScreenPoint, Scroll, SetBegin],
String USING [AppendString, UpperCase],
TTY USING [Create, GetChar, GetDecimal, GetID, Handle, Rubout],
UserTerminal USING [cursor, GetCursorPattern, SetCursorPattern],
Utility, -- USING everything
Voice USING [Check, Set];
InterfaceImplA: CEDAR MONITOR
IMPORTS
Graphics, Heuristic, Interface, MusicDefs, Piece, Real,
Score, Screen, Selection, Sheet, String, TTY, UserTerminal, Utility, Voice
EXPORTS Screen =
BEGIN
OPEN Graphics, MusicDefs, UserTerminal;
keyboard: TTY.Handle;
Error: SIGNAL;
test: BOOLFALSE;
commands: PUBLIC Screen.CommandProcs ← [Play, Listen, HandleRed, HandleYellow, HandleBlue, HandleKeyboard, Scroll, Thumb, Score.Draw, Score.FileIn, FileOut, Hardcopy, Initialize, 0];
******************************************************************
Initialization
******************************************************************
Initialize: PROC[context: Graphics.Context] RETURNS[score: ScorePTR] = {
keyboard ← TTY.Create[NIL];
score ← Piece.New[1000, TRUE];
score.beamHeap ← Utility.NewSegment[SIZE[BeamHeapRec[100]], 100, SIZE[BeamHeapRec[0]]-1];
IF score.beamHeap.max # 100 THEN ERROR;
score.chordHeap ← Utility.NewSegment[SIZE[ChordHeapRec[100]], 100, SIZE[ChordHeapRec[0]]-1];
IF score.chordHeap.max # 100 THEN ERROR;
Score.Initialize[score, context];
};
******************************************************************
Command parser
******************************************************************
HandleKeyboard: PROC[score: ScorePTR] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
ClearDirty[score];
Do[score, TTY.GetChar[keyboard]];
IF test AND Score.Test[score] THEN Error; -- you may proceed, but you cannot file out
};
HandleBlue: PROC[score: ScorePTR] = { OPEN Interface;
ENABLE Piece.Overflow => IF score = old THEN score ← new;
ClearDirty[score];
count ← FALSE;
SELECT TRUE FROM
Control[] => {HandleMenu[]; count ← FALSE};
Shift[] => {Selection.Clear[];
WHILE BlueBug[] DO Interface.MoveNote[score, Piece.NearestNote[score]]; ENDLOOP};
ENDCASE => Interface.MoveGraphical[score, defaultObject];
IF test AND Score.Test[score] THEN Error; -- you may proceed, but you cannot file out
IF count THEN commands.count ← commands.count+1;
};
HandleYellow: PROC[score: ScorePTR] = {
used to delete objects or unselect notes
OPEN CursorDefs, Interface;
temp: Cursor ← GetCursorPattern[];
SetCursorPattern[textCursor];
ClearDirty[score];
count ← FALSE;
SELECT TRUE FROM
score.sheet.scale > 3 => ChangeLook[score, 'O];
Shift[] AND Control[] => NULL;
Control[] => NULL;
ENDCASE => Interface.DeleteGraphical[score];
SetCursorPattern[temp];
WHILE YellowBug[] DO NULL; ENDLOOP;
IF test AND Score.Test[score] THEN Error; -- you may proceed, but you cannot file out
IF count THEN commands.count ← commands.count+1;
};
HandleRed: PROC[score: ScorePTR] = {
Only used to change the selection
OPEN CursorDefs;
x, y: INTEGER;
i, click: CARDINAL ← 0;
temp: Cursor ← GetCursorPattern[];
SetCursorPattern[textCursor];
ClearDirty[score];
WHILE RedBug[] DO
[x, y] ← Sheet.ScreenPoint[score.sheet];
SELECT TRUE FROM
Control[] => {
FOR i IN [0..5) DO Interface.Wait[1];
IF ~RedBug[] THEN {click ← 1; EXIT};
ENDLOOP;
IF click = 1 THEN FOR i IN [0..5) DO Interface.Wait[1];
IF RedBug[] THEN {click ← 2; EXIT};
ENDLOOP;
SELECT click FROM
0 => ChooseLine[score, Sheet.NearestTime[score.sheet, x, y].time];
1 => EXIT;
2 => ExtendLine[score];
ENDCASE => ERROR};
Shift[] => Selection.AddNote[score, Piece.NearestNote[score, x, y]];
ENDCASE => {IF score.command THEN Selection.Clear[];
Selection.AddNote[score, Piece.NearestNote[score, x, y]]};
score.command ← FALSE;
ENDLOOP;
SetCursorPattern[temp];
IF test AND Score.Test[score] THEN Error; -- you may proceed, but you cannot file out
};
ClearDirty: PROC[score: ScorePTR] = INLINE {
score.sheet.dirty1 ← 100000;
score.sheet.dirty2 ← -1};
ExtendLine: PROC[score: ScorePTR] = {
time: Time;
temp, gTemp: ScorePTR;
grey: BOOLFALSE;
switch: BOOLTRUE;
beginning: BOOLFALSE;
temp1, temp2, gTemp1, gTemp2: Time;
temp ← Selection.selection.score;
gTemp ← Selection.selection.score2;
temp1 ← Selection.selection.select1;
temp2 ← Selection.selection.select2;
gTemp1 ← Selection.selection.greySelect1;
gTemp2 ← Selection.selection.greySelect2;
WHILE RedBug[] DO
is the user switching between colors?
IF Shift[] AND NOT grey THEN
{
grey ← TRUE;
switch ← TRUE;
Selection.AddLine[temp, temp1, temp2];
};
IF NOT Shift[] AND grey THEN
{
grey ← FALSE;
switch ← TRUE;
Selection.AddGreyLine[gTemp, gTemp1, gTemp2];
};
[time] ← Sheet.NearestTime[score.sheet];
if the user is switching, then which end does he want?
IF switch THEN IF grey
THEN beginning ← Beginning[time, Selection.selection.greySelect1, Selection.selection.greySelect2]
ELSE beginning ← Beginning[time, Selection.selection.select1, Selection.selection.select2];
switch ← FALSE;
draw the appropriate line
IF grey
THEN IF beginning
THEN Selection.AddGreyLine[score, time, Selection.selection.greySelect2]
ELSE Selection.AddGreyLine[score, Selection.selection.greySelect1, time]
ELSE IF beginning
THEN Selection.AddLine[score, time, Selection.selection.select2]
ELSE Selection.AddLine[score, Selection.selection.select1, time]
ENDLOOP;
};
Beginning: PROC[time, begin, end: Time] RETURNS[BOOL] = INLINE {
IF time <= begin THEN RETURN[TRUE];
IF time >= end THEN RETURN[FALSE];
RETURN[ABS[time-begin] < ABS[time-end]];
};
ChooseLine: PROC[score: ScorePTR, time: Time] = {
time2: Time;
temp, gTemp: ScorePTR;
temp1, temp2, gTemp1, gTemp2: Time;
grey: BOOLFALSE;
temp ← Selection.selection.score;
temp1 ← Selection.selection.select1;
temp2 ← Selection.selection.select2;
gTemp ← Selection.selection.score2;
gTemp1 ← Selection.selection.greySelect1;
gTemp2 ← Selection.selection.greySelect2;
WHILE RedBug[] DO
IF Shift[] AND NOT grey THEN
{
grey ← TRUE;
Selection.AddLine[temp, temp1, temp2];
};
IF NOT Shift[] AND grey THEN
{
grey ← FALSE;
Selection.AddGreyLine[gTemp, gTemp1, gTemp2];
};
[time2] ← Sheet.NearestTime[score.sheet];
IF grey THEN Selection.AddGreyLine[score, time, time2] ELSE Selection.AddLine[score, time, time2];
ENDLOOP;
};
************************************************************************
commands from the keyboard
************************************************************************
Do: PROC[score: ScorePTR, c: CHARACTER] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
a, b: INTEGER;
SELECT c FROM
'b => IF Selection.selection.lineSelect
THEN Heuristic.MakeBeams[score, Selection.selection.select1, Selection.selection.select2]
ELSE Selection.MakeBeam[];
'B => Selection.ClearBeam[];
002C => Selection.MakeBeamOfBeams[];
'c => IF Selection.selection.lineSelect THEN
Heuristic.MakeChords[score, Selection.selection.select1, Selection.selection.select2] ELSE
Selection.MakeChord[];
'C => Selection.ClearChord[];
'd => Selection.Delete[];
'e => {Selection.AddLine[score, 0, EndOfScore[score]]; RETURN};
'f => {ReadFileName[".logical"]; Score.FileInOld[fileName]};
'F => {ReadFileName[".logical"]; Score.FileOutOld[fileName]};
'g => GuessNoteValues[select1, Selection.selection.select2];
'g => Selection.SetGrace[TRUE];
'G => Selection.SetGrace[FALSE];
007C => Heuristic.SetNoteValues[score, Selection.selection.select1, Selection.selection.select2];
'k => Score.SetKey[score, Selection.selection.select1, Selection.selection.select2, ReadKey[score]];
'l => {ChangeLook[score, TTY.GetChar[keyboard]]; RETURN};
014C => {Score.ShowLogical[score, 0, score.length-1]; Sheet.Reset[score]}; -- debugging aid
'm => Score.SetMetrenome[score, Selection.selection.select1, Selection.selection.select2, MAX[16, ReadNumbers["metrenome: < m > CR"].a]];
'n => {[a, b] ← ReadNumbers["n-tuplet: < n > CR < m > CR", TRUE]; Selection.MakeNTuplet[b, a]};
'N => Selection.HideNTuplets[TRUE];
016C => {[a, b] ← ReadNumbers["n-tuplet: < n > CR < m > CR", TRUE];
Selection.MakeNTupletOfBeams[b, a]};
'p => Selection.Transpose[ReadNumbers["transpose: +/- < n > CR"].a];
'r => IF Selection.selection.lineSelect
THEN Piece.Replace[Selection.selection.score, Piece.Copy[Selection.selection.score2, Selection.selection.greySelect1, Selection.selection.greySelect2], Selection.selection.select1, Selection.selection.select2]
ELSE Selection.SetRest[TRUE];
'R => IF ~Selection.selection.lineSelect THEN Selection.SetRest[FALSE] ELSE score.flash ← TRUE;
's => IF Selection.selection.lineSelect THEN
Heuristic.MakeSyncs[score, Selection.selection.select1, Selection.selection.select2] ELSE
Selection.MakeSync[];
'S => Selection.ClearSync[];
't => Selection.MakeTie[];
'T => Selection.ClearTie[];
'x => {Voice.Check[score]; RETURN};
'v => Voice.Set[score, ReadDigit[score]];
' => {insertMeasure ← TRUE; RETURN};
values, dotted values, doubly dotted values, triply dotted values
'0 => Selection.SetNoteValue[unknown, 0];
'1 => Selection.SetNoteValue[whole, 0]; '! => Selection.SetNoteValue[whole, 1];
'2 => Selection.SetNoteValue[half, 0]; '@ => Selection.SetNoteValue[half, 1];
'4 => Selection.SetNoteValue[quarter, 0]; '$ => Selection.SetNoteValue[quarter, 1];
'8 => Selection.SetNoteValue[eighth, 0]; '* => Selection.SetNoteValue[eighth, 1];
'6 => Selection.SetNoteValue[sixteenth, 0]; '~ => Selection.SetNoteValue[sixteenth, 1];
'3 => Selection.SetNoteValue[thirtysecond, 0]; '# => Selection.SetNoteValue[thirtysecond, 1];
'7 => Selection.SetNoteValue[sixtyfourth, 0]; '& => Selection.SetNoteValue[sixtyfourth, 1];
'[ => Selection.SetStaff[ReadDigit[score]];
'^ => Selection.SetStem[TRUE];
'← => Selection.SetStem[FALSE];
'/ => {[a, b] ← ReadNumbers["time signature: < a > CR < b > CR", TRUE];
Score.SetTimeSignature[score, [a, b], Selection.selection.select1, Selection.selection.select2]};
033C => {Selection.Clear[]; RETURN};
ENDCASE => RETURN;
score.command ← TRUE;
IF score.flash THEN {Interface.Flash[score]; RETURN};
commands.count ← commands.count+1;
IF Selection.selection.lineSelect AND score.sheet.dirty1 > score.sheet.dirty2
THEN Score.Redraw[score, Selection.selection.select1, Selection.selection.select2]
ELSE Score.Redraw[score, score.sheet.dirty1, score.sheet.dirty2];
};
ChangeLook: PROC[score: ScorePTR, c: CHARACTER] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
count: BOOLFALSE;
repaint: BOOLTRUE;
SELECT c FROM
'a => Score.Look[score, accidental, TRUE];
'A => Score.Look[score, accidental, FALSE];
'c => Score.Look[score, noCarry, FALSE];
'C => Score.Look[score, noCarry, TRUE];
'g => score.flash ← TRUE;
'h => Score.Look[score, hardcopy, TRUE];
'H => Score.Look[score, hardcopy, FALSE];
'j => {Score.Look[score, justified, , ReadDigit[score]]; count ← TRUE};
'l => {Score.Look[score, logical]; count ← TRUE};
'n => Score.Look[score, notehead, TRUE];
'N => Score.Look[score, notehead, FALSE];
'o => Score.Look[score, overview, TRUE];
'O => Score.Look[score, overview, FALSE];
'p => {Score.Look[score, physical, , ReadDigit[score]]; count ← TRUE};
's => {Score.Look[score, sync, TRUE]; repaint ← FALSE};
'S => Score.Look[score, sync, FALSE];
'v => Score.Look[score, voice, TRUE, ReadDigit[score]];
'V => {Score.Look[score, voice, FALSE]; repaint ← FALSE};
177C => TTY.Rubout;
ENDCASE => {Score.SetStyle[score, Digit[score, c], Selection.selection.select1, Selection.selection.select2]; count ← TRUE};
score.command ← TRUE;
IF score.flash THEN {Interface.Flash[score]; RETURN};
Score.Draw[score, repaint];
IF count THEN commands.count ← commands.count+1;
};
ReadKey: PROC[score: ScorePTR] RETURNS[k: INTEGER] = {
key, c: CHARACTER;
accidental: Accidental ← inKey;
CR: CHARACTER = 015C;
Major: BOOLTRUE;
negative: BOOLFALSE;
key may be expressed as [-7..7]
Screen.DisplayMessage["Please enter key name (A, Bb, C # m...) or number."];
key ← TTY.GetChar[keyboard];
IF key = '- THEN {
key ← TTY.GetChar[keyboard];
Screen.DisplayMessage[NIL];
RETURN[-Digit[score, key]]};
IF key IN ['0..'9] THEN {
Screen.DisplayMessage[NIL];
RETURN[Digit[score, key]]};
key must have been named (Am, C # , DM, e, etc.)
key ← String.UpperCase[key];
IF key = 177C THEN TTY.Rubout;
WHILE (c ← TTY.GetChar[keyboard]) # CR DO
SELECT c FROM
'm => Major ← FALSE;
'b => accidental ← flat;
'# => accidental ← sharp;
177C => TTY.Rubout;
ENDCASE;
ENDLOOP;
Screen.DisplayMessage[NIL];
SELECT key FROM
'C => k ← 0; 'D => k ← 2; 'E => k ← 4; 'F => k ← -1;
'G => k ← 1; 'A => k ← 3; 'B => k ← 5;
ENDCASE => {Interface.Flash[score]; RETURN[-100]};
IF accidental = flat THEN k ← k- 7;
IF accidental = sharp THEN k ← k+ 7;
IF NOT Major THEN k ← k- 3;
RETURN[k];
};
ReadFileName: PROC[s: STRING] = {
TTY.GetID[keyboard, fileName ! TTY.Rubout => RETRY];
String.AppendString[fileName, s];
RETURN;
};
fileName: STRING ← [50];
ReadNumbers: PROC[s: STRING, two: BOOLFALSE] RETURNS[a, b: INTEGER] = {
a ← b ← 0;
Screen.DisplayMessage[s];
a ← TTY.GetDecimal[keyboard];
Screen.DisplayMessage[NIL];
IF NOT two THEN RETURN;
Screen.DisplayMessage["and the second number..."];
b ← TTY.GetDecimal[keyboard];
Screen.DisplayMessage[NIL];
};
ReadDigit: PROC[score: ScorePTR] RETURNS[d: CARDINAL] = {
Screen.DisplayMessage["Please enter a digit"];
d ← Digit[score, TTY.GetChar[keyboard]];
Screen.DisplayMessage[NIL];
};
Digit: PROC[score: ScorePTR, c: CHARACTER] RETURNS[CARDINAL] = {
SELECT c FROM
'0 => RETURN[0];
'1 => RETURN[1]; '2 => RETURN[2]; '3 => RETURN[3];
'4 => RETURN[4]; '5 => RETURN[5]; '6 => RETURN[6];
'7 => RETURN[7]; '8 => RETURN[8]; '9 => RETURN[9];
shifted number keys
') => RETURN[10];
'! => RETURN[11]; '@ => RETURN[12]; '# => RETURN[13];
'$ => RETURN[14]; '% => RETURN[15]; '~ => RETURN[16];
'& => RETURN[17]; '* => RETURN[18]; '( => RETURN[19];
033C => RETURN[score.sheet.justification]; -- default for justify
ENDCASE => TTY.Rubout;
RETURN[0];
};
******************************************************************
The menu: change attributes, insertion
******************************************************************
defaultObject: Interface.Object ← none;
HandleMenu: PROC = {
OPEN CursorDefs, Interface;
x, y, i, j, newI, newJ: CARDINAL ← 0;
x ← UserTerminal.cursor.x;
y ← UserTerminal.cursor.y;
DisplayMenu[x+4, y+4];
CursorGetsMenu[0, 0];
WHILE BlueBug[] DO
newI ← (UserTerminal.cursor.x-x)/16;
newJ ← (UserTerminal.cursor.y-y+8)/16;
IF i = newI AND j = newJ THEN LOOP;
i ← newI;
j ← newJ;
CursorGetsMenu[i, j];
ENDLOOP;
DisplayMenu[x+4, y+4];
SELECT j FROM
0 => SELECT i FROM
0 => defaultObject ← note;
1 => defaultObject ← rest;
2 => defaultObject ← staves;
3 => defaultObject ← measure;
4 => defaultObject ← doubleMeasure;
5 => defaultObject ← repeat1;
6 => defaultObject ← repeat2;
7 => defaultObject ← endMeasure;
8 => defaultObject ← treble;
9 => defaultObject ← bass;
10 => defaultObject ← octava;
ENDCASE => defaultObject ← none;
1 => SELECT i FROM
0 => defaultObject ← doubleFlat;
1 => defaultObject ← flat;
2 => defaultObject ← natural;
3 => defaultObject ← inKey;
4 => defaultObject ← sharp;
5 => defaultObject ← doubleSharp;
6 => defaultObject ← trill;
7 => defaultObject ← mordent1;
8 => defaultObject ← mordent2;
ENDCASE => defaultObject ← none;
ENDCASE => defaultObject ← none;
};
CursorGetsMenu: PROC[i, j: CARDINAL] = {
OPEN CursorDefs;
SELECT j FROM
0 => SELECT i FROM
0 => SetCursorPattern[quarter];
1 => SetCursorPattern[rest];
2 => SetCursorPattern[measure];
3 => SetCursorPattern[measure];
4 => SetCursorPattern[doubleMeasure];
5 => SetCursorPattern[repeat1];
6 => SetCursorPattern[repeat2];
7 => SetCursorPattern[endMeasure];
8 => SetCursorPattern[trebleClef];
9 => SetCursorPattern[bassClef];
10 => SetCursorPattern[octava];
ENDCASE => SetCursorPattern[textCursor];
1 => SELECT i FROM
0 => SetCursorPattern[doubleFlat];
1 => SetCursorPattern[flat];
2 => SetCursorPattern[natural];
3 => SetCursorPattern[inKey];
4 => SetCursorPattern[sharp];
5 => SetCursorPattern[doubleSharp];
6 => SetCursorPattern[trill];
7 => SetCursorPattern[mordent1];
8 => SetCursorPattern[mordent2];
ENDCASE => SetCursorPattern[textCursor];
ENDCASE => SetCursorPattern[textCursor];
};
DisplayMenu: PROC[x, y: INTEGER] = {
OPEN Screen;
newX, newY: REAL;
[newX, newY] ← Graphics.WorldToUser[screen, x, 808 - y];
Graphics.SetCP[screen, newX, newY];
[] ← Graphics.SetPaintMode[screen, invert];
Graphics.SetStipple[screen, black];
Graphics.SetDefaultFont[screen, music];
Graphics.DrawRope[screen, "tzffghopRS"]; -- note, rest, measures, clefs
Utility.SetFont[screen, text, 12];
Graphics.DrawChar[screen, '8];
Utility.SetFont[screen, music, 8];
newY ← newY-16;
Graphics.SetCP[screen, newX, newY];
Graphics.DrawRope[screen, "EFG.MOUVW"]; -- accidentals, embellishments
Graphics.SetDefaultFont[screen, text];
};
******************************************************************
Utility procedures
******************************************************************
Hardcopy: PROC[score: ScorePTR, s: STRING] = {Score.Print[score: score, splines: ~score.sheet.hardcopy OR BlueBug[]]};
FileOut: PROC[score: ScorePTR, fileName: STRING] = {
IF NOT Score.FileOut[score, fileName] THEN {
Screen.DisplayMessage["FileOut aborted-- see mesa.typescript"];
Interface.Flash[score]};
};
Play: PROC[score: ScorePTR] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
start: CARDINAL;
IF playing THEN {Score.StopPlaying[score]; RETURN};
ELSE TURN THE PLAYER ON
start ← score.length;
insertMeasure ← FALSE;
find the indices of the selection
IF Selection.selection.select1 < Selection.selection.select2
THEN FOR i: CARDINAL IN [0..score.length) DO
IF start # score.length OR score[i].time < Selection.selection.select1 THEN LOOP;
start ← i;
EXIT; ENDLOOP
ELSE {start ← 0};
should we record any keystrokes?
IF score.sheet.display = physical AND ~listening THEN {
IF temp = NIL THEN temp ← Piece.New[1000];
Score.StartListening[temp];
merge ← TRUE};
start the player
Score.StartPlaying[score, start, score.sheet.display = physical, DisplayCursor];
IF score.flash THEN Interface.Flash[score];
};
temp: ScorePTR ← NIL;
merge: BOOLFALSE;
Listen: PROC[score: ScorePTR] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
IF listening THEN { -- TURN THE LISTENER OFF
Score.StopListening[score];
IF temp[0] = NIL THEN RETURN; -- nothing recorded
IF merge THEN Piece.Merge[score, temp, Selection.selection.select1, LAST[Time]]
ELSE {
portion: ScorePortionPTR ← zone.NEW[ScorePortionRec];
portion.score ← temp;
portion.duration ← 0; -- indicates a score from the synthesizer
Piece.Replace[score, portion, Selection.selection.select1, Selection.selection.select2]};
temp ← NIL;
Piece.CleanUpNotes[score];
Score.Look[score, voice, FALSE];
WHILE AnyBug[] DO NULL; ENDLOOP;
Score.Draw[score: score, erase: ~merge];
commands.count ← 100; -- force a backup
RETURN};
ELSE TURN THE LISTENER ON
IF temp = NIL THEN temp ← Piece.New[1000];
IF score.sheet.justification < 64 THEN score.sheet.justification ← 256;
Score.StartListening[temp];
IF score.flash THEN Interface.Flash[score];
merge ← FALSE;
};
DisplayCursor: PROC[score: ScorePTR, time: Time] = {
sx, sy: REAL;
sync: SyncPTR;
px, py: INTEGER;
IF score.sheet.display = physical THEN time ← time/score.sheet.justification;
IF time < score.sheet.begin THEN RETURN;
[px, py] ← Sheet.Map[score.sheet, time, , 2];
py ← py+64;
[sx, sy] ← Graphics.Map[score.sheet.context, Screen.screen, px, py];
cursor.x ← Real.FixI[sx];
cursor.y ← MAX[808 - Real.FixI[sy], 0];
IF NOT insertMeasure THEN RETURN;
insertMeasure ← FALSE;
sync ← zone.NEW[EventRec.sync];
sync.time ← time-8;
score ← Piece.AddEvent[score, sync];
Score.Redraw[score, time-8, time-8];
};
insertMeasure: BOOLFALSE;
Scroll: PROC[score: ScorePTR, by: INTEGER] = {
time: Time;
x, y: INTEGER;
[x, y] ← Sheet.ScreenPoint[score.sheet];
time ← Sheet.NearestTime[score.sheet, x, y-40].time;
IF by > 0
THEN Sheet.Scroll[score.sheet, MAX[Lines[score, score.sheet.begin, time], 1]]
ELSE Sheet.Scroll[score.sheet, -MAX[Lines[score, score.sheet.begin, time], 1]];
Score.Draw[score];
};
Thumb: PROC[score: ScorePTR] = {
sx, sy: REAL;
endOfScore: Time;
px, py: INTEGER;
oldBegin: Time = score.sheet.begin;
height: INTEGER ← 680;
endOfScore ← EndOfScore[score];
SELECT score.sheet.scale FROM
1 => height ← height;
2 => height ← (3*height)/2;
4 => height ← 4*height;
ENDCASE => ERROR;
WHILE YellowBug[] DO
[px, py] ← Sheet.ScreenPoint[score.sheet];
IF px > -10 THEN RETURN;
py ← -(height*score.sheet.begin)/endOfScore;
[sx, sy] ← Graphics.Map[score.sheet.context, Screen.screen, px, py];
Interface.Wait[1];
cursor.y ← MAX[808 - Real.FixI[sy], 0];
ENDLOOP;
[px, py] ← Sheet.ScreenPoint[score.sheet];
score.sheet.begin ← (Real.Fix[score.sheet.top-py]*endOfScore)/height;
score.sheet.begin ← MIN[score.sheet.begin, endOfScore-60];
score.sheet.begin ← MAX[score.sheet.begin, 0];
Sheet.SetBegin[score.sheet, score.sheet.begin];
IF score.sheet.begin # oldBegin THEN Score.Draw[score];
};
Lines: PROC[score: ScorePTR, time1, time2: Time] RETURNS[CARDINAL] = INLINE {
line, topLine: INTEGER;
line ← Sheet.LineNumber[score.sheet, Sheet.FindLine[score.sheet, time2]];
topLine ← Sheet.LineNumber[score.sheet, Sheet.FindLine[score.sheet, time1]];
RETURN[line-topLine];
};
END.
Thumb: PROC[score: ScorePTR] = {
time: Time;
x, y: INTEGER;
lines: INTEGER;
[x, y] ← Sheet.ScreenPoint[score.sheet];
time ← Sheet.NearestTime[score.sheet, x, y].time;
lines ← Lines[score, score.sheet.begin, time];
SELECT TRUE FROM
lines = 0 AND x < 0 => {Sheet.SetBegin[score.sheet, 0]};
score.sheet.scale = 1 => {Sheet.Scale[4]; Sheet.SetBegin[score.sheet, 0]};
score.sheet.scale = 2 => {Sheet.Scale[4]; Sheet.SetBegin[score.sheet, 0]};
score.sheet.scale = 4 => {
Sheet.Scale[IF score.sheet.hardcopy THEN 2 ELSE 1];
Sheet.SetBegin[score.sheet, time]};
ENDCASE;
Score.Draw[score];
};