VoiceViewersImpl.mesa
routines to create and delete viewers for editing voice and to copy voice back into normal tioga documents, plus a monitor routine for locking a viewer against simultaneous attempts to manipulate it
Ades, May 1, 1986 1:34:00 pm PDT
DIRECTORY
Convert USING [RopeFromInt],
IO USING [PutFR, int],
DictationOps USING [DictationOps],
MBQueue USING [CreateMenuEntry],
Menus USING [Menu, AppendMenuEntry, CreateEntry, MenuProc, CreateMenu],
MessageWindow USING [Blink, Append],
Rope USING [ROPE, Concat],
SoundList USING [SoundListFromIntervalSpecs, SoundChars],
SourceMarkerTracking USING [RegisterViewer],
TiogaOps USING [SaveSelA, SelectDocument, SetLooks, SetStyle, RestoreSelA, FirstChild, ViewerDoc],
TiogaOpsDefs USING [Location],
ViewerClasses USING [Viewer],
ViewerEvents USING [EventRegistration, RegisterEventProc, UnRegisterEventProc, EventProc],
ViewerLocks USING [CallUnderWriteLock],
ViewerOps USING [CreateViewer, AddProp, FetchProp, SetMenu, PaintViewer],
ViewerTools USING [TiogaContents, TiogaContentsRec, SetTiogaContents],
VoiceAging USING [youngest, oldest, AgeAllViewers, ReColorViewer],
VoiceInText USING [voiceButtonQueue, StoreVoiceAtSelection, CancelProc, DebugRope, DeleteSourceMarker, SaveRopeAtSourceMarker, thrushHandle],
VoiceMarkers USING [AddCharMark, DeleteCharMarks, TextListFromRope, RopeFromTextList, RedrawTextMarkers],
VoicePlayBack USING [PlayBackMenuProc, RemoveViewerReferences, RedrawViewer],
VoiceRecord USING [AddVoiceProc, DictationMachine],
VoiceRope USING [IntervalSpecs, Length, DescribeRope, VoiceRopeInterval],
WindowManager USING [colorDisplayOn],
VoiceViewers;
VoiceViewersImpl: CEDAR MONITOR IMPORTS Convert, DictationOps, IO, MBQueue, Menus, MessageWindow, Rope, SoundList, SourceMarkerTracking, TiogaOps, ViewerEvents, ViewerLocks, ViewerOps, ViewerTools, VoiceAging, VoiceInText, VoiceMarkers, VoicePlayBack, VoiceRecord, VoiceRope, WindowManager EXPORTS VoiceViewers = BEGIN
voiceViewerMenu: PUBLIC Menus.Menu;
CreateVoiceViewerMenu: PROC = {
voiceViewerMenu ← Menus.CreateMenu[];
Menus.AppendMenuEntry[voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "Add", VoiceRecord.AddVoiceProc]];
Menus.AppendMenuEntry[voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "Play", VoicePlayBack.PlayBackMenuProc]];
Menus.AppendMenuEntry[voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "STOP", VoiceInText.CancelProc]];
Menus.AppendMenuEntry[voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "Save", SaveVoice]];
Menus.AppendMenuEntry[voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "Store", StoreVoice]];
Menus.AppendMenuEntry[voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "Redraw", RedrawViewer]];
Menus.AppendMenuEntry[voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "Mark", VoiceMarkers.AddCharMark]];
Menus.AppendMenuEntry[voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "DeleteMarks", VoiceMarkers.DeleteCharMarks]];
Menus.AppendMenuEntry[voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "DictationMachine", VoiceRecord.DictationMachine]];
Menus.AppendMenuEntry[voiceViewerMenu, Menus.CreateEntry["DictationOps", DictationOps.DictationOps]];
};
voiceViewerInfoList: PUBLIC VoiceViewers.VoiceViewerInfo ← NIL;
BuildVoiceViewer: PUBLIC PROC [voiceID: Rope.ROPE, textInVoice: Rope.ROPE, youngVoice: BOOLEAN] RETURNS [viewer: ViewerClasses.Viewer, viewerInfo: VoiceViewers.VoiceViewerInfo, viewerNumber: INT] = {
viewerInfo ← InsertVoiceViewerInfo[];
viewer ← ViewerOps.CreateViewer[
flavor: $Text,
info: [ name: Rope.Concat["Sound Viewer #", Convert.RopeFromInt[viewerInfo.viewerNumber]],
 column: IF WindowManager.colorDisplayOn THEN color ELSE left,
 iconic: FALSE
 icon: create one of these
],
paint: TRUE];
viewer.inhibitDestroy ← FALSE;
viewerNumber ← viewerInfo.viewerNumber;
ViewerOps.AddProp[viewer, $voiceViewerInfo, viewerInfo];
viewerInfo.viewer ← viewer;
viewerInfo.color ← viewer.column = color;
ViewerOps.SetMenu[viewer, voiceViewerMenu];
viewerInfo.destroyEvent ← ViewerEvents.RegisterEventProc[
   proc: DestroyViewerEvent, event: destroy, filter: viewer, before: TRUE];
viewerInfo.changeColumnEvent ← ViewerEvents.RegisterEventProc[
   proc: ChangeColumnEvent, event: changeColumn, filter: viewer, before: FALSE];
SetViewerContents[viewer, viewerInfo, voiceID, textInVoice, youngVoice];
MakeNotEdited[viewer]
};
SetViewerContents: PUBLIC PROC [viewer: ViewerClasses.Viewer, viewerInfo: VoiceViewers.VoiceViewerInfo, voiceID: Rope.ROPE, textInVoice: Rope.ROPE, youngVoice: BOOLEAN] = {
viewercontents: ViewerTools.TiogaContents ← NEW[ViewerTools.TiogaContentsRec ← []];
intervals: VoiceRope.IntervalSpecs;
viewerInfo.soundList ← NIL;
IF voiceID # NIL THEN
{ -- **** ought to check here that the rope actually exists
viewerInfo.ropeInterval ← [voiceID, 0, VoiceRope.Length[VoiceInText.thrushHandle, NEW [VoiceRope.VoiceRopeInterval ← [voiceID, 0, 0]]]];
intervals ← VoiceRope.DescribeRope[VoiceInText.thrushHandle, NEW [VoiceRope.VoiceRopeInterval ← viewerInfo.ropeInterval]];
viewerInfo.soundList ← SoundList.SoundListFromIntervalSpecs[intervals, viewerInfo.ropeInterval.length];
[soundRope: viewercontents.contents, remnant: viewerInfo.remnant] ← SoundList.SoundChars[viewerInfo];
viewerInfo.textMarkList ← VoiceMarkers.TextListFromRope[textInVoice];
viewerInfo.ageList ← CONS [[0, IF youngVoice THEN VoiceAging.youngest-1 ELSE VoiceAging.oldest], NIL]
}
ELSE
{ viewercontents.contents ← " ";
viewerInfo.ropeInterval ← [NIL, 0, 0];
};
ViewerTools.SetTiogaContents[viewer, viewercontents];
TiogaOps.SaveSelA[];
TiogaOps.SelectDocument[viewer];
TiogaOps.SetStyle["voiceProfile", root];
TiogaOps.SetLooks["v"];
TiogaOps.RestoreSelA[];
VoiceMarkers.RedrawTextMarkers[viewer, TiogaOps.FirstChild[TiogaOps.ViewerDoc[viewer]]];
IF voiceID # NIL AND viewerInfo.ageList.first.age = VoiceAging.youngest-1 THEN VoiceAging.AgeAllViewers[viewer] ELSE VoiceAging.ReColorViewer[viewer, viewerInfo];
**** the Plass-bug: shouldn't be necessary and he says he'll have a look at it
IF viewerInfo.textMarkList # NIL THEN ViewerOps.PaintViewer[viewer: viewer, hint: client, clearClient: TRUE, whatChanged: NIL];
viewerInfo.editInProgress ← (voiceID = NIL) -- can do this because the viewer is created with editInProgress = TRUE, so nobody else can have grabbed the lock
};
MakeNotEdited: PROC [viewer: ViewerClasses.Viewer] = {
this procedure will make a voice viewer think itself unedited, when we wish to assert that there is no voice rope held only in it - use carefully!!
DoIt: PROC = {
viewerInfo: VoiceViewers.VoiceViewerInfo ← NARROW[ViewerOps.FetchProp[viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
viewerInfo.edited ← viewer.newVersion ← viewer.newFile ← FALSE;
ViewerOps.PaintViewer[viewer, caption, FALSE];
};
ViewerLocks.CallUnderWriteLock[DoIt, viewer]
};
SetVoiceViewerEditStatus: PUBLIC PROC [viewer: ViewerClasses.Viewer] = {
this is similar to the above, except that it ensures that the viewer has the same idea about whether the voice is edited as the VoiceInfo record. For use by functions [such as moving a visual cue along a voice viewer during playback] which need to edit the viewer but not the voice and then want to reflect this fact in the viewer's status
DoIt: PROC = {
viewerInfo: VoiceViewers.VoiceViewerInfo ← NARROW[ViewerOps.FetchProp[viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
IF viewerInfo.edited # viewer.newVersion THEN
{ viewer.newVersion ← viewerInfo.edited;
IF ~viewerInfo.edited THEN viewer.newFile ← FALSE;
ViewerOps.PaintViewer[viewer, caption, FALSE];
}
};
ViewerLocks.CallUnderWriteLock[DoIt, viewer]
};
SetParentViewer: PUBLIC PROC [viewerInfo: VoiceViewers.VoiceViewerInfo, parentViewer: ViewerClasses.Viewer, positionInParent: TiogaOpsDefs.Location] = {
viewerInfo.parentViewer ← parentViewer;
viewerInfo.positionInParent ← positionInParent;
SourceMarkerTracking.RegisterViewer[parentViewer]
};
RemoveParentViewer: PUBLIC PROC [viewerNumber: INT] = {
FOR info: VoiceViewers.VoiceViewerInfo ← voiceViewerInfoList, info.nextInfoRec WHILE info # NIL DO
IF info.viewerNumber = viewerNumber THEN {info.parentViewer ← NIL; RETURN}
ENDLOOP
};
InsertVoiceViewerInfo: PROC RETURNS [newViewerInfo: VoiceViewers.VoiceViewerInfo] = {
restOfList: VoiceViewers.VoiceViewerInfo ← voiceViewerInfoList;
newViewerInfo ← NEW[VoiceViewers.VoiceViewerInfoRec];
IF voiceViewerInfoList = NIL
THEN
{ newViewerInfo.viewerNumber ← 1;
voiceViewerInfoList ← newViewerInfo;
RETURN
};
IF voiceViewerInfoList.viewerNumber > 1 THEN
{ newViewerInfo.viewerNumber ← 1;
newViewerInfo.nextInfoRec ← voiceViewerInfoList;
voiceViewerInfoList ← newViewerInfo;
RETURN
};
DO
IF restOfList.nextInfoRec = NIL
OR restOfList.nextInfoRec.viewerNumber > restOfList.viewerNumber + 1
THEN
{ newViewerInfo.viewerNumber ← restOfList.viewerNumber + 1;
newViewerInfo.nextInfoRec ← restOfList.nextInfoRec;
restOfList.nextInfoRec ← newViewerInfo;
RETURN
};
restOfList ← restOfList.nextInfoRec
ENDLOOP
};
StoreVoice: Menus.MenuProc = {
**** locking against edits in this and the next one?
viewerInfo: VoiceViewers.VoiceViewerInfo ← NARROW[ViewerOps.FetchProp[NARROW[parent], $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
oldParentViewer: ViewerClasses.Viewer ← viewerInfo.parentViewer;
oldPosition: TiogaOpsDefs.Location ← viewerInfo.positionInParent;
IF VoiceInText.StoreVoiceAtSelection[viewerInfo]
THEN
{ MakeNotEdited[NARROW[parent]];
IF oldParentViewer # NIL THEN VoiceInText.DeleteSourceMarker[oldParentViewer, oldPosition, viewerInfo.viewerNumber]
}
};
SaveVoice: Menus.MenuProc = {
viewer: ViewerClasses.Viewer ← NARROW[parent];
viewerInfo: VoiceViewers.VoiceViewerInfo ← NARROW[ViewerOps.FetchProp[viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
IF ~viewerInfo.edited THEN
{ MessageWindow.Append["voice viewer is not edited", TRUE];
RETURN
};
IF viewerInfo.parentViewer = NIL THEN
{ MessageWindow.Append["voice viewer has no parent text viewer", TRUE];
MessageWindow.Blink[];
RETURN
};
IF ~VoiceInText.SaveRopeAtSourceMarker[viewerInfo.parentViewer, viewerInfo.positionInParent, viewerInfo.viewerNumber, viewerInfo.ropeInterval.ropeID, VoiceMarkers.RopeFromTextList[viewerInfo.textMarkList]] THEN
{
MessageWindow.Append["unable to find source marker for voice window: voice not saved", TRUE];
MessageWindow.Blink[];
RETURN
};
MakeNotEdited[viewer]
};
RemoveVoiceViewerInfo: PROC [viewerInfo: VoiceViewers.VoiceViewerInfo] = {
IF voiceViewerInfoList = NIL THEN ERROR;
IF voiceViewerInfoList = viewerInfo
THEN voiceViewerInfoList ← viewerInfo.nextInfoRec
ELSE
{ ptrToCurrInfo: VoiceViewers.VoiceViewerInfo ← voiceViewerInfoList;
currInfo: VoiceViewers.VoiceViewerInfo ← voiceViewerInfoList.nextInfoRec;
DO
IF currInfo = NIL THEN ERROR;
IF currInfo = viewerInfo
THEN
{ ptrToCurrInfo.nextInfoRec ← viewerInfo.nextInfoRec;
RETURN
};
ptrToCurrInfo ← currInfo;
currInfo ← currInfo.nextInfoRec
ENDLOOP
}
};
DestroyViewerEvent: ViewerEvents.EventProc = { RETURN [~DestroyVoiceInfo[viewer]] };
DestroyVoiceInfo: PROC [viewer: ViewerClasses.Viewer] RETURNS [didIt: BOOLEANTRUE] = {
viewerInfo: VoiceViewers.VoiceViewerInfo ← NARROW[ViewerOps.FetchProp[viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
IF ~VoicePlayBack.RemoveViewerReferences[viewer] THEN
{ MessageWindow.Append["Wait until voice cue disappears before destroying viewer", TRUE];
MessageWindow.Blink[];
RETURN [FALSE]
};
should check also whether viewer is undergoing editing
IF viewerInfo.parentViewer = NIL
THEN VoiceInText.DebugRope[IO.PutFR["Voice viewer %d had no parent\n", IO.int[viewerInfo.viewerNumber]]]
ELSE VoiceInText.DeleteSourceMarker[viewerInfo.parentViewer, viewerInfo.positionInParent, viewerInfo.viewerNumber];
RemoveVoiceViewerInfo[viewerInfo];
ViewerEvents.UnRegisterEventProc[viewerInfo.destroyEvent, destroy];
ViewerEvents.UnRegisterEventProc[viewerInfo.changeColumnEvent, changeColumn];
};
RedrawViewer: Menus.MenuProc = {
viewer: ViewerClasses.Viewer ← NARROW[parent];
viewerInfo: VoiceViewers.VoiceViewerInfo ← NARROW[ViewerOps.FetchProp[viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
IF GetVoiceLock[viewerInfo]
THEN
{ trueContents: Rope.ROPE ← SoundList.SoundChars[viewerInfo].soundRope;
[] ← VoicePlayBack.RedrawViewer[viewer, trueContents, 0, 0, 0, viewerInfo.remnant, FALSE, unAltered];
SetVoiceViewerEditStatus[viewer];
viewerInfo.editInProgress ← FALSE
}
};
ChangeColumnEvent: ViewerEvents.EventProc = {
viewerInfo: VoiceViewers.VoiceViewerInfo ← NARROW[ViewerOps.FetchProp[viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
IF (viewer.column = color) # viewerInfo.color THEN
{ viewerInfo.color ← viewer.column = color;
IF GetVoiceLock[viewerInfo]
THEN
{ trueContents: Rope.ROPE ← SoundList.SoundChars[viewerInfo].soundRope;
[] ← VoicePlayBack.RedrawViewer[viewer, trueContents, 0, 0, 0, viewerInfo.remnant, FALSE, unAltered];
SetVoiceViewerEditStatus[viewer];
viewerInfo.editInProgress ← FALSE
}
ELSE
{ MessageWindow.Append["Cannot repaint viewer during edit: click REDRAW to change window between color and monchrome if the wrong state persists", TRUE];
MessageWindow.Blink[]
}
}
};

GetVoiceLock: PUBLIC ENTRY PROC [info: VoiceViewers.VoiceViewerInfo] RETURNS [gotIt: BOOLEAN] = {
gotIt ← ~ info.editInProgress;
IF info.editInProgress
THEN MessageWindow.Append["Try again when the viewer's contents are stable", TRUE]
ELSE info.editInProgress ← TRUE;
};
CreateVoiceViewerMenu[];
END.