InterfaceImplA.mesa
Copyright (C) 1982, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
last modified: January 16, 1982 11: 09 AM
Edited by Doug Wyatt, June 14, 1984 3:38:22 pm PDT
DIRECTORY
CursorDefs,
Graphics USING [DisplayChar, DisplayContext, DisplayString, Map, MoveTo, ScreenToUser, SetFont, SetPaint, SetTexture, Vec],
Heuristic USING [MakeBeams, MakeChords, MakeSyncs, SetNoteValues],
Interface USING [count, DeleteGraphical, Flash, MoveNote, MoveGraphical, Object, Wait],
IODefs USING [ReadChar, ReadDecimal, ReadID, Rubout],
MusicDefs,
Piece USING [AddSync, CleanUpNotes, Delete, Insert, Merge, NearestNote, Replace],
Real USING [Fix, FixI],
Score USING [Draw, FileIn, FileOut, Look, Print, Redraw, SetKey, SetMetrenome, SetTimeSignature, ShowLogical, StartListening, StartPlaying, StopListening, StopPlaying, Test],
Screen USING [CommandProcs, DisplayMessage, screen],
Selection, -- USING everything
Sheet USING [FindLine, Initialize, LineNumber, Map, NearestTime, Reset, ScreenPoint, Scroll, SetBegin, SetStyle],
StreamDefs USING [GetDefaultKey, KeyboardHandle],
StringDefs USING [AppendString, UpperCase],
Utility, -- USING everything
Voice USING [Check, Set];
InterfaceImplA: CEDAR MONITOR RETURNS[POINTER TO Screen.CommandProcs]
IMPORTS Graphics, Heuristic, Interface, IODefs, MusicDefs, Piece, Real, Score, Screen, Selection, Sheet, StreamDefs, StringDefs, Utility, Voice =
BEGIN
OPEN Graphics, MusicDefs;
keyboard: StreamDefs.KeyboardHandle;
Error: SIGNAL;
test: BOOL ← FALSE;
commands: Screen.CommandProcs ← [Play, Listen, HandleRed, HandleYellow, HandleBlue, HandleKeyboard, Scroll, Thumb, Score.Draw, Score.FileIn, FileOut, Hardcopy, Initialize, 0];
******************************************************************
Initialization
******************************************************************
Initialize:
PROC[dc: Graphics.DisplayContext] = {
keyboard ← StreamDefs.GetDefaultKey[];
Utility.InitStorage[];
Sheet.Initialize[dc];
};
******************************************************************
Command parser
******************************************************************
HandleKeyboard:
PROC = {
ClearDirty[];
Do[IODefs.ReadChar[]];
IF test AND Score.Test[] THEN Error; --you may proceed, but you cannot file out
};
HandleBlue:
PROC = {
OPEN Interface;
ClearDirty[];
count ← FALSE;
SELECT
TRUE
FROM
Control[] => {HandleMenu[]; count ← FALSE};
Shift[] => {Selection.Clear[];
WHILE BlueBug[] DO Interface.MoveNote[Piece.NearestNote[]]; ENDLOOP};
ENDCASE => Interface.MoveGraphical[defaultObject];
IF test AND Score.Test[] THEN Error; --you may proceed, but you cannot file out
IF count THEN commands.count ← commands.count+1;
};
HandleYellow:
PROC =
-- used to delete objects or unselect notes {
OPEN CursorDefs, Interface;
temp: Cursor ← cursor^;
cursor^ ← textCursor;
ClearDirty[];
count ← FALSE;
SELECT
TRUE
FROM
scale>3 => ChangeLook['O];
Shift[] AND Control[] => NULL;
Control[] => NULL;
ENDCASE => Interface.DeleteGraphical[];
cursor^ ← temp;
WHILE YellowBug[] DO NULL; ENDLOOP;
IF test AND Score.Test[] THEN Error; --you may proceed, but you cannot file out
IF count THEN commands.count ← commands.count+1;
};
HandleRed:
PROC =
-- Only used to change the selection {
OPEN CursorDefs;
x, y: INTEGER;
i, click: CARDINAL ← 0;
temp: Cursor ← cursor^;
cursor^ ← textCursor;
ClearDirty[];
WHILE RedBug[]
DO
[x, y] ← Sheet.ScreenPoint[];
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[Sheet.NearestTime[x, y].time];
1 => EXIT;
2 => ExtendLine[];
ENDCASE => ERROR};
Shift[] => Selection.AddNote[Piece.NearestNote[x, y]];
ENDCASE => {
IF command
THEN Selection.Clear[];
Selection.AddNote[Piece.NearestNote[x, y]]};
command ← FALSE;
ENDLOOP;
cursor^ ← temp;
IF test AND Score.Test[] THEN Error; --you may proceed, but you cannot file out
};
ClearDirty: PROC = INLINE {min ← 100000; max ← -1};
ExtendLine:
PROC = {
time: Time;
grey: BOOL ← FALSE;
switch: BOOL ← TRUE;
beginning: BOOL ← FALSE;
temp1, temp2, gTemp1, gTemp2: Time;
temp1 ← select1;
temp2 ← select2;
gTemp1 ← greySelect1;
gTemp2 ← greySelect2;
WHILE RedBug[]
DO
is the user switching between colors?
IF Shift[]
AND
NOT grey
THEN
{
grey ← TRUE;
switch ← TRUE;
Selection.AddLine[temp1, temp2];
};
IF
NOT Shift[]
AND grey
THEN
{
grey ← FALSE;
switch ← TRUE;
Selection.AddGreyLine[gTemp1, gTemp2];
};
[time,] ← Sheet.NearestTime[];
if the user is switching, then which end does he want?
IF switch
THEN
IF grey
THEN beginning ← Beginning[time, greySelect1, greySelect2]
ELSE beginning ← Beginning[time, select1, select2];
switch ← FALSE;
draw the appropriate line
IF grey
THEN
IF beginning
THEN Selection.AddGreyLine[time, greySelect2]
ELSE Selection.AddGreyLine[greySelect1, time]
ELSE
IF beginning
THEN Selection.AddLine[time, select2]
ELSE Selection.AddLine[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[time: Time] = {
time2: Time;
temp1, temp2, gTemp1, gTemp2: Time;
grey: BOOL ← FALSE;
temp1 ← select1;
temp2 ← select2;
gTemp1 ← greySelect1;
gTemp2 ← greySelect2;
WHILE RedBug[]
DO
IF Shift[]
AND
NOT grey
THEN
{
grey ← TRUE;
Selection.AddLine[temp1, temp2];
};
IF
NOT Shift[]
AND grey
THEN
{
grey ← FALSE;
Selection.AddGreyLine[gTemp1, gTemp2];
};
[time2,] ← Sheet.NearestTime[];
IF grey THEN Selection.AddGreyLine[time, time2] ELSE Selection.AddLine[time, time2];
ENDLOOP;
};
************************************************************************
commands from the keyboard
************************************************************************
Do:
PROC[c:
CHARACTER] = {
a, b: INTEGER;
SELECT c
FROM
'b =>
IF lineSelect
THEN Heuristic.MakeBeams[select1, select2]
ELSE Selection.MakeBeam[];
'B => Selection.ClearBeam[];
002C=> Selection.MakeBeamOfBeams[];
'c =>
IF lineSelect
THEN
Heuristic.MakeChords[select1, select2] ELSE
Selection.MakeChord[];
'C => Selection.ClearChord[];
'd => Selection.Delete[];
'e => {Selection.AddLine[0, EndOfScore[]]; RETURN};
'f => {ReadFileName[".logical"]; Score.FileInOld[fileName]};
'F => {ReadFileName[".logical"]; Score.FileOutOld[fileName]};
'g => GuessNoteValues[select1, select2];
'g => Selection.SetGrace[TRUE];
'G => Selection.SetGrace[FALSE];
007C => Heuristic.SetNoteValues[select1, select2];
'k => Score.SetKey[select1, select2, ReadKey[]];
'l => {ChangeLook[IODefs.ReadChar[]]; RETURN};
014C => {Score.ShowLogical[0, scoreLength-1]; Sheet.Reset[]}; -- debugging aid
'm => Score.SetMetrenome[select1, 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 lineSelect
THEN Piece.Replace[select1, select2, greySelect1, greySelect2]
ELSE Selection.SetRest[TRUE];
'R => IF ~lineSelect THEN Selection.SetRest[FALSE] ELSE flash ← TRUE;
's =>
IF lineSelect
THEN
Heuristic.MakeSyncs[select1, select2] ELSE
Selection.MakeSync[];
'S => Selection.ClearSync[];
't => Selection.MakeTie[];
'T => Selection.ClearTie[];
'x => {Voice.Check[]; RETURN};
'v => Voice.Set[ReadDigit[]];
' => {insertMeasure ← TRUE; RETURN};
values, dotted values, doubly dotted values, triply dotted valuese30(0, 3810)(1, 4445)(2, 5696)(3, 6592)\1965i14I181i40I340i41I462i40I895i40I1896i26I1914i65I
'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[]];
'^ => Selection.SetStem[TRUE];
'← => Selection.SetStem[FALSE];
'/ => {[a, b] ← ReadNumbers["time signature: <a>CR <b>CR",
TRUE];
Score.SetTimeSignature[select1, select2,[a, b]]};
033C => {Selection.Clear[]; RETURN};
ENDCASE => RETURN;
command ← TRUE;
IF flash THEN {Interface.Flash[]; RETURN};
commands.count ← commands.count+1;
IF lineSelect
AND min>max
THEN Score.Redraw[select1, select2]
ELSE Score.Redraw[min, max];
};
ChangeLook:
PROC[c:
CHARACTER] = {
count: BOOL ← FALSE;
repaint: BOOL ← TRUE;
SELECT c
FROM
'a=> Score.Look[accidental, TRUE,];
'A=> Score.Look[accidental, FALSE,];
'c=> Score.Look[noCarry, FALSE,];
'C=> Score.Look[noCarry, TRUE,];
'g=> flash ← TRUE;
'h=> Score.Look[hardcopy, TRUE,];
'H=> Score.Look[hardcopy, FALSE,];
'j=> {Score.Look[justified,, ReadDigit[]]; count ← TRUE};
'l=> {Score.Look[logical,,]; count ← TRUE};
'n=> Score.Look[notehead, TRUE,];
'N=> Score.Look[notehead, FALSE,];
'o=> Score.Look[overview, TRUE,];
'O=> Score.Look[overview, FALSE,];
'p=> {Score.Look[physical,, ReadDigit[]]; count ← TRUE};
's=> {Score.Look[sync, TRUE,]; repaint ← FALSE};
'S=> Score.Look[sync, FALSE,];
'v=> Score.Look[voice, TRUE, ReadDigit[]];
'V=> {Score.Look[voice, FALSE,]; repaint ← FALSE};
177C=> IODefs.Rubout;
ENDCASE => {Sheet.SetStyle[Digit[c], select1, select2]; count ← TRUE};
command ← TRUE;
IF flash THEN {Interface.Flash[]; RETURN};
Score.Draw[repaint];
IF count THEN commands.count ← commands.count+1;
};
ReadKey:
PROC
RETURNS[k:
INTEGER] = {
key, c: CHARACTER;
accidental: Accidental ← inKey;
CR: CHARACTER=015C;
Major: BOOL ← TRUE;
negative: BOOL ← FALSE;
key may be expressed as [-7..7]
Screen.DisplayMessage["Please enter key name (A, Bb, C#m...) or number."];
key ← IODefs.ReadChar[];
IF key='-
THEN {
key ← IODefs.ReadChar[];
Screen.DisplayMessage[NIL];
RETURN[-Digit[key]]};
IF key
IN ['0..'9]
THEN {
Screen.DisplayMessage[NIL];
RETURN[Digit[key]]};
key must have been named (Am, C#, DM, e, etc.)
key ← StringDefs.UpperCase[key];
IF key=177C THEN IODefs.Rubout;
WHILE (c ← IODefs.ReadChar[])#
CR
DO
SELECT c
FROM
'm => Major ← FALSE;
'b => accidental ← flat;
'# => accidental ← sharp;
177C=> IODefs.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[]; 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] = {
IODefs.ReadID[fileName !IODefs.Rubout=> RETRY];
StringDefs.AppendString[fileName, s];
RETURN;
};
fileName: STRING ← [50];
ReadNumbers:
PROC[s:
STRING, two:
BOOL ←
FALSE]
RETURNS[a, b:
INTEGER] = {
a ← b ← 0;
Screen.DisplayMessage[s];
a ← IODefs.ReadDecimal[];
Screen.DisplayMessage[NIL];
IF NOT two THEN RETURN;
Screen.DisplayMessage["and the second number..."];
b ← IODefs.ReadDecimal[];
Screen.DisplayMessage[NIL];
};
ReadDigit:
PROC
RETURNS[d:
CARDINAL] = {
Screen.DisplayMessage["Please enter a digit"];
d ← Digit[IODefs.ReadChar[]];
Screen.DisplayMessage[NIL];
};
Digit:
PROC[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[TF]; -- default for justify
ENDCASE => IODefs.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 ← CursorX^;
y ← CursorY^;
DisplayMenu[x+4, y+4];
CursorGetsMenu[0, 0];
WHILE BlueBug[]
DO
newI ← (CursorX^-x)/16;
newJ ← (CursorY^-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 => cursor^ ← quarter;
1 => cursor^ ← rest;
2 => cursor^ ← measure;
3 => cursor^ ← measure;
4 => cursor^ ← doubleMeasure;
5 => cursor^ ← repeat1;
6 => cursor^ ← repeat2;
7 => cursor^ ← endMeasure;
8 => cursor^ ← trebleClef;
9 => cursor^ ← bassClef;
10=> cursor^ ← octava;
ENDCASE => cursor^ ← textCursor;
1 =>
SELECT i
FROM
0 => cursor^ ← doubleFlat;
1 => cursor^ ← flat;
2 => cursor^ ← natural;
3 => cursor^ ← inKey;
4 => cursor^ ← sharp;
5 => cursor^ ← doubleSharp;
6 => cursor^ ← trill;
7 => cursor^ ← mordent1;
8 => cursor^ ← mordent2;
ENDCASE => cursor^ ← textCursor;
ENDCASE => cursor^ ← textCursor;
};
DisplayMenu:
PROC[x, y:
INTEGER] = {
OPEN Screen;
sp, p: Graphics.Vec ← [x, y];
p← Graphics.ScreenToUser[screen, sp];
Graphics.MoveTo[screen, p];
Graphics.SetPaint[screen, invert];
Graphics.SetTexture[screen, black];
Graphics.SetFont[screen, music, 8];
Graphics.DisplayString[screen,"tzffghopRS"]; --note, rest, measures, clefs
Utility.SetFont[screen, text, 12];
Graphics.DisplayChar[screen,'8];
Utility.SetFont[screen, music, 8];
p.y ← p.y-16;
Graphics.MoveTo[screen, p];
Graphics.DisplayString[screen,"EFG.MOUVW"]; --accidentals, embellishments
Graphics.SetFont[screen, text, 12];
};
******************************************************************
Utility procedures
******************************************************************
Hardcopy: PROC[s: STRING]={Score.Print[splines: ~hardcopy OR BlueBug[]]};
FileOut:
PROC[s:
STRING]= {
IF
NOT Score.FileOut[s]
THEN {
Screen.DisplayMessage["FileOut aborted-- see mesa.typescript"];
Interface.Flash[]};
};
Play:
PROC = {
start: CARDINAL;
IF playing THEN {Score.StopPlaying[]; RETURN};
ELSE TURN THE PLAYER ON
start ← scoreLength;
insertMeasure ← FALSE;
find the indices of the selection
IF select1<select2
THEN
FOR i:
CARDINAL
IN [0..scoreLength)
DO
IF start#scoreLength OR score[i].time< select1 THEN LOOP;
start ← i;
EXIT; ENDLOOP
ELSE {start ← 0};
should we record any keystrokes?
IF show.display=physical
AND ~listening
THEN {
IF temp=NIL THEN temp ← Utility.NewPiece[];
Score.StartListening[temp];
merge ← TRUE};
start the player
Score.StartPlaying[score, start, show.display=physical, DisplayCursor];
IF flash THEN Interface.Flash[];
};
temp: PiecePTR ← NIL;
merge: BOOL ← FALSE;
Listen:
PROC = {
IF listening
THEN {
-- TURN THE LISTENER OFF
Score.StopListening[];
IF temp[0]=NIL THEN RETURN; -- nothing recorded
IF merge
THEN Piece.Merge[select1, EndOfScore[], temp]
ELSE {Piece.Delete[select1, select2]; Piece.Insert[select1, temp]};
temp ← NIL;
Piece.CleanUpNotes[score];
Score.Look[voice, FALSE,];
WHILE AnyBug[] DO NULL; ENDLOOP;
Score.Draw[erase: ~merge];
commands.count ← 100; -- force a backup
RETURN};
ELSE TURN THE LISTENER ON
IF temp=NIL THEN temp ← Utility.NewPiece[];
IF TF<64 THEN TF ← 256;
Score.StartListening[temp];
IF flash THEN Interface.Flash[];
merge ← FALSE;
};
DisplayCursor:
PROC[time: Time] = {
sync: SyncPTR;
sp, p: Graphics.Vec;
IF show.display=physical THEN time ← time/TF;
IF time < begin THEN RETURN;
[p.x, p.y] ← Sheet.Map[time,, 2];
p.y ← p.y+64;
sp ← Graphics.Map[context, Screen.screen, p];
CursorX^ ← Real.FixI[sp.x];
CursorY^ ← MAX[808 - Real.FixI[sp.y], 0];
IF NOT insertMeasure THEN RETURN;
insertMeasure ← FALSE;
sync ← Utility.NewSync[];
sync.time ← time-8;
sync.type ← measure;
Piece.AddSync[score, sync];
Score.Redraw[time-8, time-8];
};
insertMeasure: BOOL ← FALSE;
Scroll:
PROC[by:
INTEGER] = {
time: Time;
x, y: INTEGER;
[x, y] ← Sheet.ScreenPoint[];
time ← Sheet.NearestTime[x, y-40].time;
IF by>0
THEN Sheet.Scroll[MAX[Lines[begin, time], 1]]
ELSE Sheet.Scroll[-MAX[Lines[begin, time], 1]];
Score.Draw[];
};
Thumb:
PROC = {
height: INTEGER ← 680;
endOfScore: Time;
sp, p: Graphics.Vec;
oldBegin: Time=begin;
endOfScore ← EndOfScore[];
SELECT scale
FROM
1 => height ← height;
2 => height ← (3*height)/2;
4 => height ← 4*height;
ENDCASE => ERROR;
WHILE YellowBug[]
DO
[p.x, p.y] ← Sheet.ScreenPoint[];
IF p.x>-10 THEN RETURN;
p.y ← -(height*begin)/endOfScore;
sp ← Graphics.Map[context, Screen.screen, p];
Interface.Wait[1];
CursorY^ ← MAX[808 - Real.FixI[sp.y], 0];
ENDLOOP;
[p.x, p.y] ← Sheet.ScreenPoint[];
begin ← (Real.Fix[top-p.y]*endOfScore)/height;
begin ← MIN[begin, endOfScore-60];
begin ← MAX[begin, 0];
Sheet.SetBegin[begin];
IF begin#oldBegin THEN Score.Draw[];
};
Lines:
PROC[time1, time2: Time]
RETURNS[
CARDINAL] =
INLINE {
line, topLine: INTEGER;
line ← Sheet.LineNumber[Sheet.FindLine[time2]];
topLine ← Sheet.LineNumber[Sheet.FindLine[time1]];
RETURN[line-topLine];
};
RETURN[@commands];
END.
Thumb:
PROC = {
time: Time;
x, y: INTEGER;
lines: INTEGER;
[x, y] ← Sheet.ScreenPoint[];
time ← Sheet.NearestTime[x, y].time;
lines ← Lines[begin, time];
SELECT
TRUE
FROM
lines=0 AND x<0 => {Sheet.SetBegin[0]};
scale=1 => {Sheet.Scale[4]; Sheet.SetBegin[0]};
scale=2 => {Sheet.Scale[4]; Sheet.SetBegin[0]};
scale=4 => {Sheet.Scale[IF hardcopy THEN 2 ELSE 1]; Sheet.SetBegin[time]};
ENDCASE;
Score.Draw[];
};