DIRECTORY EditSpan, EditSpanSupport, EditNotify, Rope, UndoEvent, TextEdit, TreeSlice, TiogaLooks, TiogaNode, TiogaNodeOps, TiogaTreeOps; EditSpanImpl: CEDAR PROGRAM IMPORTS TextEdit, TiogaNodeOps, TiogaTreeOps, UndoEvent, EditSpan, EditSpanSupport, TreeSlice, EditNotify EXPORTS EditSpan SHARES TiogaNode = BEGIN OPEN EditSpan, EditSpanSupport, TreeSlice, EditNotify; CannotDoEdit: PUBLIC ERROR = CODE; afterMoved1, afterMoved2: PUBLIC TreeLoc; Slice: TYPE = TreeSlice.Slice; nullSpan: TreeSpan = TiogaTreeOps.nullSpan; nullBranchSpan: BranchSpan = TiogaTreeOps.nullBranchSpan; Move: PUBLIC PROC [destRoot, sourceRoot: RefBranchNode, dest: TreeLoc, source: TreeSpan, where: Place _ after, nesting: INTEGER _ 0, event: Event _ NIL] RETURNS [result: TreeSpan] = { sBefore, sAfter, sTop, sBottom, dBefore, dAfter: Slice; sNesting, dNesting: INTEGER; sDepth: NAT; beforeSource, afterSource: RefBranchNode; -- nodes adjacent to source after do splits afterLoc: TreeLoc; d: BranchLoc; s: BranchSpan; FreeSlices: PROC = { FreeSlice[sBefore]; FreeSlice[sAfter]; FreeSlice[sTop]; FreeSlice[sBottom]; FreeSlice[dBefore]; FreeSlice[dAfter]; sBefore _ sAfter _ sTop _ sBottom _ dBefore _ dAfter _ NIL }; ForcedUnNest: PROC [after: Ref] = { OPEN TiogaTreeOps; afterNode: RefBranchNode = TiogaNodeOps.NarrowToBranchNode[after]; span: TreeSpan = MakeTreeSpan[afterNode, LastBranchWithin[afterNode]]; IF afterNode=NIL THEN ERROR; FreeSlices[]; [] _ UnNest[Root[afterNode], span, event] }; ForcedNest: PROC = { OPEN TiogaTreeOps; bottom: RefBranchNode = TiogaNodeOps.NarrowToBranchNode[sBottom[sDepth]]; span: TreeSpan _ MakeTreeSpan[sTop[sDepth], LastBranchWithin[bottom]]; IF bottom=NIL THEN ERROR; IF TreeSlice.SliceLength[sTop] = sDepth+1 THEN { -- can't do it by calling Nest IF nesting >= 1 THEN ERROR; nesting _ nesting+1; -- move to a deeper position FreeSlices[]; RETURN }; FreeSlices[]; [] _ Nest[Root[span.start.node], span, event] }; IF CheckForNil[source] THEN RETURN [nullSpan]; IF TextLocationAndSpan[dest, source] THEN -- pure text move RETURN [DestSpanText[destRoot, sourceRoot, dest, source, TextEdit.MoveText, event]]; IF where=child THEN { where _ after; nesting _ nesting+1 } ELSE IF where=sibling THEN { parentBr, childBr: RefBranchNode; parentBr _ TiogaTreeOps.ContainingStatement[dest.node]; childBr _ TiogaTreeOps.LastBranchWithin[parentBr]; dest _ [childBr, NodeItself]; where _ after; nesting _ nesting + TiogaTreeOps.Level[parentBr] - TiogaTreeOps.Level[childBr] }; [d, s] _ DoSplitsForMove[dest, source, where, event]; beforeSource _ TiogaTreeOps.Backward[s.start.node].back; afterSource _ TiogaTreeOps.Forward[s.end.node].nx; afterLoc _ [afterSource, NodeItself]; { -- for exits IF d # TiogaTreeOps.nullBranchLoc AND ((where = after AND (d.node = beforeSource OR d.node = s.end.node)) OR (where = before AND (d.node = afterSource OR d.node = s.start.node))) THEN { -- not going anywhere, but might be changing nesting IF nesting > 0 THEN FOR i:INTEGER IN [0..nesting) DO [] _ Nest[sourceRoot, NodeSpan[s], event]; ENDLOOP ELSE IF nesting < 0 THEN FOR i:INTEGER IN [nesting..0) DO [] _ UnNest[sourceRoot, NodeSpan[s], event]; ENDLOOP } ELSE DO -- repeat this loop only if have forced nest or unnest or s and d in same tree [sBefore, sAfter, sTop, sBottom, sNesting, sDepth] _ DescribeBand[s.start.node, s.end.node ! BadBand => { s _ nullBranchSpan; GOTO ErrorReturn } ]; IF d = TiogaTreeOps.nullBranchLoc THEN { -- moving to limbo d _ CreateDest[TreeSlice.SliceLength[sTop]-sDepth]; destRoot _ TiogaTreeOps.Root[d.node]; destRoot.deleted _ TRUE; -- so will free this when it falls off the edit history list where _ after; nesting _ 1 }; [dBefore, dAfter, dNesting] _ DestSlices[d.node, where]; IF TreeSlice.CompareSliceOrder[dBefore, sBefore]=after AND TreeSlice.CompareSliceOrder[dBefore, sBottom]=before THEN GOTO ErrorReturn; -- d inside s IF ~TiogaNodeOps.IsBranch[dBefore[0]] AND BadDest[dBefore[0], sBefore, sBottom] THEN GOTO ErrorReturn; IF dBefore[0] = sBefore[0] THEN { -- s and d in same tree; too hard, so move s out first. span: TreeSpan = NodeSpan[s]; FreeSlices[]; [] _ MoveToLimbo[sourceRoot, span, event]; LOOP }; dNesting _ dNesting + nesting; SELECT NeedNestingChange[dBefore, dAfter, sTop, sBottom, dNesting, sDepth] FROM needUnNest => { ForcedUnNest[TreeSlice.LastOfSlice[dAfter]]; LOOP }; needNest => { ForcedNest[]; LOOP }; ENDCASE; IF TreeSlice.SliceLength[sAfter] > TreeSlice.SliceLength[sBefore]+1 THEN { ForcedUnNest[TreeSlice.LastOfSlice[sAfter]]; LOOP } ELSE { -- do it notify: REF MovingNodes Change; notify _ NEW[MovingNodes Change _ [MovingNodes[ destRoot, sourceRoot, d.node, s.start.node, s.end.node, TreeSlice.LastOfSlice[sBefore], sNesting, (where # before)]]]; Notify[notify, before]; DeletePrefix[sTop, sDepth]; DeletePrefix[sBottom, sDepth]; ReplaceBand[dBefore, dAfter, sTop, sBottom, dNesting, event]; Splice[sBefore, sAfter]; FreeSlices[]; Notify[notify, after]; UndoEvent.Note[event, UndoMoveNodes, notify] }; EXIT; ENDLOOP; IF d.where # NodeItself THEN { -- undo prior splits start, end: BOOLEAN _ FALSE; IF s.start.where # NodeItself THEN { -- merge start of s with front of dest start _ TRUE; [s, ] _ ReMerge[s, nullBranchSpan, s.start.node, event] }; IF s.end.where # NodeItself THEN { -- merge end of s with tail of dest end _ TRUE; [s, ] _ ReMerge[s, nullBranchSpan, s.end.node, event, TRUE] }; IF start AND end THEN { -- merge before s with after s afterLoc _ JoinStatements[TiogaTreeOps.Root[afterSource], afterSource, event] }} ELSE IF s.start.where # NodeItself AND s.end.where # NodeItself THEN { afterLoc _ JoinStatements[TiogaTreeOps.Root[afterSource], afterSource, event] }; afterMoved2 _ afterMoved1; -- save previous hint afterMoved1 _ IF afterSource=NIL THEN [beforeSource, 0] ELSE afterLoc; -- hint for repaint source.start.node _ s.start.node; source.end.node _ s.end.node; source.start.where _ source.end.where _ NodeItself; RETURN [source]; EXITS ErrorReturn => { FreeSlices[]; UndoSplitsForMove[d, s, where, event]; ERROR CannotDoEdit }}}; UndoMoveNodes: PROC [undoRef: REF, currentEvent: Event] = TRUSTED { saved: REF Change _ NARROW[undoRef]; WITH x:saved SELECT FROM MovingNodes => { [] _ Move[x.sourceRoot, x.destRoot, TiogaTreeOps.MakeTreeLoc[x.pred], TiogaTreeOps.MakeTreeSpan[x.first, x.last], after, x.nesting, currentEvent] }; ENDCASE => ERROR }; BadDest: PROC [top: Ref, sBefore, sBottom: Slice] RETURNS [error: BOOL] = { DO -- check for dest inside source s: RefBranchNode _ TiogaTreeOps.ContainingStatement[top]; before, after: Slice; [before, after] _ MakeSlices[s]; top _ before[0]; error _ TreeSlice.CompareSliceOrder[before, sBefore]=after AND TreeSlice.CompareSliceOrder[before, sBottom]=before; FreeSlice[before]; FreeSlice[after]; IF error THEN RETURN; IF TiogaNodeOps.IsBranch[top] THEN RETURN; ENDLOOP }; Transpose: PUBLIC PROC [alphaRoot, betaRoot: RefBranchNode, alpha, beta: TreeSpan, event: Event _ NIL] RETURNS [newAlpha, newBeta: TreeSpan] = { aBefore, aAfter, aTop, aBottom, bBefore, bAfter, bTop, bBottom: Slice; aNesting, bNesting: INTEGER; aDepth, bDepth: NAT; beforeAlpha, afterAlpha, beforeBeta, afterBeta: RefBranchNode; -- nodes adjacent after do splits afterAlphaLoc, afterBetaLoc: TreeLoc; a, b: BranchSpan; FreeSlices: PROC = { FreeSlice[aBefore]; FreeSlice[aAfter]; FreeSlice[aTop]; FreeSlice[aBottom]; FreeSlice[bBefore]; FreeSlice[bAfter]; FreeSlice[bTop]; FreeSlice[bBottom]; aBefore _ aAfter _ aTop _ aBottom _ bBefore _ bAfter _ bTop _ bBottom _ NIL }; { -- for exit IF CheckForNil[alpha] OR CheckForNil[beta] THEN RETURN [nullSpan, nullSpan]; IF TextSpans[alpha, beta] THEN { -- pure text transpose alphaNode: RefTextNode _ TiogaNodeOps.NarrowToTextNode[alpha.start.node]; betaNode: RefTextNode _ TiogaNodeOps.NarrowToTextNode[beta.start.node]; alphaStart: Offset _ alpha.start.where; alphaLen: Offset _ alpha.end.where - alphaStart + 1; betaStart: Offset _ beta.start.where; betaLen: Offset _ beta.end.where - betaStart + 1; alphaResultStart, alphaResultLen, betaResultStart, betaResultLen: Offset; [alphaResultStart, alphaResultLen, betaResultStart, betaResultLen] _ TextEdit.TransposeText[ alphaRoot, betaRoot, alphaNode, alphaStart, alphaLen, betaNode, betaStart, betaLen, event]; newAlpha _ [[betaNode, alphaResultStart], [betaNode, alphaResultStart+alphaResultLen-1]]; newBeta _ [[alphaNode, betaResultStart], [alphaNode, betaResultStart+betaResultLen-1]]; RETURN }; [a, b] _ DoSplits[alpha, beta, event]; -- so can deal with entire nodes beforeAlpha _ TiogaTreeOps.Backward[a.start.node].back; afterAlpha _ TiogaTreeOps.Forward[a.end.node].nx; afterAlphaLoc _ [afterAlpha, 0]; beforeBeta _ TiogaTreeOps.Backward[b.start.node].back; afterBeta _ TiogaTreeOps.Forward[b.end.node].nx; afterBetaLoc _ [afterBeta, 0]; IF afterAlpha = b.start.node THEN { -- a just before b [] _ Move[alphaRoot, betaRoot, NodeLoc[a.start], NodeSpan[b], before, 0, event] } ELSE IF afterBeta = a.start.node THEN { -- b just before a [] _ Move[betaRoot, alphaRoot, NodeLoc[b.start], NodeSpan[a], before, 0, event] } ELSE { -- get slices describing the bands of nodes to be transposed overlap: BOOLEAN; head, tail: BranchSpan; -- sections of a or b before and after the overlap startOrder, endOrder: Order; [aBefore, aAfter, aTop, aBottom, aNesting, aDepth] _ DescribeBand[a.start.node, a.end.node ! BadBand => { a _ b _ nullBranchSpan; GOTO ErrorReturn }]; [bBefore, bAfter, bTop, bBottom, bNesting, bDepth] _ DescribeBand[b.start.node, b.end.node ! BadBand => { a _ b _ nullBranchSpan; GOTO ErrorReturn }]; IF ~TiogaNodeOps.IsBranch[aBefore[0]] AND BadDest[aBefore[0], bBefore, bBottom] THEN GOTO ErrorReturn; -- a inside statement inside b IF ~TiogaNodeOps.IsBranch[bBefore[0]] AND BadDest[bBefore[0], aBefore, aBottom] THEN GOTO ErrorReturn; -- b inside statement inside a [overlap, head, tail, startOrder, endOrder] _ SliceOrder[a, b, aBefore, aBottom, bBefore, bBottom]; IF overlap THEN { -- bands overlap FreeSlices[]; IF head = nullBranchSpan AND tail = nullBranchSpan THEN NULL ELSE IF head = nullBranchSpan THEN { --move tail to before alphastart [] _ Move[alphaRoot, betaRoot, NodeLoc[a.start], NodeSpan[tail], before, 0, event]; IF endOrder=before THEN { -- a end before b end b.start _ tail.start; b.end _ a.end } ELSE { -- b end before a end a.start _ tail.start; a.end _ b.end }} ELSE IF tail = nullBranchSpan THEN { --move head to after alphaend [] _ Move[alphaRoot, betaRoot, NodeLoc[a.end], NodeSpan[head], after, 0, event]; IF startOrder=before THEN { -- a start before b start a.start _ b.start; a.end _ head.end } ELSE { -- b start before a start b.start _ a.start; b.end _ head.end }} ELSE IF startOrder # endOrder THEN NULL -- one contained in the other ELSE { --transpose head and tail [] _ Transpose[alphaRoot, betaRoot, NodeSpan[head], NodeSpan[tail], event]; IF startOrder=before THEN { -- a start before b start a.start _ b.start; a.end _ head.end; b.start _ tail.start; b.end _ a.end } ELSE { -- b start before a start b.start _ a.start; b.end _ head.end; a.start _ tail.start; a.end _ b.end }}} ELSE { -- do transpose as two moves aSpan, bSpan: TreeSpan; after1, after2: TreeLoc; bLoc: TreeLoc _ TiogaTreeOps.MakeTreeLoc[TreeSlice.LastOfSlice[bBefore]]; aLoc: TreeLoc _ TiogaTreeOps.MakeTreeLoc[TreeSlice.LastOfSlice[aBefore]]; FreeSlices[]; aSpan _ TiogaTreeOps.MakeTreeSpan[a.start.node, a.end.node]; bSpan _ TiogaTreeOps.MakeTreeSpan[b.start.node, b.end.node]; [] _ MoveToLimbo[alphaRoot, aSpan, event]; after1 _ afterMoved1; -- repaint hints [] _ MoveToLimbo[betaRoot, bSpan, event]; after2 _ afterMoved1; [] _ Move[betaRoot, TiogaTreeOps.Root[aSpan.start.node], bLoc, aSpan, after, bNesting, event]; [] _ Move[alphaRoot, TiogaTreeOps.Root[bSpan.start.node], aLoc, bSpan, after, aNesting, event]; afterMoved1 _ after1; afterMoved2 _ after2 }}; IF a.start.where # NodeItself AND b.start.where # NodeItself THEN { -- remerge starts [a, b] _ ReMerge[a, b, a.start.node, event]; [a, b] _ ReMerge[a, b, b.start.node, event] }; IF a.end.where # NodeItself AND b.end.where # NodeItself THEN { -- remerge ends [a, b] _ ReMerge[a, b, a.end.node, event, TRUE]; [a, b] _ ReMerge[a, b, b.end.node, event, TRUE]; afterAlphaLoc.node _ b.end.node; afterBetaLoc.node _ a.end.node }; afterMoved1 _ IF afterAlphaLoc.node=NIL THEN [beforeAlpha, 0] ELSE afterAlphaLoc; -- hint for repaint afterMoved2 _ IF afterBetaLoc.node=NIL THEN [beforeBeta, 0] ELSE afterBetaLoc; alpha.start _ [a.start.node, NodeItself]; alpha.end _ [a.end.node, NodeItself]; beta.start _ [b.start.node, NodeItself]; beta.end _ [b.end.node, NodeItself]; RETURN [alpha, beta]; EXITS ErrorReturn => { FreeSlices[]; UndoSplits[a, b, event]; ERROR CannotDoEdit }}}; MoveOnto: PUBLIC PROC [destRoot, sourceRoot: RefBranchNode, dest, source: TreeSpan, saveForPaste: BOOLEAN _ TRUE, event: Event _ NIL] RETURNS [result: TreeSpan] = { overlap: BOOLEAN; newDest: TreeSpan; head, tail: BranchSpan; -- sections of alpha or beta before and after the overlap startOrder, endOrder: Order; aBefore, aAfter, aTop, aBottom, bBefore, bAfter, bTop, bBottom: Slice; aNesting, bNesting: INTEGER; aDepth, bDepth: NAT; d, s: BranchSpan; FreeSlices: PROC = { FreeSlice[aBefore]; FreeSlice[aAfter]; FreeSlice[aTop]; FreeSlice[aBottom]; FreeSlice[bBefore]; FreeSlice[bAfter]; FreeSlice[bTop]; FreeSlice[bBottom]; aBefore _ aAfter _ aTop _ aBottom _ bBefore _ bAfter _ bTop _ bBottom _ NIL }; { -- for exit IF CheckForNil[source] OR CheckForNil[dest] THEN RETURN [nullSpan]; IF TextSpans[dest, source] THEN { -- pure text move RETURN [TwoSpanText[destRoot, sourceRoot, dest, source, saveForPaste, TextEdit.MoveTextOnto, event]] }; [d, s] _ DoSplits[dest, source, event]; [aBefore, aAfter, aTop, aBottom, aNesting, aDepth] _ DescribeBand[d.start.node, d.end.node ! BadBand => { d _ s _ nullBranchSpan; GOTO ErrorReturn }]; [bBefore, bAfter, bTop, bBottom, bNesting, bDepth] _ DescribeBand[s.start.node, s.end.node ! BadBand => { d _ s _ nullBranchSpan; GOTO ErrorReturn }]; IF ~TiogaNodeOps.IsBranch[aBefore[0]] AND BadDest[aBefore[0], bBefore, bBottom] THEN GOTO ErrorReturn; -- d inside statement inside s [overlap, head, tail, startOrder, endOrder] _ SliceOrder[d, s, aBefore, aBottom, bBefore, bBottom]; FreeSlices[]; IF overlap THEN { -- bands overlap. modify d so doesn't overlap IF head = nullBranchSpan AND tail = nullBranchSpan THEN GOTO ErrorReturn; IF head = nullBranchSpan THEN { -- s start = d start IF endOrder=before THEN GOTO ErrorReturn -- d end before s end ELSE d.start _ tail.start } ELSE IF tail = nullBranchSpan THEN { --s end = d end IF startOrder=before THEN d.end _ head.end -- d start before s start ELSE GOTO ErrorReturn } -- s start before d start ELSE { -- have both head and tail IF startOrder=before AND endOrder=after THEN { [] _ Delete[destRoot, NodeSpan[tail], event]; d.end _ head.end } ELSE IF startOrder=before THEN d.end _ head.end ELSE IF endOrder=after THEN d.start _ tail.start ELSE GOTO ErrorReturn }}; source _ MoveToLimbo[sourceRoot, NodeSpan[s], event]; sourceRoot _ TiogaTreeOps.Root[source.start.node]; [result, newDest] _ Transpose[sourceRoot, destRoot, source, NodeSpan[d], event]; IF saveForPaste THEN SaveSpanForPaste[newDest, event]; RETURN; EXITS ErrorReturn => { UndoSplits[d, s, event]; ERROR CannotDoEdit }}}; Replace: PUBLIC PROC [ destRoot, sourceRoot: RefBranchNode, dest, source: TreeSpan, saveForPaste: BOOL _ TRUE, event: Event _ NIL] RETURNS [result: TreeSpan] = { newDest: TreeSpan; IF TextSpans[dest, source] THEN { -- pure text replace RETURN [TwoSpanText[destRoot, sourceRoot, dest, source, saveForPaste, TextEdit.ReplaceText, event]] }; source _ CopySpan[source]; sourceRoot _ TiogaTreeOps.Root[source.start.node]; sourceRoot.deleted _ TRUE; [result, newDest] _ Transpose[sourceRoot, destRoot, source, dest, event]; IF saveForPaste THEN SaveSpanForPaste[newDest, event] }; TextSpans: PROC [dest, source: TreeSpan] RETURNS [BOOLEAN] = { RETURN [dest.start.node = dest.end.node AND dest.start.where # NodeItself AND dest.end.where # NodeItself AND TiogaNodeOps.IsText[dest.start.node] AND source.start.node = source.end.node AND source.start.where # NodeItself AND source.end.where # NodeItself AND TiogaNodeOps.IsText[source.start.node]] }; TwoSpanText: PROC [destRoot, sourceRoot: RefBranchNode, dest, source: TreeSpan, saveForPaste: BOOLEAN, textProc: TextEdit.TwoSpanProc, event: Event] RETURNS [result: TreeSpan] = { destNode: RefTextNode _ TiogaNodeOps.NarrowToTextNode[dest.start.node]; sourceNode: RefTextNode _ TiogaNodeOps.NarrowToTextNode[source.start.node]; destStart: Offset _ dest.start.where; destLen: Offset _ dest.end.where - destStart + 1; sourceStart: Offset _ source.start.where; sourceLen: Offset _ source.end.where - sourceStart + 1; resultStart, resultLen: Offset; IF saveForPaste THEN SaveTextForPaste[destNode, destStart, destLen, event]; [resultStart, resultLen] _ textProc[destRoot, sourceRoot, destNode, destStart, destLen, sourceNode, sourceStart, sourceLen, event]; RETURN [[[destNode, resultStart], [destNode, resultStart+resultLen-1]]] }; SaveForPaste: PUBLIC PROC [span: TreeSpan, event: Event _ NIL] = { node: RefTextNode; IF CheckForNil[span] THEN RETURN; IF span.start.node = span.end.node AND span.start.where # NodeItself AND span.end.where # NodeItself AND (node _ TiogaNodeOps.NarrowToTextNode[span.start.node]) # NIL THEN { -- pure text start: Offset _ span.start.where; len: Offset _ span.end.where-span.start.where+1; SaveTextForPaste[node, start, len, event] } ELSE SaveSpanForPaste[CopySpan[span], event] }; Delete: PUBLIC PROC [ root: RefBranchNode, del: TreeSpan, event: Event _ NIL, saveForPaste: BOOL _ TRUE] = { node: RefTextNode; IF CheckForNil[del] THEN RETURN; IF del.start.node = del.end.node AND del.start.where # NodeItself AND del.end.where # NodeItself AND (node _ TiogaNodeOps.NarrowToTextNode[del.start.node]) # NIL THEN { -- pure text start: Offset _ del.start.where; len: Offset _ del.end.where-del.start.where+1; IF saveForPaste THEN SaveTextForPaste[node, start, len, event]; TextEdit.DeleteText[root, node, start, len, event] } ELSE { d: TreeSpan _ MoveToLimbo[root, del, event]; IF saveForPaste THEN SaveSpanForPaste[d, event] }}; paste: REF Change; SaveTextForPaste: PROC [node: RefTextNode, start, len: Offset, event: Event] = { SaveOldPaste[event]; paste _ NEW[ChangingTextForPaste Change _ [ChangingTextForPaste[node.rope, node.runs, start, len]]] }; SaveSpanForPaste: PROC [span: TreeSpan, event: Event] = { SaveOldPaste[event]; paste _ NEW[ChangingSpanForPaste Change _ [ChangingSpanForPaste[span]]] }; RecordGroupForPaste: PUBLIC PROC [start, end: Ref, event: Event] = { span: TreeSpan; span.start _ [start, NodeItself]; span.end _ [TiogaTreeOps.LastWithin[end], NodeItself]; SaveSpanForPaste[span, event] }; SaveOldPaste: PROC [event: Event] = { UndoEvent.Note[event, RestorePaste, paste]; paste _ NIL }; RestorePaste: PROC [undoRef: REF Change, currentEvent: UndoEvent.Ref] = { SaveOldPaste[currentEvent]; paste _ undoRef }; SavedForPaste: PUBLIC PROC RETURNS [span: TreeSpan] = TRUSTED { savedPaste: REF Change _ paste; IF savedPaste=NIL THEN RETURN [nullSpan]; WITH x: savedPaste SELECT FROM ChangingTextForPaste => { -- convert saved text to a span node: RefTextNode; root: RefBranchNode; span: TreeSpan; IF x.len <= 0 THEN RETURN [nullSpan]; node _ TiogaNodeOps.NewTextNode[TiogaNode.defaultTextClassID]; root _ TiogaNodeOps.DocFromNode[node]; [] _ TextEdit.ReplaceByText[root: root, dest: node, sourceRope: x.rope, sourceRuns: x.runs, sourceStart: x.start, sourceLen: x.len]; span _ [[node, 0], [node, x.len-1]]; paste _ NEW[ChangingSpanForPaste Change _ [ChangingSpanForPaste[span]]]; RETURN [span] }; ChangingSpanForPaste => RETURN [x.span]; ENDCASE => ERROR; }; MoveToLimbo: PROC [root: RefBranchNode, span: TreeSpan, event: Event] RETURNS [result: TreeSpan] = INLINE { RETURN [Move[NIL, root, TiogaTreeOps.nullLoc, span, after, 1, event]] }; Copy: PUBLIC PROC [ destRoot, sourceRoot: RefBranchNode, dest: TreeLoc, source: TreeSpan, where: Place _ after, nesting: INTEGER _ 0, event: Event _ NIL] RETURNS [result: TreeSpan] = { IF CheckForNil[source] OR dest.node=NIL THEN RETURN [nullSpan]; IF TextLocationAndSpan[dest, source] THEN -- pure text copy RETURN [DestSpanText[destRoot, sourceRoot, dest, source, TextEdit.CopyText, event]]; source _ CopySpan[source]; sourceRoot _ TiogaTreeOps.Root[source.start.node]; sourceRoot.deleted _ TRUE; result _ Move[destRoot, sourceRoot, dest, source, where, nesting, event] }; TextLocationAndSpan: PROC [dest: TreeLoc, source: TreeSpan] RETURNS [BOOLEAN] = { RETURN [dest.where # NodeItself AND source.start.node = source.end.node AND source.start.where # NodeItself AND source.end.where # NodeItself AND TiogaNodeOps.NarrowToTextNode[source.start.node] # NIL] }; DestSpanText: PROC [ destRoot, sourceRoot: RefBranchNode, dest: TreeLoc, source: TreeSpan, textProc: TextEdit.DestSpanProc, event: Event] RETURNS [result: TreeSpan] = { destNode: RefTextNode _ TiogaNodeOps.NarrowToTextNode[dest.node]; sourceNode: RefTextNode _ TiogaNodeOps.NarrowToTextNode[source.start.node]; destLoc: Offset _ dest.where; sourceStart: Offset _ source.start.where; sourceLen: Offset _ source.end.where - sourceStart + 1; resultStart, resultLen: Offset; [resultStart, resultLen] _ textProc[destRoot, sourceRoot, destNode, destLoc, sourceNode, sourceStart, sourceLen, event]; RETURN [[[destNode, resultStart], [destNode, resultStart+resultLen-1]]] }; ChangeNesting: PUBLIC PROC [ root: RefBranchNode, span: TreeSpan, change: INTEGER, event: Event _ NIL] RETURNS [new: TreeSpan] = { before, after, top, bottom: Slice; nesting: INTEGER; depth: NAT; s: BranchSpan; FreeSlices: PROC = { FreeSlice[before]; FreeSlice[after]; FreeSlice[top]; FreeSlice[bottom]; before _ after _ top _ bottom _ NIL }; { -- for exit IF CheckForNil[span] THEN RETURN [nullSpan]; [s, ] _ DoSplits[span, nullSpan, event]; -- so can deal with entire nodes DO -- only repeat this loop if have forced nest/unnest [before, after, top, bottom, nesting, depth] _ DescribeBand[s.start.node, s.end.node ! BadBand => { s _ nullBranchSpan; GOTO ErrorReturn } ]; IF nesting+change > 1 THEN GOTO ErrorReturn; -- cannot do it SELECT NeedNestingChange[before, after, top, bottom, nesting+change, depth] FROM needUnNest => { afterNode: RefBranchNode = TiogaNodeOps.NarrowToBranchNode[TreeSlice.LastOfSlice[after]]; last: RefBranchNode = TiogaTreeOps.LastBranchWithin[afterNode]; s: TreeSpan = TiogaTreeOps.MakeTreeSpan[afterNode, last]; IF afterNode=NIL THEN ERROR; FreeSlices[]; [] _ UnNest[root, s, event]; LOOP }; needNest => { last: RefBranchNode = TiogaTreeOps.LastBranchWithin[ TiogaNodeOps.NarrowToBranchNode[bottom[depth]]]; s: TreeSpan = TiogaTreeOps.MakeTreeSpan[top[depth], last]; IF last=NIL THEN ERROR; IF TreeSlice.SliceLength[top] = depth+1 THEN GOTO ErrorReturn; FreeSlices[]; [] _ Nest[root, s, event]; LOOP }; ENDCASE => { -- do it notify: REF NodeNesting Change; notify _ NEW[NodeNesting Change _ [NodeNesting[root, s.start.node, s.end.node, change]]]; Notify[notify, before]; DeletePrefix[top, depth]; DeletePrefix[bottom, depth]; ReplaceBand[before, after, top, bottom, nesting+change, event]; FreeSlices[]; Notify[notify, after]; UndoEvent.Note[event, UndoChangeNesting, notify] }; EXIT; ENDLOOP; span.start _ [s.start.node, NodeItself]; span.end _ [s.end.node, NodeItself]; RETURN [span]; EXITS ErrorReturn => { FreeSlices[]; UndoSplits[s, nullBranchSpan, event]; ERROR CannotDoEdit }}}; UndoChangeNesting: PROC [undoRef: REF, currentEvent: Event] = TRUSTED { saved: REF Change _ NARROW[undoRef]; WITH x:saved SELECT FROM NodeNesting => { [] _ ChangeNesting[x.root, TiogaTreeOps.MakeTreeSpan[x.first, x.last], -x.change, currentEvent] }; ENDCASE => ERROR }; CheckForNil: PROC [span: TreeSpan] RETURNS [BOOLEAN] = INLINE { RETURN [span.start.node = NIL OR span.end.node = NIL] }; NodeSpan: PROC [span: BranchSpan] RETURNS [TreeSpan] = INLINE { RETURN [[[span.start.node, NodeItself], [span.end.node, NodeItself]]] }; NodeLoc: PROC [loc: BranchLoc] RETURNS [TreeLoc] = INLINE { RETURN [[loc.node, NodeItself]] }; END.... EditSpanImpl.mesa; written by Bill Paxton, June 1981 edited by McGregor, February 14, 1983 1:52 pm edited by Bill Paxton, December 23, 1982 2:47 pm edited by Maxwell, January 5, 1983 12:46 pm ***** Editing operations on spans dest cannot be within source result is moved span split source and dest, if necessary, so can deal with entire nodes check for d already in correct position Dest is inside a list or box node. Check to see if statement is inside s newAlpha is new location of alpha span; ditto for newBeta now check for a b adjacent or overlapping as special cases move b nodes to before a nodes move a nodes to before b nodes check for overlap like Replace, but moves source instead of copying it result is moved span get slices for d and s check for overlap replace dest span by copy of source span result is the new copy of source --really REF ChangingTextForPaste/ChangingSpanForPaste Change result is the new copy of source ***** Nesting moves span to a deeper nesting level in tree ***** Miscellaneous ÊJ˜JšÏc4™4Jš-™-Jš0™0Jš œ"™+J˜JšÏk ˜ J˜ J˜J˜ J˜J˜ J˜ J˜ J˜ J˜ J˜ J˜ J˜šœž œ˜Jšžœb˜iJšžœ ˜Jšžœ ˜—Jšž˜Jšžœ2˜6J˜Jšœžœžœžœ˜"Jšœžœ ˜)Jšœžœ˜J˜+J˜9J˜Jšœ!™!˜šÏnœžœžœG˜XJšœžœžœ˜?Jšžœ˜Jšœ™Jšœ™J˜7Jšœžœ˜Jšœžœ˜ Jšœ*+˜UJ˜J˜ J˜J˜šŸ œžœ˜J˜LJ˜&Jšœ7žœ˜>J˜—šŸ œžœžœ˜6JšœB˜BJšœF˜FJšžœ žœžœžœ˜J˜ J˜,J˜—šŸ œžœžœ˜'JšœI˜IJšœF˜FJšžœžœžœžœ˜šžœ(žœ˜OJšžœžœžœ˜Jšœ˜1J˜ Jšžœ˜ —J˜ J˜0J˜—Jšžœžœžœ ˜.J˜šžœ#žœ˜;šžœN˜TJ˜——Jšžœ žœ'˜:šžœžœžœ˜J˜!Jšœ7˜7Jšœ2˜2Jšœ˜Jšœ˜JšœQ˜Q—J˜JšœB™BJšœ5˜5Jšœ8˜8Jšœ2˜2J˜%J˜Jšœ ˜Jšœ'™'šžœ ž˜%šœžœžœ˜CJšžœžœžœ˜H—šžœ4˜;šžœ ž˜šžœžœžœž˜ Jšœ*˜*Jšž˜——šžœžœ ž˜šžœžœžœž˜ Jšœ,˜,Jšžœ˜ ————šžœžœN˜W˜4JšœIžœ˜^—šžœ žœ˜;J˜3J˜%Jšœžœ<˜UJ˜—Jšœ8˜8šžœ5žœ˜;Jšœ5žœžœ ˜Y—šžœ$žœ&˜OJšžœžœ ˜J™I—šžœžœ7˜YJšœ˜J˜ J˜*Jšžœ˜—J˜šžœEž˜OJšœ=žœ˜DJšœžœ˜#Jšžœ˜—šžœBžœ˜JJšœ-žœ˜3—šžœ˜Jšœžœ˜šœ žœ#˜/J˜7J˜>—J˜J˜:J˜=J˜J˜ J˜J˜/—Jšžœ˜Jšžœ˜J˜—šžœžœ˜3Jšœ žœžœ˜šžœžœ&˜KJšœžœ˜ Jšœ:˜:—šžœžœ#˜FJšœžœ˜ Jšœ7žœ˜>—šžœžœžœ˜6JšœP˜P——šžœžœžœžœ˜FJšœP˜PJ˜—Jšœ˜0Jš œžœ žœžœžœ ˜ZJ˜J˜?Jšœ3˜3Jšžœ ˜J˜šžœ˜J˜ J˜&Jšžœ˜J˜——šŸ œžœ žœžœ˜CJšœžœ žœ ˜$Jšžœ žœž˜˜˜#JšœM˜MJšœ"˜"——Jšžœžœ˜J˜—šŸœžœ%žœ žœ˜Kšžœ˜"Jšœ9˜9Jšœ˜Jšœ ˜ J˜šœžœ4žœ˜?Jšœ4˜4—Jšœ$˜$Jšžœžœžœ˜Jšžœžœžœ˜*Jšžœ˜ J˜——šŸ œžœžœLžœ˜fJšžœ"˜)Jšœ9™9J˜J˜FJšœžœ˜Jšœžœ˜Jšœ?!˜`J˜%J˜J˜šŸ œžœ˜J˜LJ˜KJšœHžœ˜OJ˜—Jšœ ˜ J˜šžœžœž˜/Jšžœ˜J˜—šžœžœ˜7J˜IJ˜GJ˜'J˜4J˜%J˜1J˜Išœ\˜\J˜[—J˜YJ˜WJšžœ˜ J˜—Jšœ' ˜GJ˜Jšœ7˜7Jšœ1˜1J˜ Jšœ6˜6Jšœ0˜0J˜J˜Jšœ:™:šžœžœ˜6Jšœ™JšœQ˜Q—šžœžœžœ˜:Jšœ™JšœQ˜Q—šžœ<˜DJšœ žœ˜Jšœ2˜JJ˜˜4šœ3˜3Jšœžœ˜.——˜4šœ2˜2Jšœžœ˜.——šžœ$žœ&˜OJšžœžœ˜5—šžœ$žœ&˜OJšžœžœ˜5—Jšœ™˜-J˜5—šžœ žœ˜"J˜ Jšžœžœžœž˜<šžœžœžœ ˜EJšœS˜Sšžœžœ˜/J˜%—šžœ˜J˜&——šžœžœžœ˜BJšœP˜Pšžœžœ˜5J˜%—šžœ˜ J˜&——Jš žœžœžœžœ˜Ešžœ˜ JšœK˜Kšžœžœ˜5J˜$J˜%—šžœ˜ J˜$J˜'———šžœ˜#J˜J˜JšœI˜IJšœI˜IJ˜ Jšœ<˜˜SJšœžœžœžœ˜1Jšžœ˜Jšœ4™4Jšœ™Jšœ žœ˜Jšœ˜Jšœ9˜QJ˜J˜FJšœžœ˜Jšœžœ˜J˜J˜šŸ œžœ˜J˜LJ˜KJšœHžœ˜OJ˜—Jšœ ˜ J˜Jšžœžœžœžœ ˜CJ˜šžœžœ˜3šžœa˜gJ˜——J˜'J˜Jšœ™˜4JšœNžœ˜b—˜4šœMžœ˜aJ˜——šžœ$žœ&˜OJšžœžœ˜5—J˜J˜cJ˜ J˜Jšœ™šžœ žœ-˜?Jšžœžœžœžœ ˜Išžœžœ˜4Jšžœžœžœ ˜>Jšžœ˜—šžœžœžœ˜4Jšžœžœ˜DJšžœžœ˜1—šžœ˜!šžœžœžœ˜.J˜@—Jšžœžœžœ˜/Jšžœžœžœ˜0Jšžœžœ˜J˜——J˜5J˜2J˜PJšžœžœ"˜6Jšžœ˜J˜šžœ+žœ˜GJ˜——šŸœžœžœ˜Jšœ<˜šžœ"ž˜+Jšœžœž˜AJšœ%ž˜(Jšœ$ž˜'Jšœ žœž˜EJšœ*˜*J˜——šŸ œžœ>˜OJšœžœ/˜DJšžœ˜J˜GJ˜KJ˜%J˜1J˜)J˜7J˜Jšžœžœ7˜Kšœ9˜9J˜I—JšžœD˜JJ˜—šŸ œžœžœ!žœ˜BJ˜Jšžœžœžœ˜!šžœ!ž˜&Jšœžœž˜Ašœ:žœžœ ˜QJ˜!J˜0J˜+——Jšžœ+˜/J˜—šŸœžœžœ˜Jšœ3žœžœžœ˜VJšœž˜Jšžœžœžœ˜ šžœž˜$šœžœž˜?Jšœ9žœžœ ˜PJ˜ J˜.Jšžœžœ+˜?J˜4——šžœ˜J˜,Jšžœžœ˜3J˜——Jšœžœ˜Jš=™=J˜šŸœžœ:˜PJ˜šœžœ˜)J˜<—J˜—šŸœžœ#˜9J˜Jšœžœ?˜JJ˜—šŸœž œ$˜DJ˜J˜!J˜6J˜ J˜—šŸ œžœ˜%Jšœ4žœ˜:J˜—šŸ œžœ žœ)˜IJ˜.J˜—š Ÿ œžœžœžœžœ˜?Jšœ žœ˜Jšžœ žœžœžœ ˜)Jšžœžœž˜šœ˜9J˜J˜J˜Jšžœ žœžœ ˜%Jšœ>˜>J˜&˜3J˜'J˜(—J˜$Jšœžœ=˜HJšžœ ˜—Jšœžœ ˜(Jšžœžœ˜J˜J˜—šŸ œžœ5žœ˜bJšžœžœžœ8˜QJ˜—šŸœžœžœ˜JšœE˜EJšœžœžœ˜?Jšžœ˜Jšœ ™ Jš žœžœ žœžœžœ ˜?šžœ#žœ˜;JšžœN˜T—J˜J˜2Jšœžœ˜J˜KJ˜—šŸœžœ#žœžœ˜Qšžœž˜#šœ$ž˜'šœ žœ˜AJšžœ4žœ˜>—J˜———šŸ œžœ˜JšœE˜EJ˜.Jšžœ˜J˜AJ˜KJ˜J˜)J˜7J˜šœ9˜9J˜>—JšžœD˜J—J˜—Jšœ ™ ˜šŸ œžœžœ˜Jšœ-žœžœ˜IJšžœ˜Jšœ,™,J˜"Jšœ žœ˜Jšœžœ˜ J˜J˜šŸ œžœ˜J˜HJšœ žœ˜'J˜—Jšœ ˜ J˜Jšžœžœžœ ˜,J˜Jšœ) ˜IJ˜Jšžœ3˜6˜.Jšœ2žœžœ˜^—Jšžœžœžœ˜J˜ J˜Jšžœ˜—šžœ˜Jšœžœ˜Jšœ žœM˜YJ˜J˜J˜J˜?J˜ J˜J˜3—Jšžœ˜Jšžœ˜J˜J˜(J˜$Jšžœ˜J˜šžœ˜Jšœ4žœ˜KJ˜——šŸœžœ žœžœ˜GJšœžœ žœ ˜$Jšžœ žœž˜˜Jšœb˜b—Jšžœžœ˜J˜——šœ™J˜š Ÿ œžœžœžœžœ˜?Jšžœžœžœžœ˜8J˜—šŸœžœžœžœ˜?JšžœB˜HJ˜—š Ÿœžœžœ žœžœ˜^J˜——J˜Jšžœ˜—…—^<v†