<> <> <> <> <> 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: BOOL _ FALSE; commands: PUBLIC Screen.CommandProcs _ [Play, Listen, HandleRed, HandleYellow, HandleBlue, HandleKeyboard, Scroll, Thumb, Score.Draw, Score.FileIn, FileOut, Hardcopy, Initialize, 0]; <<******************************************************************>> <> <<******************************************************************>> 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]; }; <<******************************************************************>> <> <<******************************************************************>> 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] = { <> 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] = { <> 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: BOOL _ FALSE; switch: BOOL _ TRUE; beginning: BOOL _ FALSE; 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 <> 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 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; <> 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: BOOL _ FALSE; 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; }; <<************************************************************************>> <> <<************************************************************************>> 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}; <> '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: BOOL _ FALSE; repaint: BOOL _ TRUE; 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: BOOL _ TRUE; negative: BOOL _ FALSE; <> 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 _ 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: BOOL _ FALSE] 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]; <> ') => 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]; }; <<******************************************************************>> <> <<******************************************************************>> 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]; }; <<******************************************************************>> <> <<******************************************************************>> 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}; <> start _ score.length; insertMeasure _ FALSE; <> 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}; <> IF score.sheet.display = physical AND ~listening THEN { IF temp = NIL THEN temp _ Piece.New[1000]; Score.StartListening[temp]; merge _ TRUE}; <> Score.StartPlaying[score, start, score.sheet.display = physical, DisplayCursor]; IF score.flash THEN Interface.Flash[score]; }; temp: ScorePTR _ NIL; merge: BOOL _ FALSE; 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}; <> 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: BOOL _ FALSE; 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]; };