NarratedDocsImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Polle Zellweger (PTZ) January 6, 1987 7:20:47 pm PST
DIRECTORY
Atom USING [ PropList ],
BasicTime USING [ Now, ToPupTime ],
GList USING [ Append, Member, Remove ],
IO,
Jukebox USING [ bytesPerMS ],
MessageWindow USING [ Append, Blink ],
Process USING [ MsecToTicks, Pause ],
Rope,
TEditSelectionOpsEtc USING [ ShowGivenPositionRange ],
TextEdit USING [ GetCharProp, PutCharProp, PutProp, Size ],
TextNode USING [ LocNumber, Ref, StepForward ],
TiogaAccess USING [ EndOf, FromViewer, Get, GetIndex, Reader, TiogaChar ],
TiogaButtons USING [ TextNodeRef, TiogaOpsRef ],
TiogaOps USING [ CallWithLocks, GetSelection, Location, LocOffset, LocRelative, NoSelection, Ref, SelectionGrain, SetSelection, ViewerDoc ],
ViewerClasses USING [ Viewer ],
ViewerOps USING [ FetchProp ],
ViewRec USING [ ViewRef ],
VoiceInText USING [ PlaySelection, thrushHandle ],
VoiceRope USING [ Length, Stop, VoiceRopeInterval ]
;
NarratedDocsImpl:
CEDAR
PROGRAM
IMPORTS BasicTime, GList, IO, MessageWindow, Process, Rope, TEditSelectionOpsEtc, TextEdit, TextNode, TiogaAccess, TiogaButtons, TiogaOps, ViewerOps, ViewRec, VoiceInText, VoiceRope
EXPORTS NarratedDocs
= {
Types
ScriptList: TYPE ~ LIST OF Script;
Script: TYPE ~ REF ScriptBody;
ScriptBody:
TYPE ~
RECORD [
do we record a filename and/or viewer here, or do we require the user to click in the scripted viewer to give us a pointer?
sid: ScriptID ← NIL,
sName: ScriptName ← "",
sDesc: ScriptDesc ←
NIL,
may be good to have entry descs too, so user can figure out what is going on. But would these be voice rope descs & uniquefied for multiple copies in a single script, or would they be an entry desc? Note that the same voice rope can be attached to different places in the same document and appear in a script at each location, which is of course different than the same annotation appearing multiple times in a script. So an entry is described by (script, seqNum, docLoc, voiceRope). voiceRope can be determined from docLoc, but is not VISIBLE from it. Navigating in a Complex Invisible Space.
sCreator: ScriptCreator ← "",
numEntries: INT ← 0,
firstEntry, lastEntry: ScriptEntry ← NIL,
file: FileID
];
ScriptID: TYPE = INT;
ScriptName: TYPE ~ Rope.ROPE;
ScriptDesc: TYPE ~ Rope.ROPE;
ScriptCreator: TYPE ~ Rope.ROPE;
ScriptEntry:
TYPE ~
REF ScriptEntryBody;
these are an internal representation for the script tool
ScriptEntryBody:
TYPE ~
RECORD [
entryID: EntryID ← NIL,
file: FileID ← NIL,
next, prev: ScriptEntry ← NIL,
pauseBefore: INT ← 0, -- in msec (is sec more reasonable?)
pauseAfter: INT ← 0, -- ditto
action: Rope.ROPE ← "",
charIndex: INT ← -1, -- trust this only if document is not edited
seqNum: INT ← -1 -- trust this only if document is not edited
];
ScriptExt:
TYPE ~
REF ScriptExtBody;
these are an external representation that will be stored (as $scriptList property) on root
EntryID:
TYPE ~
REF EntryIDBody;
these are stored (in a list, as $script property) on characters in the document
EntryIDBody:
TYPE ~
RECORD [
sid: ScriptName ← "",
eid: INT ← 0
];
EntryIDList: TYPE ~ LIST OF EntryID;
FileID:
TYPE =
RECORD [
name: Rope.ROPE ← "",
createTime: BasicTime.GMT ← BasicTime.nullGMT
];
ScriptTool: TYPE = REF ScriptToolBody;
ScriptToolBody:
TYPE =
RECORD [
scriptName: ScriptName ← "",
playScript: PROC,
stop: PROC,
seqNum: INT ← 0,
playEntry: PROC,
findEntry: PROC,
nextEntry: PROC,
prevEntry: PROC,
createScript: PROC,
destroyScript: PROC,
action: Rope.ROPE ← "",
time: INT ← 0,
addEntry: PROC,
deleteEntry: PROC,
listScripts: PROC,
listEntries: PROC,
extractScript: PROC,
applyScript: PROC,
msg: Rope.ROPE
];
Declarations
scriptList: ScriptList ← NIL;
Script Creation and Editing
GList operations assume type = LIST OF REF X for some X.
Is seqNum> script.numEntries an ERROR or a way to specify the end of the list?
scriptStyleParam: Rope.ROPE ← "0 outlineBoxBearoff 1 outlineBoxThickness";
SuitableViewer:
PROC [selectedViewer: ViewerClasses.Viewer]
RETURNS [
BOOLEAN] = {
Copied from VoiceInTextImpl
RETURN [selectedViewer.class.flavor = $Text AND ViewerOps.FetchProp[selectedViewer, $voiceViewerInfo] = NIL] };
InsertSelectedAnnotationsAfter:
PUBLIC
PROC [script: Script, seqNum:
INT]
RETURNS [newSeqNum:
INT] ~ {
Add all voice annotations in selection to script in order following seqNum.
Can't use TiogaAccess because need to change char props.
selectedViewer: ViewerClasses.Viewer;
suitableViewer: BOOLEAN;
voiceThere: BOOLEAN;
AddSelectedAnnotations:
PROC [root: TiogaOps.Ref] = {
charsInSelection: INT ← 0;
soundsInSelection: INT ← 0;
startChar, endChar, targetChar: TiogaOps.Location;
node: TextNode.Ref;
caretBefore: BOOLEAN;
pendingDelete: BOOLEAN;
level: TiogaOps.SelectionGrain;
[viewer: selectedViewer, start: startChar, end: endChar, caretBefore: caretBefore, pendingDelete: pendingDelete, level: level] ← TiogaOps.GetSelection[];
suitableViewer ← SuitableViewer[selectedViewer];
IF suitableViewer
AND pendingDelete
THEN {
TiogaOps.SetSelection[viewer: selectedViewer, start: startChar, end: endChar,
level: level, caretBefore: caretBefore,
pendingDelete: FALSE, which: primary] -- simply makes not pending delete
};
IF suitableViewer
AND
NOT level = point
THEN {
targetChar ← startChar;
DO
charsInSelection ← charsInSelection + 1;
node ← TiogaButtons.TextNodeRef[targetChar.node]; -- just a type converter
voiceThere ← TextEdit.GetCharProp[node, targetChar.where, $voice] # NIL;
IF voiceThere
THEN {
newEntryID: EntryID ← MakeNewEntryID[script];
newEntryIDList: EntryIDList← LIST[newEntryID];
Add annotation to script: char props
entryList: EntryIDList ← NARROW[TextEdit.GetCharProp[node, targetChar.where, $script]];
entryList ← NARROW[GList.Append[entryList, newEntryIDList]];
TextEdit.PutCharProp[node, targetChar.where, $script, entryList];
TextEdit.PutCharProp[node, targetChar.where, $Postfix, scriptStyleParam];
TextEdit.PutCharProp[node, targetChar.where, $Artwork,
NIL];
Give user visual feedback that this voice entry is in some script. WHICH script is a bit more complicated than this.
There are also some real difficulties with crossing document boundaries -- when to resolve the references? Could be editing 2 anonymous viewers and building up a script across both of them. So even at the SAVE of one, you don't know what the filename of the other is. And if you did, the user could change it thereafter. Damn! Obviously need something like Interest relations to manage this.
AddEntryViaSeqNum[newEntryID, script, seqNum];
seqNum ← seqNum + 1;
soundsInSelection ← soundsInSelection + 1;
};
IF targetChar = endChar THEN EXIT;
targetChar ← TiogaOps.LocRelative[location: targetChar, count: 1,
break: 1, skipCommentNodes: FALSE];
ENDLOOP;
};
IF soundsInSelection = 0
THEN
MessageWindow.Append["No sounds in selection", TRUE]
ELSE
ScriptToolMsg[IO.PutFR["%d entries added to script\n", IO.int[soundsInSelection]]];
Update seqNum field in tool if middle moused?
};
TiogaOps.CallWithLocks[AddSelectedAnnotations ! TiogaOps.NoSelection => {suitableViewer ← FALSE; CONTINUE}];
test for failure conditions and report them to the user after releasing the viewer lock
IF
NOT suitableViewer
THEN {
MessageWindow.Append["Make a selection in a Tioga viewer first", TRUE];
MessageWindow.Blink[];
newSeqNum ← -1; -- This may be a dumb thing to do
}
ELSE newSeqNum ← seqNum;
};
DeleteSelectedAnnotations:
PUBLIC
PROC [script: Script, seqNum:
INT]
RETURNS [newSeqNum:
INT] ~ {
Delete the sequence of voice annotations in selection from script starting at seqNum
selectedViewer: ViewerClasses.Viewer;
suitableViewer: BOOLEAN;
voiceThere: BOOLEAN;
RemoveSelectedAnnotations:
PROC [root: TiogaOps.Ref] = {
charsInSelection: INT ← 0;
soundsInSelection: INT ← 0;
startChar, endChar, targetChar: TiogaOps.Location;
node: TextNode.Ref;
caretBefore: BOOLEAN;
pendingDelete: BOOLEAN;
level: TiogaOps.SelectionGrain;
[viewer: selectedViewer, start: startChar, end: endChar, caretBefore: caretBefore, pendingDelete: pendingDelete, level: level] ← TiogaOps.GetSelection[];
suitableViewer ← SuitableViewer[selectedViewer];
IF suitableViewer
AND pendingDelete
THEN {
TiogaOps.SetSelection[viewer: selectedViewer, start: startChar, end: endChar,
level: level, caretBefore: caretBefore,
pendingDelete: FALSE, which: primary] -- simply makes not pending delete
};
IF suitableViewer
AND
NOT level = point
THEN {
targetChar ← startChar;
DO
charsInSelection ← charsInSelection + 1;
node ← TiogaButtons.TextNodeRef[targetChar.node]; -- just a type converter
voiceThere ← TextEdit.GetCharProp[node, targetChar.where, $voice] # NIL;
IF voiceThere
THEN {
oldEntryID: EntryID ← GetEntryIDFromSeqNum[script, seqNum];
Remove annotation from script: char props
entryList: EntryIDList ← NARROW[TextEdit.GetCharProp[node, targetChar.where, $script]];
IF GList.Member[oldEntryID, entryList]
THEN
entryList ← NARROW[GList.Remove[oldEntryID, entryList]]
ELSE
EXIT; -- No more in sequence. Note that seqNum stays constant throughout this operation
TextEdit.PutCharProp[node, targetChar.where, $script, entryList];
IF entryList =
NIL
THEN {
TextEdit.PutCharProp[node, targetChar.where, $Postfix, NIL];
TextEdit.PutCharProp[node, targetChar.where, $Artwork, NARROW["TalksBubble", Rope.ROPE]]; -- the NARROW prevents a REF TEXT being created
Give user visual feedback that this voice entry is no longer in any script. WHICH script is a bit more complicated than this.
};
DeleteEntryViaSeqNum[script, seqNum];
soundsInSelection ← soundsInSelection + 1;
};
IF seqNum > script.numEntries
THEN seqNum ← script.numEntries;
removed last entry or seqNum out of range!
IF targetChar = endChar THEN EXIT;
targetChar ← TiogaOps.LocRelative[location: targetChar, count: 1,
break: 1, skipCommentNodes: FALSE];
ENDLOOP;
};
IF soundsInSelection = 0
THEN
MessageWindow.Append["No sounds in selection", TRUE]
ELSE
ScriptToolMsg[IO.PutFR["%d entries removed from script\n", IO.int[soundsInSelection]]];
Update seqNum field in tool if middle moused?
};
TiogaOps.CallWithLocks[RemoveSelectedAnnotations ! TiogaOps.NoSelection => {suitableViewer ← FALSE; CONTINUE}];
test for failure conditions and report them to the user after releasing the viewer lock
IF
NOT suitableViewer
THEN {
MessageWindow.Append["Make a selection in a Tioga viewer first", TRUE];
MessageWindow.Blink[];
newSeqNum ← -1; -- This may be a dumb thing to do
}
ELSE newSeqNum ← seqNum;
};
AddEntryViaSeqNum:
PROC [entryID: EntryID, script: Script, seqNum:
INT] ~ {
Add entry to script AFTER seqNum.
newEntry, curPtr: ScriptEntry;
IF seqNum < 0 THEN ERROR; --
newEntry ← NEW[ScriptEntryBody ← [entryID: entryID, next: NIL, prev: NIL] ];
IF seqNum = 0
THEN {
newEntry.next ← script.firstEntry;
newEntry.prev ← NIL;
script.firstEntry ← newEntry;
}
ELSE {
curPtr ← FindPositionOfSeqNum[script, seqNum];
newEntry.next ← curPtr.next;
curPtr.next ← newEntry;
newEntry.prev ← curPtr;
};
IF newEntry.next #
NIL
THEN
newEntry.next.prev ← newEntry
ELSE {
IF seqNum # script.numEntries THEN ERROR; --
script.lastEntry ← newEntry;
};
script.numEntries ← script.numEntries + 1;
};
DeleteEntryViaSeqNum:
PROC [script: Script, seqNum:
INT] ~ {
Delete entry seqNum from script.
curPtr: ScriptEntry;
IF seqNum <= 0 THEN ERROR; -- other checks could be done too
curPtr ← FindPositionOfSeqNum[script, seqNum];
IF curPtr.prev =
NIL
THEN {
script.firstEntry ← curPtr.next;
}
ELSE
curPtr.prev.next ← curPtr.next;
IF curPtr.next #
NIL
THEN
curPtr.next.prev ← curPtr.prev
ELSE {
script.lastEntry ← curPtr.prev;
};
script.numEntries ← script.numEntries - 1;
};
FindPositionOfSeqNum:
PROC [script: Script, seqNum:
INT]
RETURNS [s: ScriptEntry] ~ {
IF seqNum < script.numEntries/2
THEN {
-- go forward from start
s ← script.firstEntry;
FOR n:
INT
IN (1..seqNum]
DO
s ← s.next;
ENDLOOP;
}
ELSE {
-- go backward from end
s ← script.lastEntry;
FOR n:
INT
DECREASING
IN [seqNum..script.numEntries)
DO
s ← s.prev;
ENDLOOP;
};
};
GetEntryIDFromSeqNum:
PROC [script: Script, seqNum:
INT]
RETURNS [entryID: EntryID] ~ {
curPtr: ScriptEntry ← FindPositionOfSeqNum[script, seqNum];
RETURN[curPtr.entryID]
};
GetSeqNumFromEntryID:
PROC [script: Script, entryID: EntryID]
RETURNS [seqNum:
INT] ~ {
seqNum ← 0;
FOR se: ScriptEntry ← script.firstEntry, se.next
WHILE se #
NIL
DO
seqNum ← seqNum + 1;
IF EqualEntryID[se.entryID, entryID] THEN EXIT;
ENDLOOP;
};
GetScriptEntryFromEntryID:
PROC [script: Script, entryID: EntryID]
RETURNS [se: ScriptEntry] ~ {
FOR se ← script.firstEntry, se.next
WHILE se #
NIL
DO
IF EqualEntryID[se.entryID, entryID] THEN EXIT;
ENDLOOP;
};
EqualEntryID:
PROC [e1, e2: EntryID]
RETURNS [
BOOL] ~ {
RETURN[e1.sid = e2.sid AND e1.eid = e2.eid];
};
MakeNewEntryID:
PROC [script: Script]
RETURNS [entryID: EntryID] ~ {
entryID ← NEW[EntryIDBody ← [sid: script.scriptName, eid: script.numEntries + 1]];
};
CreateNewScript:
PROC [scriptName: Rope.
ROPE]
RETURNS [new:
BOOL ←
TRUE] ~ {
IF LookupScript[scriptName]#NIL THEN RETURN[FALSE]
ELSE
script ← NEW[ScriptBody ← [scriptName: scriptName] ];
must ensure that there aren't 2 scripts with the same name for the same doc!! Complicated in the presence of multiple docs, private/public scripts, etc.
Good enough for now, but should include something about filename/creator/machinename later.
scriptList ← CONS[script, scriptList];
};
GetCharIndexFromEntryID:
PROC [viewer: ViewerClasses.Viewer, entryID: EntryID]
RETURNS [charIndex:
INT ← -1] ~ {
wholeFile: TiogaAccess.Reader ← TiogaAccess.FromViewer[viewer];
c: TiogaAccess.TiogaChar;
props: Atom.PropList;
WHILE charIndex < 0
DO
IF TiogaAccess.EndOf[wholeFile] THEN EXIT;
c ← TiogaAccess.Get[wholeFile];
FOR props ← c.propList, props.rest
WHILE props #
NIL
DO
IF props.first.key = $script
AND GList.Member[entryID, props.first.val]
THEN
GList.Member just checks ref equality, not the internal representation. This is only ok until scripts are stored and recreated.
charIndex ← TiogaAccess.GetIndex[wholeFile];
ENDLOOP;
ENDLOOP;
charIndex ← charIndex -1;
};
CheckPropProc: TYPE ~ PROC [r: REF, rlist: REF] RETURNS [BOOL];
CProp: CheckPropProc ~ {
e: EntryID ← NARROW[r];
FOR elist: EntryIDList ←
NARROW[rlist], elist.rest
WHILE elist #
NIL
DO
IF EqualEntryID[elist.first, e] THEN RETURN [TRUE];
ENDLOOP;
RETURN [FALSE]
};
FindCharProp:
PROC [viewer: ViewerClasses.Viewer, propName:
ATOM, propVal:
REF, checkPropProc: CheckPropProc]
RETURNS [foundLoc: TiogaOps.Location] ~ {
Searches document forward from the beginning for occurrence of character property propName with value propVal.
rootNode: TextNode.Ref ← TiogaButtons.TextNodeRef[TiogaOps.ViewerDoc[viewer]]; -- TextNodeRef is just a type convertor - replace with a more cautious implementation??
foundLoc ← [NIL, -1];
FOR n: TextNode.Ref ← rootNode, TextNode.StepForward[n]
UNTIL n =
NIL
DO
SearchCharProps:
PROC
RETURNS [found:
BOOLEAN ←
FALSE] ~ {
FOR i:
INT
IN [startChar..endChar)
UNTIL found
DO
propList: REF ← TextEdit.GetCharProp[n, i, propName];
IF checkPropProc[r: propVal, rlist: propList] THEN {foundLoc.where ← i; found ← TRUE;}
ENDLOOP;
};
startChar: INT ← 0;
endChar: INT ← TextEdit.Size[n];
IF n.hascharprops
AND SearchCharProps[].found
THEN {
foundLoc.node ← TiogaButtons.TiogaOpsRef[n];
EXIT;
};
ENDLOOP;
};
GetSeqNumsFromSelection:
PROC [script: Script, seqNum:
INT]
RETURNS [s: ScriptEntry] ~ {
.....
seqNumList: LIST OF INT;
node: TextNode.Ref;
targetChar: TiogaOps.Location;
FOR entryList: EntryIDList ←
NARROW[TextEdit.GetCharProp[node, targetChar.where, $script]], entryList.rest
DO
seqNumList ←
CONS[GetSeqNumFromEntryID[script, entryList.first], seqNumList];
TYPES ARE ALL WRONG HERE!!!
ENDLOOP;
.....
if seqNumList has one elt, fill in seqNum field in tool, else clear seqNum field and display seqNumList in msg area
};
StoreScriptsRepAtRoot:
PROC [viewer: ViewerClasses.Viewer] ~ {
Document has <$script, entryUIDList> properties scattered throughout it. Script Tool has linked list of <entryUID, other fields>
IF document has been edited THEN CheckScriptsForDuplicates[];
rootNode: TextNode.Ref ← TiogaButtons.TextNodeRef[TiogaOps.ViewerDoc[viewer]]; -- TextNodeRef is just a type convertor - replace with a more cautious implementation?? [several references throughout TiogaVoice]
FillInCharIndices[rootNode];
stream: IO.STREAM ← IO.ROS[];
FOR s: Script ← scriptList.first, s.rest
WHILE s #
NIL
DO
WriteOneScript[s, stream];
ENDLOOP;
TextEdit.PutProp[rootNode, $scriptList, IO.RopeFromROS[stream]];
};
TraverseCharProps:
PROC [viewer: ViewerClasses.Viewer, propName:
ATOM, propVal:
REF, checkPropProc: CheckPropProc]
RETURNS [foundLoc: TiogaOps.Location] ~ {
Searches document forward from the beginning for occurrence of character property propName with value propVal.
rootNode: TextNode.Ref ← TiogaButtons.TextNodeRef[TiogaOps.ViewerDoc[viewer]]; -- TextNodeRef is just a type convertor - replace with a more cautious implementation??
foundLoc ← [NIL, -1];
FOR n: TextNode.Ref ← rootNode, TextNode.StepForward[n]
UNTIL n =
NIL
DO
startChar: INT ← 0;
endChar: INT ← TextEdit.Size[n];
IF n.hascharprops
THEN {
FOR i:
INT
IN [startChar..endChar)
DO
propList: REF ← TextEdit.GetCharProp[n, i, $script];
IF GetProp[r: propVal, rlist: propList] THEN {foundLoc.where ← i; found ← TRUE;}
ENDLOOP;
};
ENDLOOP;
};
GetProp: CheckPropProc ~ {
PROC [r: REF, rlist: REF] RETURNS [BOOL];
FOR elist: EntryIDList ←
NARROW[rlist], elist.rest
WHILE elist #
NIL
DO
e: EntryID ← elist.first;
s: Script ← LookupScript[e.sid];
se: ScriptEntry ← GetScriptEntryFromEntryID[s, e.entryID];
se.charIndex ← TiogaOps.LocOffset[[FirstChild[rootNode],0], [n, i]];
ENDLOOP;
RETURN [FALSE]
};
WriteOneScript:
PROC [s: Script, stream:
IO.
STREAM] ~ {
stream.Put["(", Convert.RopeFromRope[s.scriptName], " ", Convert.RopeFromRope[s.scriptDesc], " "];
stream.Put[Convert.RopeFromInt[s.numEntries, " "];
FOR se: ScriptEntry ← s.firstEntry, se.next
DO
WriteOneEntry[se, stream];
ENDLOOP;
stream.Put[")"];
};
WriteOneEntry:
PROC [se: ScriptEntry, stream:
IO.
STREAM] ~ {
stream.Put["(", Convert.RopeFromInt[se.entryID.eid], " ", Convert.RopeFromInt[se.charIndex], ")"];
};
LookupScript:
PROC [scriptName: ScriptName]
RETURNS [script: Script ←
NIL] ~ {
FOR s: ScriptList ← scriptList, scriptList.rest
WHILE s #
NIL
DO
IF Rope.Equal[s.first.scriptName, scriptName] THEN RETURN [s.first]
ENDLOOP;
};
Script Playback
PlayEntry:
PROC [script: Script, seqNum:
INT] ~ {
Should do this in a separate process, so that can accept other user actions (esp stop) during playback.
viewer: ViewerClasses.Viewer;
start: TiogaOps.Location;
[viewer, start] ← FindEntry[script, seqNum]; -- probably should lock things here
VoiceInText.PlaySelection[];
WaitForPlayToFinish[start];
ShowPosition[viewer, start, FALSE];
};
WaitForPlayToFinish:
PROC [charLoc: TiogaOps.Location] ~ {
Should really use progress reports from VoiceRope rather than independent timing.
node: TextNode.Ref ← TiogaButtons.TextNodeRef[charLoc.node]; -- just a type converter
voiceID: Rope.ROPE ← NARROW[TextEdit.GetCharProp[node, charLoc.where, $voice]];
ropeLength:
INT ← VoiceRope.Length[handle: VoiceInText.thrushHandle, vr:
NEW [VoiceRope.VoiceRopeInterval ← [voiceID, 0, 0]]];
may be too long? ignores start & length values specified in voice rope
ropeLengthInMsec: INT ← ropeLength/Jukebox.bytesPerMS;
Process.Pause[Process.MsecToTicks[ropeLengthInMsec]];
Could of course click PlayEntry, then click Play immediately => the timing would be off. For now, don't do that.
};
FindEntry:
PROC [script: Script, seqNum:
INT]
RETURNS [selectedViewer: ViewerClasses.Viewer, start: TiogaOps.Location] ~ {
scriptEntry: ScriptEntry ← FindPositionOfSeqNum[script, seqNum];
[viewer: selectedViewer] ← TiogaOps.GetSelection[];
start ← FindCharProp[selectedViewer, $script, scriptEntry.entryID, CProp];
IF start.node #
NIL
THEN {
ShowPosition[selectedViewer, start, TRUE];
};
};
ShowPosition:
PROC [viewer: ViewerClasses.Viewer, charLoc: TiogaOps.Location, pendingDelete:
BOOL]~ {
charIndex:
INT ← TextNode.LocNumber[at: [TiogaButtons.TextNodeRef[charLoc.node], charLoc.where], skipCommentNodes:
FALSE];
this may need to be adjusted by 1??
TEditSelectionOpsEtc.ShowGivenPositionRange[viewer: viewer, selectionId: primary, posI: charIndex, posF: charIndex, skipCommentNodes: FALSE, pendingDelete: pendingDelete];
};
Script Tool
st: ScriptTool;
InitScriptTool:
PROC ~ {
st ←
NEW[ScriptToolBody ← [
scriptName: "",
playScript: PlayScriptProc,
stop: StopProc,
seqNum: INT ← 0,
playEntry: PlayEntryProc,
findEntry: FindEntryProc,
nextEntry: NextEntryProc,
prevEntry: PrevEntryProc,
createScript: CreateScriptProc,
destroyScript: DestroyScriptProc,
action: Rope.ROPE ← "",
time: INT ← 0,
addEntry: AddEntriesProc,
deleteEntry: DeleteEntriesProc,
listScripts: ListScriptsProc,
listEntries: ListEntriesProc,
extractScript: ExtractScriptProc,
applyScript: ApplyScriptProc,
msg: ""
] ];
[] ← ViewRec.ViewRef[agg: st, viewerInit: [name: "Script Tool"]];
AddEntriesProc:
PROC ~ {
ScriptToolMsg[""];
st.seqNum ← InsertSelectedAnnotationsAfter[LookupScript[st.scriptName], st.seqNum]
};
DeleteEntriesProc:
PROC ~ {
ScriptToolMsg[""];
st.seqNum ← DeleteSelectedAnnotations[LookupScript[st.scriptName], st.seqNum]
};
FindEntryProc:
PROC ~ {
ScriptToolMsg[""];
[] ← FindEntry[LookupScript[st.scriptName], st.seqNum];
};
NextEntryProc:
PROC ~ {
ScriptToolMsg[""];
IF st.seqNum < LookupScript[st.scriptName].numEntries
THEN {
st.seqNum ← st.seqNum + 1;
[] ← FindEntry[LookupScript[st.scriptName], st.seqNum];
}
ELSE
ScriptToolMsg["End of script"];
};
PrevEntryProc:
PROC ~ {
ScriptToolMsg[""];
IF st.seqNum > 1
THEN {
st.seqNum ← st.seqNum - 1;
[] ← FindEntry[LookupScript[st.scriptName], st.seqNum];
}
ELSE
ScriptToolMsg["Beginning of script"];
};
PlayEntryProc:
PROC ~ {
ScriptToolMsg[""];
[] ← PlayEntry[LookupScript[st.scriptName], st.seqNum];
};
PlayScriptProc:
PROC ~ {
script: Script ← LookupScript[st.scriptName];
ScriptToolMsg[""];
FOR n:
INT ← 1, n+1
WHILE n <= script.numEntries
DO
st.seqNum ← n; -- user feedback
PlayEntry[script, n];
ENDLOOP;
};
StopProc:
PROC ~ {
ScriptToolMsg[""];
VoiceRope.Stop[VoiceInText.thrushHandle];
};
CreateScriptProc:
PROC ~ {
IF CreateNewScript[st.scriptName].new =
TRUE
THEN {
ScriptToolMsg["new script created"];
st.seqNum ← 0;
};
ELSE
ScriptToolMsg["duplicate script name -- select another"];
};
DestroyScriptProc:
PROC ~ {
ScriptToolMsg[""];
[] ← CreateNewScript[st.scriptName];
};
ListScriptsProc:
PROC ~ {
st.msg ← "";
FOR s: ScriptList ← scriptList, s.rest
WHILE s #
NIL
DO
st.msg ← Rope.Cat[st.msg, IO.PutFR["%d ", IO.card[s.first.scriptName]]];
ENDLOOP;
};
ScriptToolMsg:
PROC [msg: Rope.
ROPE] ~ {
Call this only once per user function, otherwise user won't be able to read it before it disappears.
st.msg ← msg;
};
Body of NarratedDocsImpl
InitScriptTool[];
TiogaOps.RegisterCommand[name: $RedSave, proc: StoreScriptsRepAtRoot];
TiogaOps.RegisterCommand[name: $YellowSave, proc: StoreScriptsRepAtRoot];
TiogaOps.RegisterCommand[name: $BlueSave, proc: StoreScriptsRepAtRoot];
TiogaOps.RegisterCommand[name: $RedStore, proc: StoreScriptsRepAtRoot];
TiogaOps.RegisterCommand[name: $YellowStore, proc: StoreScriptsRepAtRoot];
TiogaOps.RegisterCommand[name: $BlueStore, proc: StoreScriptsRepAtRoot];
}.
Polle Zellweger (PTZ) August 21, 1986 5:34:34 pm PDT
changes to: DIRECTORY, NarratedDocsImpl
Polle Zellweger (PTZ) August 21, 1986 11:27:32 pm PDT
changes to: AddSelectedAnnotationsToScript, AddEntryToScriptRep, scriptStyleParam, DeleteEntryViaSeqNum, FindPositionOfSeqNum, SuitableViewer (local of InsertSelectedAnnotationsAfter), AddSelectedAnnotations (local of InsertSelectedAnnotationsAfter), DeleteSelectedAnnotations, RemoveSelectedAnnotations (local of DeleteSelectedAnnotations), GetSeqNumsFromSelection, GetEntryIDFromSeqNum
-- curPtr ← FindPositionOfSeqNum[script, seqNum];
IF seqNum < curNum
THEN {
IF seqNum < curNum/2
THEN {
go forward from start
curPtr ← script.firstEntry;
FOR curNum:
INT
IN [2..seqNum]
DO
curPtr ← curPtr.next;
ENDLOOP;
}
ELSE {
go backward from curPtr
FOR curNum:
INT ← curNum-1, curNum-1
WHILE curNum<seqNum
DO
curPtr ← curPtr.prev;
ENDLOOP;
}
}
ELSE {
IF seqNum < (numEntries-curNum)/2
THEN {
go forward from curPtr
FOR curNum:
INT
IN [seqNum+1..numEntries]
DO
curPtr ← curPtr.next;
ENDLOOP;
}
ELSE {
go backward from end
curPtr ← script.lastEntry;
FOR curNum:
INT ← numEntries, curNum-1
WHILE curNum>seqNum
DO
curPtr ← curPtr.prev;
ENDLOOP;
}
};
Polle Zellweger (PTZ) August 22, 1986 7:17:07 pm PDT
changes to: AddEntryViaSeqNum, DeleteEntryViaSeqNum, FindPositionOfSeqNum, ELSE, DIRECTORY, RemoveSelectedAnnotations (local of DeleteSelectedAnnotations), GetEntryIDFromSeqNum, GetCharFromEntryID, AddSelectedAnnotations (local of InsertSelectedAnnotationsAfter)
Polle Zellweger (PTZ) August 23, 1986 6:20:37 pm PDT
changes to: DIRECTORY, NarratedDocsImpl, NconcIntervals, scriptStyleParam, SuitableViewer, InsertSelectedAnnotationsAfter, AddSelectedAnnotations (local of InsertSelectedAnnotationsAfter), DeleteSelectedAnnotations, RemoveSelectedAnnotations (local of DeleteSelectedAnnotations), AddEntryViaSeqNum, DeleteEntryViaSeqNum, FindPositionOfSeqNum, GetEntryIDFromSeqNum, MakeNewEntryID, CreateNewScript, GetCharIndexFromEntryID, GetSeqNumsFromSelection, IF, ELSE, StoreScriptRepAtRoot, ScriptTool, ScriptToolBody, }, st, InitScriptTool, AddEntries, LookupScript, DeleteEntries, FindEntry, PlayEntryProc, Play, Play
Polle Zellweger (PTZ) August 24, 1986 8:37:19 pm PDT
changes to: InitScriptTool, AddEntriesProc, DeleteEntriesProc, FindEntryProc, PlayEntry, PlayEntryProc, PlayProc, StopProc, NewScriptProc, ListScriptsProc, ListScriptsProc, MakeNewEntryID, GetCharIndexFromEntryID, StoreScriptsRepAtRoot, DIRECTORY, NarratedDocsImpl, ScriptList, Script, ScriptBody, ScriptEntry, ScriptEntryBody, EntryID, EntryIDBody, ScriptTool, ScriptToolBody, scriptStyleParam, SuitableViewer, InsertSelectedAnnotationsAfter, AddSelectedAnnotations (local of InsertSelectedAnnotationsAfter), DeleteSelectedAnnotations, RemoveSelectedAnnotations (local of DeleteSelectedAnnotations), AddEntryViaSeqNum, DeleteEntryViaSeqNum, FindPositionOfSeqNum, GetEntryIDFromSeqNum, CreateNewScript, GetSeqNumsFromSelection, LookupScript, InitScriptTool, IF, ELSE, EntryIDList, scriptList, FindEntry, GetSeqNumFromEntryID, LookupScript
Polle Zellweger (PTZ) August 24, 1986 10:30:46 pm PDT
changes to: ScriptBody, ScriptID, ScriptEntry, EntryIDBody, ScriptToolBody, CreateNewScript, LookupScript
Polle Zellweger (PTZ) August 24, 1986 11:05:38 pm PDT
changes to: AddSelectedAnnotations (local of InsertSelectedAnnotationsAfter), RemoveSelectedAnnotations (local of DeleteSelectedAnnotations), FindPositionOfSeqNum
Polle Zellweger (PTZ) August 25, 1986 9:31:28 pm PDT
changes to: DIRECTORY, NarratedDocsImpl, GetCharIndexFromEntryID, CheckPropProc, FindCharProp, PlayEntry, FindEntry
Polle Zellweger (PTZ) August 25, 1986 11:56:46 pm PDT
changes to: ScriptEntryBody, ScriptBody, GetSeqNumFromEntryID, EqualEntryID, MakeNewEntryID, CheckPropProc, CProp, FindCharProp, SearchCharProps (local of FindCharProp), PlayEntry, FindEntry, DIRECTORY, NarratedDocsImpl, FindEntryProc, PlayProc
Polle Zellweger (PTZ) August 26, 1986 0:25:01 am PDT
changes to: ListScriptsProc, AddSelectedAnnotations (local of InsertSelectedAnnotationsAfter), RemoveSelectedAnnotations (local of DeleteSelectedAnnotations)
Polle Zellweger (PTZ) August 26, 1986 6:10:42 pm PDT
changes to: AddEntryViaSeqNum, DeleteEntryViaSeqNum
Polle Zellweger (PTZ) August 27, 1986 10:58:34 am PDT
changes to: ScriptToolBody, RemoveSelectedAnnotations (local of DeleteSelectedAnnotations), InitScriptTool, NextEntryProc, PrevEntryProc, PlayEntryProc
Polle Zellweger (PTZ) August 27, 1986 12:21:02 pm PDT
changes to: PlayEntry, WaitForPlayToFinish, FindEntry, ShowPosition, AddEntriesProc, DeleteEntriesProc, FindEntryProc, NextEntryProc, PrevEntryProc, PlayEntryProc, PlayProc, StopProc, NewScriptProc
Polle Zellweger (PTZ) October 17, 1986 2:51:42 pm PDT
changes to: ScriptBody, ScriptName, ScriptEntryBody, EntryIDBody, EntryIDList, FileID, ScriptToolBody, MakeNewEntryID, CreateNewScript, LookupScript, InitScriptTool, AddEntriesProc, DeleteEntriesProc, FindEntryProc, NextEntryProc, PrevEntryProc, PlayEntryProc, PlayScriptProc, CreateScriptProc, DestroyScriptProc, ListScriptsProc
Polle Zellweger (PTZ) October 24, 1986 3:05:28 pm PDT
changes to: ScriptEntry, ScriptEntryBody, ScriptExt, EntryID, FindCharProp, FindCharProp, StoreScriptsRepAtRoot
Polle Zellweger (PTZ) November 19, 1986 6:06:19 pm PST
changes to: DIRECTORY, ScriptBody, ScriptEntryBody, GetSeqNumFromEntryID, GetScriptEntryFromEntryID, CProp, StoreScriptsRepAtRoot, TraverseCharProps, GetProp, WriteOneScript, WriteOneEntry, LookupScript
Polle Zellweger (PTZ) January 6, 1987 7:20:47 pm PST
changes to: ScriptBody, ScriptID, ScriptName, ScriptDesc, ScriptCreator