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 ], WalnutSendOpsExtras 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, WalnutSendOpsExtras, WalnutSendOps, WalnutOps, WalnutRegistry, WalnutWindow = { OPEN IO; voiceHandle: Intervoice.Handle; msgText: REF TEXT_RefText.ObtainScratch[5000]; 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].voiceFileID; IF voiceFileID = NIL THEN WalnutSendOpsExtras.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 ~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 ~PlayFromSelection[NARROW[parent]] THEN PlayFromMessageHeaders[VoiceHdrsFromGVID[WalnutWindow.GetMsgName[viewer]]]; }; VoiceMoveTo: ENTRY WalnutRegistry.MoveProc = { 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 = { 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 = { 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] = { WalnutSendOpsExtras.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."]; }. πWalnutVoiceImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last Edited by: Swinehart, September 29, 1986 6:41:04 am PDT Definitions Menu Buttons Buttons should use existing walnut MBQueues! Are there any? Walnut Sender Respond to record button in Sender by recording a message and inserting a VoiceFileID entry in the Walnut message header Sender and Walnut Message viewer STOP buttons Add a line to the Sender in which a VoiceFileID can be inserted. -- Sender Play button If primary selection is in this viewer and selects a voiceFileID, should play it only; otherwise, play all of them. Walnut Message Viewers Message viewer Play button If primary selection is in this viewer and selects a voiceFileID, should play that instead; otherwise play all of them. Procedures registered with WalnutRegistry [msgName: ROPE, fromMsgSet: ROPE, toMsgSet: ROPE, clientData: REF ANY] [msgGroup: REF WalnutRegistry.MsgGroup, event: WalnutRegistry.MsgGroupEvent, clientData: REF ANY] Utilities, Available from elsewhere parentViewer is viewer containing Play menu entry. If selection is a VoiceFileID node (wherever located), play that and return TRUE. Otherwise ... Play in reverse order (observe clever recursion), to reverse the effect of ParseHeaders. [fieldName: ROPE] RETURNS [wantThisOne: BOOL, continue: BOOL] Initialization (Walnut Voice and Finch) Set up the button procs needed by Walnut. Swinehart, January 30, 1986 3:47:33 pm PST Now uses Intervoice to manage recording and playback. changes to: voiceHandle, RecordProc, DoRecordProc, StopProc, PlayFromSelection, PlayFromMessageHeaders, FindVoiceID, WalnutComplain, InitializeWalnutVoice Swinehart, May 19, 1986 9:17:52 am PDT Cedar 6.1 changes to: DIRECTORY, WalnutVoiceImpl, Rid ΚŸ˜™Icodešœ Οmœ1™Jšœžœ ˜Jšœ žœ˜"Jšœ žœ ˜Jšœžœ ˜$Jšœ žœ8˜GJšœžœ4˜HJšœžœ˜)Jšœžœ#˜6Jšœ žœ˜/Jšœ˜J˜—šœžœžœ˜šž˜Jšœ$žœ¨˜Ξ—Jšžœžœ˜J˜—™ J™Jšœ˜J•StartOfExpansione -- [msgGroup: REF WalnutRegistry.MsgGroup, event: WalnutRegistry.MsgGroupEvent, clientData: REF ANY]šœ žœžœ˜.J˜—™ J™>™ J™šΟn œžœž˜%Jšœ Οbœg™xJšžœžœžœ˜Jšžœžœžœ˜@Jšœ˜—J˜šŸ œžœ#˜5Jšœžœ.˜Dšžœž˜JšœC˜C—šžœ ˜$Jšœžœžœ˜<—Jšœ˜—J˜šŸœžœ˜"Jšœ! œ™-Jšžœžœžœ˜J˜Jšœ˜J˜—šŸ œžœ˜$JšœC™CJšžœžœžœ˜Jšœžœ ˜-J˜Lšœ˜J˜——šŸœžœžœ˜0Jšœ œ™Jšžœžœžœ˜Jšœžœ ˜-J™ušžœžœ ž˜*šœ(˜(Jšœžœ!˜C——Jšœ˜J˜——™J™šŸœžœ˜)JšœΠbnœ™Jšžœžœžœ˜Jšœžœ ˜.J™yšžœžœ ž˜*JšœK˜K—Jšœ˜—J˜——™)J˜–J -- [msgName: ROPE, fromMsgSet: ROPE, toMsgSet: ROPE, clientData: REF ANY]šŸ œžœ˜.JšΠckΡbck’£’™FJš žœžœžœžœžœ˜0J˜Jš žœ žœžœ žœžœžœ˜/Jš žœ!žœžœ žœž œ˜aJšœ%˜%šžœ1žœžœž˜Ešœ˜Jšœ3˜3Jšœ(˜(—Jšžœ˜—J˜J˜—šŸœžœ ˜:Jš’a™aJš žœžœžœžœžœ˜0Jšžœžœžœžœ˜!šžœžœžœ"ž˜6Jšžœ žœžœžœ˜šžœž˜Jšœ žœΟc;˜JJšœF˜FJšžœ˜—Jšžœ˜—Jšœ˜J˜——™#J™šŸœžœžœ$˜DJšžœ žœžœ˜ J™”J˜J˜$Jšœžœ˜Jšœ žœ˜J˜:Jšžœžœžœžœ˜>J˜/Jšžœ0ž ˜>J˜RJšžœžœ˜ J˜J˜—šŸœžœžœ#˜HJ™XJšžœ žœžœžœ˜J˜%J˜GJ˜—J˜šŸœ˜Jšžœžœ žœžœ˜Bšœ1˜1Jšœžœ žœžœ˜S—Jšžœ5˜;J˜J˜—š Ÿœžœžœžœžœ˜Yš žœžœžœžœž˜-Jšœ1˜1—J˜J˜—–A -- [fieldName: ROPE] RETURNS [wantThisOne: BOOL, continue: BOOL]šŸ œ˜$Jš’=™=Jšžœ žœžœ˜4J˜J˜—š Ÿ œžœžœžœžœ˜/Jšžœ<˜BšŸœžœžœžœ žœžœžœžœ˜GJš žœžœžœžœžœžœ˜RJ˜——š Ÿœžœžœžœžœ˜CJšžœE˜KJ˜J˜—šŸœžœžœ˜.Jšœ/˜/J˜——™'J˜šŸœžœ˜1Jšœžœ!˜7Jšœžœ!˜4˜J˜O—Jš€)™)J˜?J˜