<<>> <> <> <> <> <> <<>> DIRECTORY BasicTime USING [GMT], Commander USING [CommandProc, Register], CommanderOps USING [ArgumentVector, Failed, Parse ], Convert USING [RopeFromTime ], FS USING [defaultStreamOptions, Error, FileInfo, StreamOpen, StreamOptions ], <> IO USING [Close, PutFR1, PutRope, rope, STREAM ], <> Rope USING [Cat, Fetch, Find, Length, MaxLen, ROPE, Substr ], Tioga USING [ PropList ], TiogaAccess USING [FromFile, Get, Reader, TiogaChar ], TiogaFileIO USING [ GetParts ], TiogaVoicePrivate USING [ thrushHandle ], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc ], ViewerOps USING [AddProp, FetchProp], VoiceRope USING [Handle, Retain, VoiceRopeInterval ] ; VoiceInterestImpl: CEDAR PROGRAM IMPORTS Commander, CommanderOps, Convert, FS, -- FSBackdoor, Process, -- IO, Rope, TiogaAccess, TiogaFileIO, TiogaVoicePrivate, ViewerEvents, ViewerOps, VoiceRope EXPORTS TiogaVoicePrivate ~ BEGIN <> <> <<>> backgroundCommentary: IO.STREAM ¬ NIL; Commentate: PROC [remark: Rope.ROPE, commentary: IO.STREAM] = INLINE { IF commentary # NIL THEN commentary.PutRope[remark]; }; LocalFile: ERROR; DoFileInterests: PROC [file: Rope.ROPE, commentary: IO.STREAM] = { <> globalName: Rope.ROPE; voiceList: Rope.ROPE ¬ NIL; createDate: BasicTime.GMT; [globalName, createDate] ¬ GetFileNameAndDate[file, commentary]; IF NOT IsTiogaFile[globalName] THEN { Commentate[" Not a Tioga file\n", commentary]; RETURN }; voiceList ¬ GetVoiceList[globalName]; IF voiceList = NIL THEN Commentate[" No voice in file\n", commentary] ELSE RegisterFileInterests[globalName, createDate, voiceList, commentary]; }; GetFileNameAndDate: PROC [file: Rope.ROPE, commentary: IO.STREAM] RETURNS [globalName: Rope.ROPE, createDate: BasicTime.GMT] ~ { fullFName, attachedTo: Rope.ROPE; keep: CARDINAL; [fullFName: fullFName, attachedTo: attachedTo, keep: keep, created: createDate] ¬ FS.FileInfo[file ! FS.Error => GOTO quit]; -- if there's something wrong with the file, do nothing. Presumably whoever called whatever triggered us will complain. <> Commentate[IO.PutFR1["File %g:\n", IO.rope[fullFName]], commentary]; IF attachedTo = NIL THEN { <> <> globalName ¬ fullFName; Commentate["File is global:\n", commentary] <<}>> } ELSE { globalName ¬ attachedTo; Commentate[IO.PutFR1["File is attached to global file %g:\n", IO.rope[globalName]], commentary] }; EXITS quit => RETURN; }; <> <<>> <> IsTiogaFile: PROC [filename: Rope.ROPE] RETURNS [tiogaFile: BOOL] ~ { fileStream: IO.STREAM; streamOptions: FS.StreamOptions ¬ FS.defaultStreamOptions; <> streamOptions[tiogaRead] ¬ FALSE; fileStream ¬ FS.StreamOpen[fileName: filename, streamOptions: streamOptions]; tiogaFile ¬ TiogaFileIO.GetParts[fileStream].isTioga; fileStream.Close[]; }; GetVoiceList: PROC [filename: Rope.ROPE] RETURNS [voiceList: Rope.ROPE] ~ { fileStream: TiogaAccess.Reader ¬ TiogaAccess.FromFile[filename]; rootChar: TiogaAccess.TiogaChar ¬ TiogaAccess.Get[fileStream]; -- first character produces the root properties FOR rootProps: Tioga.PropList ¬ rootChar.propList, rootProps.rest WHILE rootProps # NIL DO IF rootProps.first.key = $voicelist THEN { IF voiceList = NIL THEN voiceList ¬ NARROW[rootProps.first.val, Rope.ROPE] ELSE ERROR } ENDLOOP }; RegisterFileInterests: PROC [filename: Rope.ROPE, createDate: BasicTime.GMT, voiceList: Rope.ROPE, commentary: IO.STREAM] ~ { nextVoice: Rope.ROPE; startOfID: INT ¬ 1; endOfID: INT; Commentate[" Voice message IDs are\n", commentary]; IF NOT ( voiceList.Length > 0 AND voiceList.Fetch[0] = '& ) THEN ERROR; DO endOfID ¬ voiceList.Find["&", startOfID]; nextVoice ¬ voiceList.Substr[startOfID, IF endOfID = -1 THEN Rope.MaxLen ELSE endOfID - startOfID]; IF nextVoice.Length = 0 THEN ERROR; <> VoiceRope.Retain[handle: TiogaVoicePrivate.thrushHandle, vr: NEW [VoiceRope.VoiceRopeInterval ¬ [nextVoice, 0, 0]], refID: filename.Cat[" ", Convert.RopeFromTime[createDate]], class: "TiogaVoice"]; Commentate[nextVoice, commentary]; Commentate["\n", commentary]; IF endOfID = -1 THEN RETURN; startOfID ¬ endOfID + 1 ENDLOOP }; RegisterVoiceInterestCmd: Commander.CommandProc = { { ENABLE { FS.Error => IF error.group # user THEN REJECT ELSE {msg ¬ error.explanation; GOTO Quit}; LocalFile => {msg ¬ "File must be global or have a global attachment"; GOTO Quit}; }; argv: CommanderOps.ArgumentVector; argv ¬ CommanderOps.Parse[cmd ! CommanderOps.Failed => {msg ¬ errorMsg; GOTO Quit}]; IF argv.argc # 2 THEN { msg ¬ "Usage: RegisterVoiceInterest inFile"; GOTO Quit; }; DoFileInterests[argv[1], cmd.out] EXITS Quit => RETURN [$Failure, msg] }}; BackgroundCommentaryInit: Commander.CommandProc ~ { backgroundCommentary ¬ cmd.out }; <<>> <> RegisterViewerInterests: PUBLIC PROC [viewer: ViewerClasses.Viewer, voiceList: Rope.ROPE] ~ { <> <> eventReg: ViewerEvents.EventRegistration; IF voiceList=NIL THEN { Commentate[" No voice in file\n", backgroundCommentary]; RETURN; }; ViewerOps.AddProp[viewer, $VoiceList, voiceList]; eventReg ¬ ViewerEvents.RegisterEventProc[proc: DoViewerInterests, event: save, filter: viewer, before: FALSE]; <> ViewerOps.AddProp[viewer, $VoiceEventReg, eventReg]; }; DoViewerInterests: ViewerEvents.EventProc ~ { <> globalName: Rope.ROPE; voiceList: Rope.ROPE ¬ NIL; createDate: BasicTime.GMT; [globalName, createDate] ¬ GetFileNameAndDate[viewer.file, backgroundCommentary]; voiceList ¬ NARROW[ViewerOps.FetchProp[viewer, $VoiceList]]; IF voiceList#NIL THEN RegisterFileInterests[globalName, createDate, voiceList, backgroundCommentary]; ViewerOps.AddProp[viewer, $VoiceList, NIL]; ViewerEvents.UnRegisterEventProc[proc: ViewerOps.FetchProp[viewer, $VoiceEventReg], event: save]; ViewerOps.AddProp[viewer, $VoiceEventReg, NIL]; }; <> <<>> <> <<>> <> <<>> <> <<>> <> <> <> <> <> <> <> <<};>> <<>> <> <<>> <> <> <> Commander.Register[key: "RegisterVoiceInterest", proc: RegisterVoiceInterestCmd, doc: "RegisterVoiceInterest inFile: register loganberry interests for all voice messages in a global or globally attached file"]; Commander.Register[key: "InterestInfo", proc: BackgroundCommentaryInit, doc: "InterestInfo: registers an output stream for commentary about voice interest registration"]; END.