SourceMarkerTrackingImpl.mesa
code which ensures that the records of parent viewers & positions within them attached to voice viewers are kept consistent as the parent viewers are edited
Ades, May 1, 1986 1:24:50 pm PDT
n.b. no consideration given in this code to the existence of split viewers
DIRECTORY
IO USING [PutFR, int],
TextEdit USING [Size],
TiogaButtons USING [TextNodeRef],
TiogaOps USING [GetSelection, StepForward],
TiogaOpsDefs USING [Location, Ref],
ViewerClasses USING [Viewer],
ViewerEvents USING [EventRegistration, EventProc, RegisterEventProc, UnRegisterEventProc],
VoiceInText USING [DebugRope],
VoiceViewers USING [VoiceViewerInfo, voiceViewerInfoList],
SourceMarkerTracking;
SourceMarkerTrackingImpl: CEDAR PROGRAM IMPORTS IO, TextEdit, TiogaButtons, TiogaOps, ViewerEvents, VoiceInText, VoiceViewers EXPORTS SourceMarkerTracking = BEGIN
first section deals with what happens when you delete a text viewer which is pointed to by voice viewer(s)
ViewerAndDestroyProc: TYPE = RECORD [
viewer: ViewerClasses.Viewer,
destroyProc: ViewerEvents.EventRegistration
];
registeredViewerList: LIST OF ViewerAndDestroyProc;
this is a list of all the viewers in which source markers have been created and which therefore have a destroyProc set up to sever parent links. When the destroyProc is called the viewer will be deleted from this list; however when all relevant source markers have been deleted from the viewer we do not bother to delete it from the list
RegisterViewer: PUBLIC PROC [viewer: ViewerClasses.Viewer] = {
called everytime a source marker is created within a text viewer
alreadyInList: BOOLEANFALSE;
FOR l: LIST OF ViewerAndDestroyProc ← registeredViewerList, l.rest WHILE l # NIL AND ~alreadyInList DO IF l.first.viewer = viewer THEN alreadyInList ← TRUE ENDLOOP;
IF ~alreadyInList THEN
{ newEntry: ViewerAndDestroyProc ← [viewer, ViewerEvents.RegisterEventProc[
   proc: DestroyViewerEvent, event: destroy, filter: viewer, before: TRUE]];
registeredViewerList ← CONS [newEntry, registeredViewerList]
}
};
DestroyViewerEvent: ViewerEvents.EventProc = {
firstEntry: BOOLEAN ← viewer = registeredViewerList.first.viewer;
previousEntry: LIST OF ViewerAndDestroyProc ← registeredViewerList;
IF ~firstEntry THEN WHILE previousEntry.rest.first.viewer # viewer DO previousEntry ← previousEntry.rest ENDLOOP; -- if this runs off the end then something has gone seriously wrong
ViewerEvents.UnRegisterEventProc[(IF firstEntry THEN registeredViewerList ELSE previousEntry.rest).first.destroyProc, destroy];
IF firstEntry THEN registeredViewerList ← registeredViewerList.rest ELSE previousEntry.rest ← previousEntry.rest.rest;
RemoveParentPointersTo[viewer];
};
RemoveParentPointersTo: PUBLIC PROC [viewer: ViewerClasses.Viewer] = {
FOR info: VoiceViewers.VoiceViewerInfo ← VoiceViewers.voiceViewerInfoList, info.nextInfoRec WHILE info # NIL DO
IF info.parentViewer = viewer THEN
{ info.parentViewer ← NIL;
VoiceInText.DebugRope[IO.PutFR["detaching parent link for voice viewer %d\n", IO.int[info.viewerNumber]]]
}
ENDLOOP
};
remainder of the code deals with tracking pointers correctly as the text they point to is edited
TrackDeletes: PUBLIC PROC = {
called before the primary selection is deleted, if in a text viewer
N.B. that the selection may span more than one node
viewer: ViewerClasses.Viewer;
start, end, current: TiogaOpsDefs.Location;
infoList: LIST OF VoiceViewers.VoiceViewerInfo ← NIL;
SeverLinks: PROC [node: TiogaOpsDefs.Ref, from, to: INT] = {
FOR l: LIST OF VoiceViewers.VoiceViewerInfo ← infoList, l.rest WHILE l # NIL DO
IF l.first.positionInParent.node = node AND l.first.positionInParent.where IN [from..to]
THEN
{ l.first.parentViewer ← NIL;
VoiceInText.DebugRope[IO.PutFR["detaching parent link for voice viewer %d\n", IO.int[l.first.viewerNumber]]]
}
ENDLOOP
};
[viewer: viewer, start: start, end: end] ← TiogaOps.GetSelection[];
FOR info: VoiceViewers.VoiceViewerInfo ← VoiceViewers.voiceViewerInfoList, info.nextInfoRec WHILE info # NIL DO IF info.parentViewer = viewer THEN infoList ← CONS [info, infoList] ENDLOOP;
IF infoList = NIL THEN RETURN;
we now have a list of all the voice viewers whose parent links point at the viewer containing the primary selection: we need to go through all the nodes in the selection severing the links which point within the selection and altering those which lie in the last node of the selection, beyond the end of the selection
IF start.node = end.node
THEN SeverLinks[start.node, start.where, end.where]
ELSE
{ current ← start;
SeverLinks[current.node, current.where, TextEdit.Size[TiogaButtons.TextNodeRef[current.node]]-1];
DO
current.node ← TiogaOps.StepForward[current.node];
IF current.node = end.node THEN
{ SeverLinks[current.node, 0, end.where];
EXIT
}
ELSE SeverLinks[current.node, 0, TextEdit.Size[TiogaButtons.TextNodeRef[current.node]]-1]
ENDLOOP
};
{ positionAdjustment: INT ← end.where - start.where + 1;
FOR l: LIST OF VoiceViewers.VoiceViewerInfo ← infoList, l.rest WHILE l # NIL DO
IF l.first.positionInParent.node = end.node AND l.first.positionInParent.where > end.where
THEN l.first.positionInParent ← [start.node, l.first.positionInParent.where - positionAdjustment]
ENDLOOP
}
};
END.