<> <> <> <<>> DIRECTORY Commander USING [ CommandProc, Register ], CommandTool USING [ NextArgument ], Intervoice USING [ Forget, Handle, Open, Play, Record, Retain, Stop ], IO, MBQueue USING [ Create ], Menus USING [MenuProc], Process USING [ Detach ], RefText USING [ ObtainScratch, TrustTextAsRope], Rope USING [Cat, Equal, Fetch, Find, Length, MakeRope, ROPE, Substr], TiogaOps USING [ Location, GetRope, GetSelection, ViewerDoc ], ViewerClasses USING [Viewer], VoiceUtils USING [ CurrentRName ], WalnutDefs USING [ Error ], WalnutDocumentRope USING [ Create ], WalnutOps USING [ GetMsgHeaders, MsgHeaders,ParseHeaders, ParseProc ], WalnutRegistry USING [ MoveProc, MsgGroupProc, MsgGroupSize, Register ], WalnutSendInternal USING [SenderReport], WalnutSendOps USING [AddToSendMenu, AppendHeaderLine], WalnutWindow USING [ AddToMsgMenu, GetMsgName ] ; WalnutVoiceImpl: CEDAR MONITOR IMPORTS Commander, CommandTool, Intervoice, IO, MBQueue, Process, RefText, Rope, TiogaOps, VoiceUtils, WalnutDefs, WalnutDocumentRope, WalnutSendInternal, WalnutSendOps, WalnutOps, WalnutRegistry, WalnutWindow = { OPEN IO; <> <<>> voiceHandle: Intervoice.Handle; msgText: REF TEXT_RefText.ObtainScratch[5000]; <> <<Buttons should use existing walnut MBQueues! Are there any?>> <> <<>> RecordProc: ENTRY Menus.MenuProc = { <> ENABLE UNWIND => NULL; TRUSTED { Process.Detach[FORK DoRecordProc[NARROW[parent]] ]; }; }; DoRecordProc: PROC [viewer: ViewerClasses.Viewer] = { voiceFileID: Rope.ROPE = Intervoice.Record[voiceHandle]; IF voiceFileID = NIL THEN WalnutSendInternal.SenderReport[msg: "Attempt to record failed\n"] ELSE WalnutSendOps.AppendHeaderLine[ viewer, IO.PutFR["VoiceFileID: %g", IO.rope[voiceFileID]]]; }; StopProc: ENTRY Menus.MenuProc = { <> ENABLE UNWIND => NULL; Intervoice.Stop[voiceHandle]; }; AttachProc: ENTRY Menus.MenuProc = { <> ENABLE UNWIND => NULL; viewer: ViewerClasses.Viewer_ NARROW[parent]; WalnutSendOps.AppendHeaderLine[viewer, "VoiceFileID: \001VoiceFileID\002"]; }; SenderPlayProc: ENTRY Menus.MenuProc = TRUSTED { <> ENABLE UNWIND => NULL; viewer: ViewerClasses.Viewer_ NARROW[parent]; <<If primary selection is in this viewer and selects a voiceFileID, should play it only; otherwise, play all of them.>> IF ~PlayFromSelection[NARROW[parent]] THEN PlayFromMessageHeaders[VoiceHdrsFromMsg[ WalnutDocumentRope.Create[LOOPHOLE [TiogaOps.ViewerDoc[viewer]]]]]; }; <> <<>> MessagePlayProc: ENTRY Menus.MenuProc = { <> ENABLE UNWIND => NULL; viewer: ViewerClasses.Viewer = NARROW[parent]; <<If primary selection is in this viewer and selects a voiceFileID, should play that instead; otherwise play all of them.>> IF ~PlayFromSelection[NARROW[parent]] THEN PlayFromMessageHeaders[VoiceHdrsFromGVID[WalnutWindow.GetMsgName[viewer]]]; }; <> VoiceMoveTo: ENTRY WalnutRegistry.MoveProc = { <<[msgName: ROPE, fromMsgSet: ROPE, toMsgSet: ROPE, clientData: REF ANY]>> ENABLE { ABORTED => CONTINUE; UNWIND => NULL; }; headers: WalnutOps.MsgHeaders; IF voiceHandle=NIL OR toMsgSet=NIL THEN RETURN; IF (Rope.Equal[toMsgSet, "deleted",FALSE] OR Rope.Equal[toMsgSet, "active", FALSE]) THEN RETURN; headers _ VoiceHdrsFromGVID[msgName]; FOR hdrs: WalnutOps.MsgHeaders _ headers, hdrs.rest WHILE hdrs#NIL DO []_Intervoice.Retain[ handle: voiceHandle, voiceFileID: hdrs.first.value, refID: Rid[msgName], refIDType: "GVID"]; ENDLOOP; }; ChangeInterestEntry: ENTRY WalnutRegistry.MsgGroupProc = { <<[msgGroup: REF WalnutRegistry.MsgGroup, event: WalnutRegistry.MsgGroupEvent, clientData: REF ANY]>> ENABLE { ABORTED => CONTINUE; UNWIND => NULL; }; IF voiceHandle = NIL THEN RETURN; FOR i: CARDINAL IN [0..WalnutRegistry.MsgGroupSize) DO IF msgGroup[i]=NIL THEN LOOP; SELECT event FROM added => NULL; -- Present design calls for no action at message read time. destroyed => Intervoice.Forget[voiceHandle, Rid[msgGroup[i]], "GVID"]; ENDCASE; ENDLOOP; }; <> <<>> PlayFromSelection: INTERNAL PROC[parentViewer: ViewerClasses.Viewer] RETURNS [played: BOOL_FALSE] = { <> selV: ViewerClasses.Viewer; selStart, selEnd: TiogaOps.Location; nodeContents: Rope.ROPE; vfIndex: INT_-1; [selV, selStart, selEnd] _ TiogaOps.GetSelection[primary]; IF selV#parentViewer OR selStart.node#selEnd.node THEN RETURN; nodeContents _ TiogaOps.GetRope[selStart.node]; IF (vfIndex_nodeContents.Find["VoiceFileID: "])<0 THEN RETURN; Intervoice.Play[handle: voiceHandle, voiceFileID: nodeContents.Substr[start: 14]]; RETURN[TRUE]; }; PlayFromMessageHeaders: INTERNAL PROC[headers: WalnutOps.MsgHeaders] = { <> IF headers=NIL THEN RETURN; PlayFromMessageHeaders[headers.rest]; Intervoice.Play[handle: voiceHandle, voiceFileID: headers.first.value]; }; VoiceHdrsFromGVID: INTERNAL PROC[msgID: Rope.ROPE] RETURNS [WalnutOps.MsgHeaders] = { msgText _ WalnutOps.GetMsgHeaders[msgID, msgText! WalnutDefs.Error => IF who=$log AND code=$MsgHeadersTooLong THEN msgText.length_0]; RETURN[VoiceHdrsFromMsg[RefText.TrustTextAsRope[msgText]]]; }; VoiceHdrsFromMsg: INTERNAL PROC[msgHeaders: Rope.ROPE] RETURNS [WalnutOps.MsgHeaders] = { RETURN[IF msgHeaders.Length[]=0 THEN NIL ELSE WalnutOps.ParseHeaders[msgHeaders, FindVoiceID]]; }; FindVoiceID: WalnutOps.ParseProc = { <<[fieldName: ROPE] RETURNS [wantThisOne: BOOL, continue: BOOL]>> RETURN[fieldName.Equal["voicefileid", FALSE], TRUE]; }; LowerCaseRope: PROC[r: ROPE] RETURNS [ROPE] = { RETURN[Rope.MakeRope[base: r, size: r.Length[], fetch: LCFetch]]}; LCFetch: SAFE PROC[data: REF, index: INT] RETURNS [c: CHAR] = TRUSTED { SELECT (c_NARROW[data,ROPE].Fetch[index]) FROM IN ['A..'Z]=>c_c+('a-'A); ENDCASE}; Rid: PROC[msgHeader: Rope.ROPE] RETURNS [dbRidValue: Rope.ROPE] = { RETURN[Rope.Cat[LowerCaseRope[VoiceUtils.CurrentRName[]], "/", msgHeader]]; }; WalnutComplain: PROC[complaint: Rope.ROPE] = { WalnutSendInternal.SenderReport[complaint]; }; <> InitializeWalnutVoice: Commander.CommandProc = { tunesDBName: Rope.ROPE _ CommandTool.NextArgument[cmd]; instance: Rope.ROPE _ CommandTool.NextArgument[cmd]; voiceHandle _ Intervoice.Open[ tunesDBName: tunesDBName, tunesDBInstance: instance, Complain: WalnutComplain]; <> WalnutSendOps.AddToSendMenu[label: "Record", proc: RecordProc]; WalnutSendOps.AddToSendMenu[label: "STOP!", proc: StopProc]; WalnutSendOps.AddToSendMenu[label: "Play", proc: SenderPlayProc]; WalnutSendOps.AddToSendMenu[label: "Attach", proc: AttachProc]; WalnutWindow.AddToMsgMenu[label: "Play", proc: MessagePlayProc]; WalnutWindow.AddToMsgMenu[label: "STOP!", proc: StopProc]; []_WalnutRegistry.Register[ [ msgGroupProc: ChangeInterestEntry, msgGroupData: NIL, -- used to be handle moveProc: VoiceMoveTo, moveProcData: NIL -- used to be handle ], MBQueue.Create[] ]; }; Commander.Register["WalnutVoice", InitializeWalnutVoice, "WalnutVoice [] [Instance]\nRegister WalnutVoice with walnut, extend Walnut menus."]; }. <> <> <> <> <> <> <<>>