VoiceAgingImpl.mesa
module which adds colors to and ages colors in voice viewers: the dictation ops buttons in voice viewers may use the ages of sections of a viewer to determine where to delete/playback/resume recording
Ades, April 25, 1986 12:18:32 pm PST
in each voice viewer is a list of ages: these are pairs of INTs and give the starting position for the age and its value. The age lasts until the next age entry's position or the end if last in the list.
DIRECTORY
Rope USING [ROPE, Length],
TextEdit USING [PutCharProp],
TextNode USING [Ref],
TiogaButtons USING [TextNodeRef],
TiogaOps USING [FirstChild, ViewerDoc, GetRope],
ViewerClasses USING [Viewer],
ViewerOps USING [FetchProp, PaintViewer],
VoiceViewers USING [VoiceViewerInfo, IntPair, voiceViewerInfoList, SetVoiceViewerEditStatus],
VoiceAging;
VoiceAgingImpl: CEDAR PROGRAM IMPORTS Rope, TextEdit, TiogaButtons, TiogaOps, ViewerOps, VoiceViewers EXPORTS VoiceAging = BEGIN
in each voice viewer is a list of ages: these are pairs of INTs and give the starting position for the age and its value. The age lasts until the next age entry's position or the end if last in the list.
when age lists are edited, adjacent entries with the same age values can clearly be merged. The valid ages are [oldest..youngest] as given below. youngest-1 is a transitory value used before a call to AgeAllViewers when voice is inserted. oldest+1 is used to mean "this viewer didn't age at all in the last call to AgeAllViewers: oldest+1 may only appear as a single element age list with a position of 0
the following values may be changed to anything you care for, so long as oldest>youngest
oldest: PUBLIC INT ← 5;
youngest: PUBLIC INT ← 1;
and provided you manually supply the correct number of colors required for this array -- see end of this module
ageColors: ARRAY [1..6] OF Rope.ROPE;
ReColorViewer: PUBLIC PROC [viewer: ViewerClasses.Viewer, voiceViewerInfo: VoiceViewers.VoiceViewerInfo ← NIL, repaint: BOOLEANFALSE] = {
a viewer has been edited and the age list for it has been updated appropriately: redo the color looks so that the age marks are once more in the correct places
node: TextNode.Ref ← TiogaButtons.TextNodeRef[TiogaOps.FirstChild[TiogaOps.ViewerDoc[viewer]]];
nodeLength: INT ← TiogaOps.GetRope[TiogaOps.FirstChild[TiogaOps.ViewerDoc[viewer]]].Length;
IF voiceViewerInfo = NIL THEN voiceViewerInfo ← NARROW[ViewerOps.FetchProp[viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
voiceViewerInfo.color ← viewer.column = color;
IF voiceViewerInfo.ageList = NIL OR ~voiceViewerInfo.color THEN RETURN;
TextEdit.PutCharProp[node, 0, $Postfix, NIL, nodeLength]; -- may not be necessary
FOR l: LIST OF VoiceViewers.IntPair ← voiceViewerInfo.ageList, l.rest WHILE l # NIL DO
TextEdit.PutCharProp[node, l.first.position, $Postfix, ageColors[l.first.age], IF l.rest # NIL THEN MIN[l.rest.first.position, nodeLength]-l.first.position ELSE nodeLength-l.first.position]
ENDLOOP;
VoiceViewers.SetVoiceViewerEditStatus[viewer];
**** I still haven't got right when to repaint and when not
ViewerOps.PaintViewer[viewer: viewer, hint: client, clearClient: TRUE, whatChanged: NIL]
};
AgeAllViewers: PUBLIC PROC [youngestViewer: ViewerClasses.Viewer] = {
an editing event has occured which ages all the voice viewers
FOR currInfo: VoiceViewers.VoiceViewerInfo ← VoiceViewers.voiceViewerInfoList, currInfo.nextInfoRec WHILE currInfo # NIL DO
l: LIST OF VoiceViewers.IntPair ← currInfo.ageList;
IF l # NIL THEN
IF ~(l.rest = NIL AND l.first.age = oldest+1) THEN
{ IF (l.rest = NIL AND l.first.age = oldest)
THEN
{ ReColorViewer[currInfo.viewer, currInfo, currInfo.viewer # youngestViewer];
l.first.age ← oldest+1 -- special treatment because this list may be the result of having cut out some newer voice in the most recent edit, in which case the colors need redrawing
}
ELSE
{ --in all other cases the ages are only allowed to be incremented to oldest
WHILE l # NIL DO
l.first.age ← MIN[l.first.age+1, oldest];
l ← l.rest
ENDLOOP;
l ← currInfo.ageList;
WHILE l # NIL AND l.rest # NIL DO --the dual WHILE condition is because we can delete the next entry and it could be the last
IF l.first.age = l.rest.first.age THEN l.rest ← l.rest.rest ELSE l ← l.rest
ENDLOOP;
ReColorViewer[currInfo.viewer, currInfo]
}
}
ENDLOOP
};
EditAges: PUBLIC PROC [viewerInfo: VoiceViewers.VoiceViewerInfo, unchangedHead, deleteChars, insertChars: INT] = {
this gets called when an edit is made to the contents of the viewer: it keeps the ageList in step with the edit. If insertChars # 0 then the new position is made of age youngest-1 so that it will become the youngest when the next call to AgeAllViewers is made
ageAtEndOfCut: INT;
atHead: BOOLEANTRUE;
l: LIST OF VoiceViewers.IntPair ← viewerInfo.ageList;
IF l = NIL THEN ERROR; 
WHILE l.rest # NIL AND l.rest.first.position < unchangedHead + deleteChars DO l ← l.rest ENDLOOP;
ageAtEndOfCut ← l.first.age;
WHILE atHead AND viewerInfo.ageList # NIL DO
SELECT viewerInfo.ageList.first.position FROM
>= unchangedHead+deleteChars => {viewerInfo.ageList.first.position ← viewerInfo.ageList.first.position + insertChars - deleteChars; atHead ← FALSE};
>= unchangedHead => viewerInfo.ageList ← viewerInfo.ageList.rest;
ENDCASE => atHead ← FALSE;
ENDLOOP;
IF viewerInfo.ageList # NIL THEN
{ l ← viewerInfo.ageList;
WHILE l # NIL AND l.rest # NIL DO --the dual WHILE condition is because we can delete the next entry and it could be the last
SELECT l.rest.first.position FROM
>= unchangedHead+deleteChars =>
{l.rest.first.position ← l.rest.first.position + insertChars - deleteChars;
l ← l.rest};
>= unchangedHead => l.rest ← l.rest.rest;
ENDCASE => l ← l.rest
ENDLOOP
};
IF viewerInfo.ageList = NIL OR viewerInfo.ageList.first.position >= unchangedHead + insertChars
THEN
{ IF viewerInfo.ageList = NIL OR viewerInfo.ageList.first.position # unchangedHead + insertChars THEN viewerInfo.ageList ← CONS [[unchangedHead + insertChars, ageAtEndOfCut], viewerInfo.ageList];
IF insertChars # 0 THEN viewerInfo.ageList ← CONS [[unchangedHead, youngest-1], viewerInfo.ageList]
}
ELSE
{ l ← viewerInfo.ageList;
WHILE l.rest # NIL AND l.rest.first.position < unchangedHead + insertChars DO l ← l.rest ENDLOOP;
IF l.rest = NIL OR l.rest.first.position # unchangedHead + insertChars THEN l.rest ← CONS [[unchangedHead + insertChars, ageAtEndOfCut], l.rest];
IF insertChars # 0 THEN l.rest ← CONS [[unchangedHead, youngest-1], l.rest]
};
FOR l ← viewerInfo.ageList, l.rest WHILE l # NIL DO IF l.first.age > oldest THEN l.first.age ← oldest ENDLOOP;
l ← viewerInfo.ageList;
WHILE l # NIL AND l.rest # NIL DO --the dual WHILE condition is because we can delete the next entry and it could be the last
IF l.first.age = l.rest.first.age THEN l.rest ← l.rest.rest ELSE l ← l.rest
ENDLOOP
};
ageColors[1] ← "0.13 0.97 0.96 textColor";
ageColors[2] ← "0.06 0.94 1.00 textColor";
ageColors[3] ← "0.99 1.00 1.00 textColor";
ageColors[4] ← "0.99 1.00 0.65 textColor";
ageColors[5] ← ageColors[6] ← "0.97 1.00 0.28 textColor"; -- last two must be the same
these are HSV values: colors can be generated experimentally using the ColorTool program
END.