-- TreeEditImpl.mesa
-- written by Bill Paxton, May 1981
-- last edit by Bill Paxton, 29-Jun-81 8:46:02
DIRECTORY
TreeEdit,
EditNotify,
NodeProps,
TextEdit,
OtherNode,
TextNode;
TreeEditImpl: PROGRAM
IMPORTS nodeI:TextNode, editI:TextEdit, TreeEdit, EditNotify,
propsI:NodeProps, otherI:OtherNode
EXPORTS TreeEdit =
BEGIN
OPEN TreeEdit, EditNotify;
-- **** Editing Operations for tree structure ****
ReplaceNodes: PUBLIC PROC [destFirst, destLast, sourceFirst, sourceLast: Ref] = {
notify: REF ReplacingNodes Change;
first,last,destParent,destPrev,destAfter: Ref;
[first,last] ← CopySpan[sourceFirst,sourceLast];
notify ← NEW[ReplacingNodes Change ←
[ReplacingNodes[destFirst, destLast, sourceFirst, sourceLast]]];
[destParent,destPrev,destAfter] ← Relatives[destFirst,destLast];
Notify[destParent,NIL,notify,before];
IF first=NIL THEN Del[destParent,destPrev,destAfter]
ELSE Rep[destParent,destPrev,destAfter,first,last];
Notify[destParent,NIL,notify,after] };
CopyNodes: PUBLIC PROC [dest, sourceFirst, sourceLast: Ref, child: BOOLEAN ← TRUE] = {
first, last, parent: Ref;
notify: REF CopyingNodes Change;
IF dest=NIL THEN RETURN;
notify ← NEW [CopyingNodes Change ←
[CopyingNodes[dest, sourceFirst, sourceLast, child]]];
parent ← IF child THEN dest ELSE nodeI.Parent[dest];
Notify[parent,NIL,notify,before];
[first,last] ← CopySpan[sourceFirst,sourceLast];
InsertSpan[dest,first,last,child];
Notify[parent,NIL,notify,after] };
BadMove: PUBLIC ERROR = CODE;
MoveNodesOnto: PUBLIC PROC [destFirst, destLast, moveFirst, moveLast: Ref] = {
-- dest cannot be within source
notify: REF MovingNodesOnto Change;
moveParent, movePrev, moveAfter: Ref;
destParent, destPrev, destAfter: Ref;
IF moveFirst=NIL THEN { DeleteNodes[destFirst,destLast]; RETURN };
[moveParent,movePrev,moveAfter] ← Relatives[moveFirst,moveLast];
[destParent,destPrev,destAfter] ← Relatives[destFirst,destLast];
IF moveParent=destParent THEN { -- check overlapping or adjacent
IF InsideSiblings[moveFirst,destFirst,destLast] THEN {
IF InsideSiblings[moveLast,destFirst,destLast] AND
moveLast # destLast THEN DeleteNodes[moveAfter,destLast];
IF moveFirst # destFirst THEN DeleteNodes[destFirst,movePrev];
RETURN };
IF InsideSiblings[moveLast,destFirst,destLast] THEN {
IF moveLast # destLast THEN DeleteNodes[moveAfter,destLast];
RETURN };
IF InsideSiblings[destFirst,moveFirst,moveLast] THEN RETURN;
IF moveAfter=destFirst OR destAfter=moveFirst THEN {
DeleteNodes[destFirst,destLast]; RETURN }};
IF InsideSiblings[destFirst,moveFirst,moveLast] THEN ERROR BadMove; -- dest inside source
notify ← NEW [MovingNodesOnto Change ←
[MovingNodesOnto[destFirst, destLast, moveFirst, moveLast]]];
Notify[destParent,moveParent,notify,before];
Del[moveParent,movePrev,moveAfter];
Rep[destParent,destPrev,destAfter,moveFirst,moveLast];
Notify[destParent,moveParent,notify,after] };
MoveNodes: PUBLIC PROC [dest, moveFirst, moveLast: Ref, child: BOOLEAN ← TRUE] = {
-- dest cannot be within source
notify: REF MovingNodes Change;
newParent, moveParent, movePrev, moveAfter: Ref;
IF InsideSpan[dest,moveFirst,moveLast] THEN ERROR BadMove;
[moveParent,movePrev,moveAfter] ← Relatives[moveFirst,moveLast];
notify ← NEW [MovingNodes Change ←
[MovingNodes[dest, moveFirst, moveLast, child]]];
newParent ← IF child THEN dest ELSE nodeI.Parent[dest];
Notify[newParent,moveParent,notify,before];
Del[moveParent,movePrev,moveAfter];
InsertSpan[dest,moveFirst,moveLast,child];
Notify[newParent,moveParent,notify,after] };
BadTranspose: PUBLIC ERROR = CODE;
TransposeNodes: PUBLIC PROC [alphaFirst, alphaLast, betaFirst, betaLast: Ref] = {
-- alpha and beta must not overlap
alphaParent, alphaPrev, alphaAfter, betaParent, betaPrev, betaAfter: Ref;
notify: REF TransposingNodes Change;
IF OverlappingSpans[alphaFirst,alphaLast,betaFirst,betaLast] THEN ERROR BadTranspose;
[alphaParent,alphaPrev,alphaAfter] ← Relatives[alphaFirst,alphaLast];
[betaParent,betaPrev,betaAfter] ← Relatives[betaFirst,betaLast];
notify ← NEW [TransposingNodes Change ←
[TransposingNodes[alphaFirst, alphaLast, betaFirst, betaLast]]];
Notify[alphaParent,betaParent,notify,before];
IF alphaAfter=betaFirst THEN { -- move alpha after beta
Del[alphaParent,alphaPrev,alphaAfter];
InsertSpan[betaLast,alphaFirst,alphaLast,FALSE] }
ELSE IF betaAfter=alphaFirst THEN { -- move beta after alpha
Del[betaParent,betaPrev,betaAfter];
InsertSpan[alphaLast,betaFirst,betaLast,FALSE] }
ELSE { -- move both
Del[alphaParent,alphaPrev,alphaAfter];
Del[betaParent,betaPrev,betaAfter];
IF alphaPrev # NIL THEN InsertSpan[alphaPrev,betaFirst,betaLast,FALSE]
ELSE InsertSpan[alphaParent,betaFirst,betaLast,TRUE];
IF betaPrev # NIL THEN InsertSpan[betaPrev,alphaFirst,alphaLast,FALSE]
ELSE InsertSpan[betaParent,alphaFirst,alphaLast,TRUE] };
Notify[alphaParent,betaParent,notify,after] };
InsertTextNode: PUBLIC PROC [dest: Ref, inherit, child: BOOLEAN ← TRUE]
RETURNS [new: RefTextNode] = {
newParent: Ref;
notify: REF InsertingTextNode Change;
new ← CreateTextNode[];
IF inherit THEN Inherit[dest,new];
notify ← NEW [InsertingTextNode Change ←
[InsertingTextNode[dest, inherit, child, new]]];
newParent ← IF child THEN dest ELSE nodeI.Parent[dest];
Notify[newParent,NIL,notify,before];
InsertSpan[dest,new,new,child];
Notify[newParent,NIL,notify,after] };
InsertOtherNode: PUBLIC PROC [dest: Ref, inherit, child: BOOLEAN ← TRUE]
RETURNS [new: RefOtherNode] = {
newParent: Ref;
notify: REF InsertingOtherNode Change;
new ← CreateOtherNode[];
IF inherit THEN Inherit[dest,new];
notify ← NEW [InsertingOtherNode Change ←
[InsertingOtherNode[dest, inherit, child, new]]];
newParent ← IF child THEN dest ELSE nodeI.Parent[dest];
Notify[newParent,NIL,notify,before];
InsertSpan[dest,new,new,child];
Notify[newParent,NIL,notify,after] };
SplitNode: PUBLIC PROC [node: RefTextNode, textLoc: Offset, inherit, child: BOOLEAN ← TRUE] = {
new: RefTextNode ← InsertTextNode[node,inherit,child];
[] ← editI.MoveText[new,0,node,textLoc] };
MergeNodes: PUBLIC PROC [node: RefTextNode] = {
-- merge node and its next sibling
-- sibling must also be a text node
-- node must be terminal
-- copies text of node to start of sibling, then deletes node
sibling: Ref;
merge: RefTextNode;
IF node.child # NIL THEN RETURN;
IF (sibling ← nodeI.Next[node])=NIL THEN RETURN;
IF (merge ← nodeI.NarrowToTextNode[sibling])=NIL THEN RETURN;
[] ← editI.CopyText[merge,0,node];
DeleteNode[node] };
-- **** Support routines for editing operations
Inherit: PROC [old, new: Ref] = INLINE {
CopyProp: PROC [name: ATOM, value: REF] RETURNS [BOOLEAN] = {
newvalue: REF;
IF (newvalue ← propsI.CopyInfo[name,value]) # NIL THEN
propsI.PutProp[new,name,newvalue];
RETURN [FALSE] };
new.typename ← old.typename;
new.hasstyle ← old.hasstyle;
IF old.props # NIL THEN [] ← propsI.MapProps[old,CopyProp] };
InsertSpan: PROC [dest, first, last: Ref, child: BOOLEAN] = {
IF first=NIL THEN RETURN;
IF child THEN { -- insert as first children of dest
IF dest.child # NIL THEN { last.next ← dest.child; last.last ← FALSE }
ELSE { last.next ← dest; last.last ← TRUE };
dest.child ← first }
ELSE { -- insert as next sibling of dest
last.next ← dest.next; last.last ← dest.last;
dest.last ← FALSE; dest.next ← first }};
CopySpan: PUBLIC PROC [sourceFirst, sourceLast: Ref] RETURNS [first, last: Ref] = {
node, topnode, child, new, prev, parent: Ref;
IF (topnode ← node ← sourceFirst)=NIL THEN RETURN;
DO -- create new node each time through the loop
WITH n:node SELECT FROM
text => {
txt: RefTextNode;
new ← txt ← nodeI.qZone.NEW[nodeI.TextBody];
txt.rope ← n.rope; txt.runs ← n.runs };
other => {
othr: RefOtherNode;
new ← othr ← nodeI.qZone.NEW[nodeI.OtherBody];
othr.variety ← n.variety;
othr.info ← otherI.CopyInfo[@n] };
ENDCASE => ERROR;
Inherit[node,new];
-- insert new
IF prev#NIL THEN prev.next ← new
ELSE IF parent#NIL THEN parent.child ← new;
-- go to next one
IF (child ← node.child) # NIL THEN { -- descend in the tree
node ← child; new.next ← parent; parent ← new; prev ← NIL }
ELSE DO -- move to next node, sibling or up* then sibling
IF node=topnode THEN {
IF first=NIL THEN first ← new;
IF topnode=sourceLast THEN { last ← new; RETURN };
topnode ← node ← topnode.next; prev ← new; EXIT };
IF ~node.last THEN { node ← node.next; prev ← new; EXIT };
new.last ← TRUE; prev ← parent; parent ← parent.next;
node ← node.next;
ENDLOOP;
ENDLOOP };
Relatives: PUBLIC PROC [destFirst, destLast:Ref] RETURNS [destParent, destPrev, destAfter:Ref] = {
OPEN nodeI;
nx: Ref;
destAfter ← Next[destLast];
destParent ← Parent[destAfter];
IF (destPrev ← FirstChild[destParent])=destFirst THEN destPrev ← NIL
ELSE DO IF (nx←destPrev.next)=destFirst THEN RETURN; destPrev ← nx; ENDLOOP };
InsideSpan: PUBLIC PROC [node,first,last: Ref] RETURNS [inside: BOOLEAN] = {
-- returns true if node is inside the span of branches [first..last]
spanParent: Ref ← nodeI.Parent[first];
nodeParent: Ref;
DO IF node=NIL THEN RETURN [FALSE];
IF (nodeParent ← nodeI.Parent[node]) = spanParent THEN
RETURN [InsideSiblings[node,first,last]];
node ← nodeParent;
ENDLOOP };
InsideSiblings: PUBLIC PROC [node,first,last: Ref] RETURNS [inside: BOOLEAN] = {
FOR n:Ref ← first, n.next DO
SELECT n FROM
node => RETURN [TRUE];
last => EXIT;
ENDCASE;
ENDLOOP;
RETURN [FALSE] };
OverlappingSpans: PUBLIC PROC [alphaFirst, alphaLast, betaFirst, betaLast: Ref]
RETURNS [overlap: BOOLEAN] = {
-- returns true if branches [alphaFirst..alphaLast] overlap [betaFirst..betaLast]
RETURN [
IF nodeI.Parent[alphaFirst] = nodeI.Parent[betaFirst] THEN
InsideSiblings[alphaFirst,betaFirst,betaLast] OR
InsideSiblings[betaFirst,alphaFirst,alphaLast]
ELSE InsideSpan[alphaFirst,betaFirst,betaLast] OR
InsideSpan[betaFirst,alphaFirst,alphaLast]] };
Rep: PROC [destParent,destPrev,destAfter,first,last:Ref] = {
IF destPrev # NIL THEN { destPrev.last ← FALSE; destPrev.next ← first }
ELSE destParent.child ← first;
IF destAfter # NIL THEN { last.last ← FALSE; last.next ← destAfter }
ELSE { last.last ← TRUE; last.next ← destParent }};
Del: PROC [destParent,destPrev,destAfter:Ref] = {
IF destPrev # NIL THEN
IF destAfter # NIL THEN destPrev.next ← destAfter
ELSE { destPrev.last ← TRUE; destPrev.next ← destParent }
ELSE destParent.child ← destAfter };
-- **** Operations for creating nodes ****
CreateTextNode: PUBLIC PROC RETURNS [RefTextNode] = { RETURN [nodeI.qZone.NEW[nodeI.TextBody]] };
CreateOtherNode: PUBLIC PROC RETURNS [RefOtherNode] = { RETURN [nodeI.qZone.NEW[nodeI.OtherBody]] };
-- ***** Initialization
Start: PUBLIC PROC = { -- for initialization only
};
END.