CONST numKeys = 22; whKeyWidth = 30; whKeyHeight = 80; blKeyWidth = 16; blKeyHeight = 50; viewHMargin = 15; viewVMargin = 10; VAR waveTable: ARRAY [0..51] OF Microseconds; whiteKeys: ARRAY [1..numKeys] OF INTEGER; blackKeys: ARRAY [1..numKeys] OF INTEGER; {entry of -1 means no black key on top of left edge of corresponding white key} METHODS OF TBox; FUNCTION {TBox.}CREATE{(object: TObject; itsHeap: THeap; itsNumber: INTEGER; itsColor: TColor; itsWaveLength: Microseconds): TBox}; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(itsHeap, THISCLASS); SELF := TBox(object); WITH SELF DO BEGIN number := itsNumber; bounds := zeroLRect; color := itsColor; wavelength := itsWaveLength; overBox1 := NIL; overBox2 := NIL; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE {TBox.}Fields{(PROCEDURE Field(nameAndType: S255))}; BEGIN SUPERSELF.Fields(Field); Field('number: INTEGER'); Field('bounds: LRect'); Field('color: Byte'); Field('waveLength: LONGINT'); FIeld('overBox1: TBox'); FIeld('overBox2: TBox'); Field(''); END; {$ENDC} PROCEDURE {TBox.}Draw; VAR lPat: LPattern; BEGIN {$IFC fTrace}BP(10);{$ENDC} PenNormal; PenSize(1, 1); IF LRectIsVisible(SELF.bounds) THEN BEGIN CASE SELF.color OF colorWhite: lPat := lPatWhite; colorBlack: lPat := lPatBlack; OTHERWISE BEGIN ABCbreak('invalid color', SELF.color); lPat := lPatWhite; {should not happen} END; END; FillLRect(SELF.bounds, lPat); FrameLRect(SELF.bounds); END; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TKeyProcess; FUNCTION {TKeyProcess.}CREATE{(object: TObject; itsHeap: THeap): TKeyProcess}; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(itsHeap, THISCLASS); SELF := TKeyProcess(TProcess.CREATE(object, itsHeap)); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TKeyProcess.}Commence{(phraseVersion: INTEGER)}; BEGIN {$IFC fTrace}BP(11);{$ENDC} SUPERSELF.Commence(phraseVersion); waveTable[ 0] := 8191; waveTable[ 1] := 8191; waveTable[ 2] := 8191; waveTable[ 3] := 8191; waveTable[ 4] := 8191; waveTable[ 5] := 8099; waveTable[ 6] := 7645; waveTable[ 7] := 7215; waveTable[ 8] := 6810; waveTable[ 9] := 6428; waveTable[10] := 6067; waveTable[11] := 5727; waveTable[12] := 5405; waveTable[13] := 5102; waveTable[14] := 4816; waveTable[15] := 4545; waveTable[16] := 4290; waveTable[17] := 4050; waveTable[18] := 3822; waveTable[19] := 3608; waveTable[20] := 3405; waveTable[21] := 3214; waveTable[22] := 3034; waveTable[23] := 2863; waveTable[24] := 2703; waveTable[25] := 2551; waveTable[26] := 2408; waveTable[27] := 2273; waveTable[28] := 2145; waveTable[29] := 2025; waveTable[30] := 1911; waveTable[31] := 1804; waveTable[32] := 1703; waveTable[33] := 1607; waveTable[34] := 1517; waveTable[35] := 1432; waveTable[36] := 1351; waveTable[37] := 1276; waveTable[38] := 1204; waveTable[39] := 1136; waveTable[40] := 1073; waveTable[41] := 1012; waveTable[42] := 956; waveTable[43] := 902; waveTable[44] := 851; waveTable[45] := 804; waveTable[46] := 758; waveTable[47] := 716; waveTable[48] := 676; waveTable[49] := 638; waveTable[50] := 602; waveTable[51] := 568; whiteKeys[ 1] := 15; whiteKeys[ 2] := 17; whiteKeys[ 3] := 19; whiteKeys[ 4] := 20; whiteKeys[ 5] := 22; whiteKeys[ 6] := 24; whiteKeys[ 7] := 26; whiteKeys[ 8] := 27; whiteKeys[ 9] := 29; whiteKeys[10] := 31; whiteKeys[11] := 32; whiteKeys[12] := 34; whiteKeys[13] := 36; whiteKeys[14] := 38; whiteKeys[15] := 39; whiteKeys[16] := 41; whiteKeys[17] := 43; whiteKeys[18] := 44; whiteKeys[19] := 46; whiteKeys[20] := 48; whiteKeys[21] := 50; whiteKeys[22] := 51; blackKeys[ 1] := -1; blackKeys[ 2] := 16; blackKeys[ 3] := 18; blackKeys[ 4] := -1; blackKeys[ 5] := 21; blackKeys[ 6] := 23; blackKeys[ 7] := 25; blackKeys[ 8] := -1; blackKeys[ 9] := 28; blackKeys[10] := 30; blackKeys[11] := -1; blackKeys[12] := 33; blackKeys[13] := 35; blackKeys[14] := 37; blackKeys[15] := -1; blackKeys[16] := 40; blackKeys[17] := 42; blackKeys[18] := -1; blackKeys[19] := 45; blackKeys[20] := 47; blackKeys[21] := 49; blackKeys[22] := -1; {$IFC fTrace}EP;{$ENDC} END; FUNCTION {TKeyProcess.}NewDocManager{(volumePrefix: TFilePath; openAsTool: BOOLEAN): TDocManager}; BEGIN {$IFC fTrace}BP(11);{$ENDC} NewDocManager := TKeyDocManager.CREATE(NIL, mainHeap, volumePrefix); {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TKeyDocManager; FUNCTION {TKeyDocManager.}CREATE{(object: TObject; itsHeap: THeap; itsPathPrefix: TFilePath) : TKeyDocManager}; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(itsHeap, THISCLASS); SELF := TKeyDocManager(TDocManager.CREATE(object, itsHeap, itsPathPrefix)); {$IFC fTrace}EP;{$ENDC} END; FUNCTION {TKeyDocManager.}NewWindow{(heap: THeap; wmgrID: TWindowID):TWindow}; BEGIN {$IFC fTrace}BP(11);{$ENDC} NewWindow := TKeyWindow.CREATE(NIL, heap, wmgrID); {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TKeyView; FUNCTION {TKeyView.}CREATE{(object: TObject; itsHeap: THeap; itsPanel: TPanel; itsExtent: LRect; itsBoxList: TList): TKeyView}; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(itsHeap, THISCLASS); SELF := TKeyView(itsPanel.NewView(object, itsExtent, TPrintManager.CREATE(NIL, itsHeap), stdMargins, TRUE)); SELF.boxList := itsBoxList; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE {TKeyView.}Fields{(PROCEDURE Field(nameAndType: S255))}; BEGIN SUPERSELF.Fields(Field); Field('boxList: TList'); Field(''); END; {$ENDC} FUNCTION {TKeyView.}BoxWith{(LPt: LPoint): TBox}; VAR box: TBox; s: TListScanner; BEGIN {$IFC fTrace}BP(11);{$ENDC} boxWith := NIL; s := SELF.boxList.Scanner; WHILE s.Scan(box) DO IF LPtInLRect(LPt, box.bounds) THEN BoxWith := box; {last one found (front one) is returned} {$IFC fTrace}EP;{$ENDC} END; FUNCTION {TKeyView.}CursorAt{(mouseLPt: LPoint): TCursorNumber}; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF SELF.BoxWith(mouseLPt) = NIL THEN CursorAt := SUPERSELF.CursorAt(mouseLPt) ELSE CursorAt := fingerCursor; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TKeyView.}Draw; VAR s: TListScanner; box: TBox; BEGIN {$IFC fTrace}BP(10);{$ENDC} s := SELF.boxList.Scanner; WHILE s.Scan(box) DO box.Draw; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TKeySelection; FUNCTION {TKeySelection.}CREATE{(object: TObject; itsHeap: THeap; itsView: TView; itsKind: INTEGER; itsAnchorLPt: LPoint): TKeySelection}; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(itsHeap, THISCLASS); SELF := TKeySelection(TSelection.CREATE(object, itsHeap, itsView, itsKind, itsAnchorLPt)); SELF.currentBox := NIL; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE {TKeySelection.}Fields{(PROCEDURE Field(nameAndType: S255))}; BEGIN SUPERSELF.Fields(Field); Field('currentBox: TBox'); Field(''); END; {$ENDC} PROCEDURE {TKeySelection.}Highlight{(highTransit: THighTransit)}; VAR box: TBox; pat: Pattern; rgn1: RgnHandle; rgn2: RgnHandle; BEGIN {$IFC fTrace}BP(10);{$ENDC} box := SELF.currentBox; IF (box <> NIL) AND ((highTransit = hOffToOn) OR (highTransit = hOnToOff)) THEN BEGIN rgn1 := NewRgn; rgn2 := NewRgn; OpenRgn; FrameLRect(box.bounds); CloseRgn(rgn1); IF box.color = colorWhite THEN {subtract overlapping black keys} BEGIN OpenRgn; IF box.overBox1 <> NIL THEN box.overBox1.Draw; IF box.overBox2 <> NIL THEN box.overBox2.Draw; CloseRgn(rgn2); DiffRgn(rgn1, rgn2 ,rgn1); END; PenMode(patXOR); thePad.LPatToPat(lPatGray, pat); PenPat(pat); InsetRgn(rgn1, 3, 2); PaintRgn(rgn1); DisposeRgn(rgn1); DisposeRgn(rgn2); END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TKeySelection.}MousePress{(mouseLPt: LPoint)}; VAR panel: TPanel; keyWindow: TKeyWindow; pickedBox: TBox; noSelection: TSelection; theKind: INTEGER; keyView: TKeyView; BEGIN {$IFC fTrace}BP(10);{$ENDC} panel := SELF.panel; keyWindow := TKeyWindow(panel.window); keyView := TKeyView(SELF.view); pickedBox := keyView.BoxWith(mouseLPt); { find box where mouse press/move happened } IF pickedBox = NIL THEN { Mouse press outside of keyboard } BEGIN IF (SELF.currentBox <> NIL) THEN { was there a key already down? } BEGIN keyWindow.Silence; { turn off tone } panel.Highlight(SELF, hOnToOff); END; SELF.currentBox := NIL; { no "current" key } END ELSE { Mouse press on keyboard } BEGIN IF (pickedBox <> SELF.currentBox) THEN { this key same as last one? } BEGIN { No... } panel.Highlight(SELF, hOnToOff); keyWindow.Sound(pickedBox.waveLength); { start tone } SELF.currentBox := pickedBox; { save this key as the new "current" key } panel.Highlight(SELF, hOffToOn); END; END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TKeySelection.}MouseMove{(mouseLPt: LPoint)}; BEGIN {$IFC fTrace}BP(11);{$ENDC} SELF.MousePress(mouseLPt); { do same thing as for initial MousePress } {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TKeySelection.}MouseRelease; VAR fakeLPt: LPoint; BEGIN {$IFC fTrace}BP(12);{$ENDC} SetLPt(fakeLPt, -1, -1); SELF.MousePress(fakeLPt); {any LPoint not on the keyboard will do} {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TKeySelection.}KeyChar{(ch: CHAR)}; BEGIN {$IFC fTrace}BP(10);{$ENDC} {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TKeyWindow; FUNCTION {TKeyWindow.}CREATE{(object: TObject; itsHeap: THeap; itsWmgrID: TWindowID): TWindow}; VAR t: SpeakerVolume; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF object = NIL THEN object := NewObject(itsHeap, THISCLASS); SELF := TKeyWindow(TWindow.CREATE(object, itsHeap, itsWmgrID, FALSE)); WITH SELF DO BEGIN hSize := 100; vSize := 100; appVolume := 1; {initial volume inside application} END; t := Volume; SELF.initialVolume := t; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE {TKeyWindow.}Fields{(PROCEDURE Field(nameAndType: S255))}; BEGIN SUPERSELF.Fields(Field); Field('initialVolume: INTEGER'); Field('appVolume: INTEGER'); Field('hSize: INTEGER'); Field('vSize: INTEGER'); Field(''); END; {$ENDC} PROCEDURE {TKeyWindow.}Activate; VAR border: Rect; pt: Point; BEGIN {$IFC fTrace}BP(10);{$ENDC} TWindow.Activate; SELF.GetBorder(border); WITH SELF, border DO {$H-} SetPt(pt, hSize + right, vSize + bottom); {$H+} SELF.ResizeTo(pt); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TKeyWindow.}BlankStationery; VAR keyList: TList; number: INTEGER; keyLRect: LRect; box: TBox; viewLRect: LRect; panel: TPanel; keyView: TKeyView; keySelection: TKeySelection; aWhiteKey: TBox; BEGIN {$IFC fTrace}BP(10);{$ENDC} keyList := TList.CREATE(NIL, SELF.Heap, numKeys); {allocate enough space for white keys to start} number := 0; SetLRect(keyLRect, 0, 0, whKeyWidth, whKeyHeight); OffsetLRect(keyLRect, viewHMargin, viewVMargin); WHILE number < numKeys DO BEGIN number := number + 1; box := TBox.CREATE(NIL, SELF.heap, number, colorWhite, waveTable[whiteKeys[number]]); box.bounds := keyLRect; keyList.InsAt(number, box); OffsetLRect(keyLRect, whKeyWidth, 0); END; WITH keyLRect DO SetLRect(viewLRect, 0, 0, left + viewHMargin, bottom + viewVMargin); WITH viewLRect DO panel := TPanel.CREATE(NIL, SELF.Heap, SELF, 0, 0, [], []); WITH SELF, viewLRect DO BEGIN hSize := right; vSize := bottom; END; keyView := TKeyView.CREATE(NIL, SELF.Heap, panel, viewLRect, keyList); keySelection := TKeySelection(panel.selection.FreedAndReplacedBy( TKeySelection.CREATE(NIL, SELF.Heap, keyView, pickKind, zeroLPt))); number := 0; SetLRect(keyLRect, 0, 0, blKeyWidth, blKeyHeight); OffsetLRect(keyLRect, viewHMargin - (blKeyWidth DIV 2), viewVMargin); WHILE number < numKeys DO BEGIN number := number + 1; IF blackKeys[number] >= 0 THEN BEGIN box := TBox.CREATE(NIL, SELF.heap, number, colorBlack, waveTable[blackKeys[number]]); box.bounds := keyLRect; keyList.InsLast(box); {Make the white keys that 'box' covers point to it} aWhiteKey := TBox(keyList.At(number)); aWhiteKey.overBox1 := box; IF number > 1 THEN BEGIN aWhiteKey := TBox(keyList.At(number - 1)); aWhiteKey.overBox2 := box; END; END; OffsetLRect(keyLRect, whKeyWidth, 0); END; {$IFC fTrace}EP;{$ENDC} END; FUNCTION {TKeyWindow.}CanDoCommand{(cmdNumber: TCmdNumber; VAR checkIt: BOOLEAN): BOOLEAN}; VAR t: SpeakerVolume; cmd: TCmdNumber; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF (cmdNumber >= uVol0) AND (cmdNumber <= uVolMax) THEN BEGIN CanDoCommand := TRUE; checkIt := cmdNumber = SELF.appVolume + uVol0; END ELSE CanDoCommand := SUPERSELF.CanDoCommand(cmdNumber, checkIt); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TKeyWindow.}Deactivate; VAR border: Rect; pt: Point; BEGIN {$IFC fTrace}BP(10);{$ENDC} (********** Put this in if you want the window to shrink down on a deactivate SELF.GetBorder(border); WITH SELF, border DO {$H-} SetPt(pt, 60, 40); {$H+} TArea.PushFocus; SELF.Focus; SELF.ResizeTo(pt); TArea.PopFocus; **********) SUPERSELF.Deactivate; {$IFC fTrace}EP;{$ENDC} END; FUNCTION {TKeyWindow.}NewCommand{(cmdNumber: TCmdNumber): TCommand}; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF (cmdNumber >= uVol0) AND (cmdNumber <= uVolMax) THEN BEGIN NewCommand := NIL; SELF.appVolume := cmdNumber - uVol0; END ELSE NewCommand := SUPERSELF.NewCommand(cmdNumber); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TKeyWindow.}Silence; BEGIN {$IFC fTrace}BP(10);{$ENDC} Silence; SetVolume(SELF.initialVolume); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TKeyWindow.}Sound{(waveLength: Microseconds)}; BEGIN {$IFC fTrace}BP(10);{$ENDC} SetVolume(SELF.appVolume); Noise(waveLength); {$IFC fTrace}EP;{$ENDC} END; END;