DIRECTORY EditNotify USING [Change, Notify], EditSpan USING [ChangeNesting, Event, Location, Looks, Nest, Node, NodeOrder, nullLocation, nullSpan, Place, Span, UnNest], EditSpanSupport USING [Apply, BadBand, CompareSliceOrder, CopySpan, CreateDest, DeletePrefix, DescribeBand, DestSlices, DoSplits, DoSplits2, FreeSlice, LastOfSlice, NeedNestingChange, NodeItself, NodeLoc, NodeSpan, ReMerge, ReplaceBand, Slice, SliceLength, SliceOrder, Splice, UndoSplits, UndoSplits2], NodeProps USING [CopyInfo, Is, MapProps, PutProp], TextEdit USING [CapChange, ChangeCaps, ChangeLooks, CopyText, DeleteText, DestSpanProc, MoveText, MoveTextOnto, ReplaceText, TransposeProc, TransposeText, TwoSpanProc], TextNode USING [LastSibling, LastWithin, Level, MakeNodeLoc, MakeNodeSpan, NewTextNode, Parent, Previous, Root, StepBackward, StepForward], UndoEvent USING [Note]; EditSpanImpl: CEDAR PROGRAM IMPORTS EditSpan, EditSpanSupport, EditNotify, NodeProps, TextEdit, TextNode, UndoEvent EXPORTS EditSpan = BEGIN OPEN EditSpan; Change: TYPE = EditNotify.Change; CannotDoEdit: PUBLIC ERROR = CODE; afterMoved1: PUBLIC Location; afterMoved2: PUBLIC Location; CheckForNil: PROC [span: Span] RETURNS [BOOLEAN] = INLINE { RETURN [span.start.node = NIL OR span.end.node = NIL] }; Replace: PUBLIC PROC [destRoot, sourceRoot: Node, dest, source: Span, saveForPaste: BOOLEAN _ TRUE, event: Event _ NIL] RETURNS [result: Span] = { newDest: Span; IF TextSpans[dest, source] THEN { -- pure text replace RETURN [TwoSpanText[destRoot, sourceRoot, dest, source, saveForPaste, TextEdit.ReplaceText, event]] }; source _ EditSpanSupport.CopySpan[source]; sourceRoot _ TextNode.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: Span] RETURNS [BOOLEAN] = INLINE --gfi saver-- { RETURN [ dest.start.node = dest.end.node AND dest.start.where # EditSpanSupport.NodeItself AND dest.end.where # EditSpanSupport.NodeItself AND source.start.node = source.end.node AND source.start.where # EditSpanSupport.NodeItself AND source.end.where # EditSpanSupport.NodeItself ] }; TwoSpanText: PROC [destRoot, sourceRoot: Node, dest, source: Span, saveForPaste: BOOLEAN, textProc: TextEdit.TwoSpanProc, event: Event] RETURNS [result: Span] = INLINE --gfi saver-- { destNode: Node _ dest.start.node; sourceNode: Node _ source.start.node; destStart: INT _ dest.start.where; destLen: INT _ dest.end.where - destStart + 1; sourceStart: INT _ source.start.where; sourceLen: INT _ source.end.where - sourceStart + 1; resultStart, resultLen: INT; 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: Span, event: Event _ NIL] = { IF CheckForNil[span] THEN RETURN; IF span.start.node = span.end.node AND span.start.where # EditSpanSupport.NodeItself AND span.end.where # EditSpanSupport.NodeItself THEN { -- pure text node: Node _ span.start.node; start: INT _ span.start.where; len: INT _ span.end.where-span.start.where+1; SaveTextForPaste[node, start, len, event] } ELSE SaveSpanForPaste[EditSpanSupport.CopySpan[span], event] }; Delete: PUBLIC PROC [root: Node, del: Span, event: Event _ NIL, saveForPaste: BOOLEAN _ TRUE] = { IF CheckForNil[del] THEN RETURN; IF del.start.node = del.end.node AND del.start.where # EditSpanSupport.NodeItself AND del.end.where # EditSpanSupport.NodeItself THEN { -- pure text node: Node _ del.start.node; start: INT _ del.start.where; len: INT _ del.end.where-del.start.where+1; IF saveForPaste THEN SaveTextForPaste[node, start, len, event]; TextEdit.DeleteText[root, node, start, len, event] } ELSE { d: Span _ MoveToLimbo[root, del, event]; IF saveForPaste THEN SaveSpanForPaste[d, event] } }; paste: REF Change.ChangingSpanForPaste; SaveTextForPaste: PROC [node: Node, start, len: INT, event: Event] = INLINE --gfi saver-- { span: Span ~ IF len = 0 THEN nullSpan ELSE [[node, start], [node, start+len-1]]; SaveSpanForPaste[EditSpanSupport.CopySpan[span], event]; }; SaveSpanForPaste: PROC [span: Span, event: Event] = INLINE --gfi saver-- { SaveOldPaste[event]; paste _ NEW[Change.ChangingSpanForPaste _ [ChangingSpanForPaste[span]]]; }; SaveOldPaste: PROC [event: Event] = INLINE --gfi saver-- { UndoEvent.Note[event, RestorePaste, paste]; paste _ NIL }; RestorePaste: PROC [undoRef: REF Change, currentEvent: Event] = { SaveOldPaste[currentEvent]; paste _ NARROW[undoRef] }; SavedForPaste: PUBLIC PROC RETURNS [span: Span] = { savedPaste: REF Change _ paste; IF savedPaste=NIL THEN RETURN [nullSpan]; WITH savedPaste SELECT FROM x: REF Change.ChangingSpanForPaste => RETURN [x.span]; ENDCASE => ERROR; }; MoveToLimbo: PROC [root: Node, span: Span, event: Event] RETURNS [result: Span] = INLINE --gfi saver-- { RETURN [Move[NIL, root, nullLocation, span, after, 1, event]] }; Copy: PUBLIC PROC [destRoot, sourceRoot: Node, dest: Location, source: Span, where: Place _ after, nesting: INTEGER _ 0, event: Event _ NIL] RETURNS [result: Span] = { 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 _ EditSpanSupport.CopySpan[source]; sourceRoot _ TextNode.Root[source.start.node]; sourceRoot.deleted _ TRUE; result _ Move[destRoot, sourceRoot, dest, source, where, nesting, event] }; TextLocationAndSpan: PROC [dest: Location, source: Span] RETURNS [BOOLEAN] = INLINE --gfi saver-- { RETURN [dest.where # EditSpanSupport.NodeItself AND source.start.node = source.end.node AND source.start.where # EditSpanSupport.NodeItself AND source.end.where # EditSpanSupport.NodeItself] }; DestSpanText: PROC [destRoot, sourceRoot: Node, dest: Location, source: Span, textProc: TextEdit.DestSpanProc, event: Event] RETURNS [result: Span] = { destNode: Node _ dest.node; sourceNode: Node _ source.start.node; destLoc: INT _ dest.where; sourceStart: INT _ source.start.where; sourceLen: INT _ source.end.where - sourceStart + 1; resultStart, resultLen: INT; [resultStart, resultLen] _ textProc[destRoot, sourceRoot, destNode, destLoc, sourceNode, sourceStart, sourceLen, event]; RETURN [[[destNode, resultStart], [destNode, resultStart+resultLen-1]]] }; Move: PUBLIC PROC [destRoot, sourceRoot: Node, dest: Location, source: Span, where: Place _ after, nesting: INTEGER _ 0, event: Event _ NIL] RETURNS [result: Span] = { sBefore, sAfter, sTop, sBottom, dBefore, dAfter: EditSpanSupport.Slice; sNesting, dNesting: INTEGER; sDepth: NAT; beforeSource, afterSource: Node; -- nodes adjacent to source after do splits afterLoc: Location; FreeSlices: PROC = { EditSpanSupport.FreeSlice[sBefore]; EditSpanSupport.FreeSlice[sAfter]; EditSpanSupport.FreeSlice[sTop]; EditSpanSupport.FreeSlice[sBottom]; EditSpanSupport.FreeSlice[dBefore]; EditSpanSupport.FreeSlice[dAfter]; sBefore _ sAfter _ sTop _ sBottom _ dBefore _ dAfter _ NIL }; ForcedUnNest: PROC [afterNode: Node] = { span: Span = TextNode.MakeNodeSpan[afterNode, TextNode.LastWithin[afterNode]]; FreeSlices[]; [] _ UnNest[TextNode.Root[afterNode], span, event] }; ForcedNest: PROC = { span: Span _ TextNode.MakeNodeSpan[sTop[sDepth], TextNode.LastWithin[sBottom[sDepth]]]; IF EditSpanSupport.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[TextNode.Root[span.start.node], span, event] }; IF CheckForNil[source] THEN RETURN [nullSpan]; IF TextLocationAndSpan[dest, source] THEN { RETURN [DestSpanText[destRoot, sourceRoot, dest, source, TextEdit.MoveText, event]]; }; SELECT where FROM child => { where _ after; nesting _ nesting+1 }; sibling => { newDest: Node = TextNode.LastWithin[dest.node]; where _ after; nesting _ nesting + TextNode.Level[dest.node] - TextNode.Level[newDest]; dest _ [newDest, EditSpanSupport.NodeItself]; }; ENDCASE => NULL; [dest, source, where, nesting] _ EditSpanSupport.DoSplits2[dest, source, where, nesting, event]; beforeSource _ TextNode.StepBackward[source.start.node]; afterSource _ TextNode.StepForward[source.end.node]; afterLoc _ [afterSource, 0]; { -- for exits IF dest # nullLocation AND ((where = after AND (dest.node = beforeSource OR dest.node = source.end.node)) OR (where = before AND (dest.node = afterSource OR dest.node = source.start.node))) THEN { -- not going anywhere, but might be changing nesting SELECT nesting FROM > 0 => { FOR i:INTEGER IN [0..nesting) DO [] _ Nest[sourceRoot, EditSpanSupport.NodeSpan[source], event]; ENDLOOP }; < 0 => { FOR i:INTEGER IN [nesting..0) DO [] _ UnNest[sourceRoot, EditSpanSupport.NodeSpan[source], event]; ENDLOOP }; ENDCASE => NULL; } ELSE { DO -- repeat this loop only if have forced nest or unnest or source and dest in same tree [sBefore, sAfter, sTop, sBottom, sNesting, sDepth] _ EditSpanSupport.DescribeBand[source.start.node, source.end.node ! EditSpanSupport.BadBand => { source _ nullSpan; GOTO ErrorReturn } ]; IF dest = nullLocation THEN { -- moving to limbo dest _ EditSpanSupport.CreateDest[EditSpanSupport.SliceLength[sTop]-sDepth]; destRoot _ TextNode.Root[dest.node]; destRoot.deleted _ TRUE; -- so will free this when it falls off the edit history list where _ after; nesting _ 1 }; [dBefore, dAfter, dNesting] _ EditSpanSupport.DestSlices[dest.node, where]; IF EditSpanSupport.CompareSliceOrder[dBefore, sBefore]=after AND EditSpanSupport.CompareSliceOrder[dBefore, sBottom]=before THEN GOTO ErrorReturn; IF dBefore[0] = sBefore[0] THEN { -- source and dest in same tree span: Span = TextNode.MakeNodeSpan[source.start.node, source.end.node]; FreeSlices[]; [] _ MoveToLimbo[sourceRoot, span, event]; LOOP }; dNesting _ dNesting + nesting; SELECT EditSpanSupport.NeedNestingChange[dBefore, dAfter, sTop, sBottom, dNesting, sDepth] FROM needUnNest => { ForcedUnNest[EditSpanSupport.LastOfSlice[dAfter]]; LOOP }; needNest => { ForcedNest[]; LOOP }; ENDCASE; IF EditSpanSupport.SliceLength[sAfter] > EditSpanSupport.SliceLength[sBefore]+1 THEN { ForcedUnNest[EditSpanSupport.LastOfSlice[sAfter]]; LOOP } ELSE { -- do it notify: REF MovingNodes Change; notify _ NEW[MovingNodes Change _ [MovingNodes[ destRoot, sourceRoot, dest.node, source.start.node, source.end.node, EditSpanSupport.LastOfSlice[sBefore], sNesting, (where # before)]]]; EditNotify.Notify[notify, before]; EditSpanSupport.DeletePrefix[sTop, sDepth]; EditSpanSupport.DeletePrefix[sBottom, sDepth]; EditSpanSupport.ReplaceBand[dBefore, dAfter, sTop, sBottom, dNesting, event]; EditSpanSupport.Splice[sBefore, sAfter]; FreeSlices[]; EditNotify.Notify[notify, after]; UndoEvent.Note[event, UndoMoveNodes, notify] }; EXIT; ENDLOOP }; IF dest.where # EditSpanSupport.NodeItself THEN { -- undo prior splits start, end: BOOLEAN _ FALSE; IF source.start.where # EditSpanSupport.NodeItself THEN { -- merge start of source with front of dest start _ TRUE; [source, ] _ EditSpanSupport.ReMerge[source, nullSpan, source.start.node, event] }; IF source.end.where # EditSpanSupport.NodeItself THEN { -- merge end of source with tail of dest end _ TRUE; [source, ] _ EditSpanSupport.ReMerge[source, nullSpan, source.end.node, event, TRUE] }; IF start AND end THEN { -- merge before source with after source afterLoc _ Merge[TextNode.Root[afterSource], afterSource, event] } } ELSE IF source.start.where # EditSpanSupport.NodeItself AND source.end.where # EditSpanSupport.NodeItself THEN { afterLoc _ Merge[TextNode.Root[afterSource], afterSource, event] }; afterMoved2 _ afterMoved1; -- save previous hint afterMoved1 _ IF afterSource=NIL THEN [beforeSource, 0] ELSE afterLoc; -- hint for repaint RETURN [source]; EXITS ErrorReturn => { FreeSlices[]; [, source] _ EditSpanSupport.UndoSplits2[dest, source, event]; ERROR CannotDoEdit } } }; UndoMoveNodes: PROC [undoRef: REF, currentEvent: Event] = { saved: REF Change _ NARROW[undoRef]; WITH saved SELECT FROM x: REF Change.MovingNodes => { [] _ Move[x.sourceRoot, x.destRoot, TextNode.MakeNodeLoc[x.pred], TextNode.MakeNodeSpan[x.first, x.last], after, x.nesting, currentEvent] }; ENDCASE => ERROR }; Transpose: PUBLIC PROC [alphaRoot, betaRoot: Node, alpha, beta: Span, event: Event _ NIL] RETURNS [newAlpha, newBeta: Span] = { aBefore, aAfter, aTop, aBottom, bBefore, bAfter, bTop, bBottom: EditSpanSupport.Slice; aNesting, bNesting: INTEGER; aDepth, bDepth: NAT; beforeAlpha, afterAlpha, beforeBeta, afterBeta: Node; -- nodes adjacent after do splits afterAlphaLoc, afterBetaLoc: Location; FreeSlices: PROC = { EditSpanSupport.FreeSlice[aBefore]; EditSpanSupport.FreeSlice[aAfter]; EditSpanSupport.FreeSlice[aTop]; EditSpanSupport.FreeSlice[aBottom]; EditSpanSupport.FreeSlice[bBefore]; EditSpanSupport.FreeSlice[bAfter]; EditSpanSupport.FreeSlice[bTop]; EditSpanSupport.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: Node _ alpha.start.node; betaNode: Node _ beta.start.node; alphaStart: INT _ alpha.start.where; alphaLen: INT _ alpha.end.where - alphaStart + 1; betaStart: INT _ beta.start.where; betaLen: INT _ beta.end.where - betaStart + 1; alphaResultStart, alphaResultLen, betaResultStart, betaResultLen: INT; [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 }; [alpha, beta] _ EditSpanSupport.DoSplits[alpha, beta, event]; -- so can deal with entire nodes beforeAlpha _ TextNode.StepBackward[alpha.start.node]; afterAlpha _ TextNode.StepForward[alpha.end.node]; afterAlphaLoc _ [afterAlpha, 0]; beforeBeta _ TextNode.StepBackward[beta.start.node]; afterBeta _ TextNode.StepForward[beta.end.node]; afterBetaLoc _ [afterBeta, 0]; IF afterAlpha = beta.start.node THEN { -- alpha just before beta [] _ Move[alphaRoot, betaRoot, EditSpanSupport.NodeLoc[alpha.start], EditSpanSupport.NodeSpan[beta], before, 0, event] } ELSE IF afterBeta = alpha.start.node THEN { -- beta just before alpha [] _ Move[betaRoot, alphaRoot, EditSpanSupport.NodeLoc[beta.start], EditSpanSupport.NodeSpan[alpha], before, 0, event] } ELSE { -- get slices describing the bands of nodes to be transposed overlap: BOOLEAN; head, tail: Span; -- sections of alpha or beta before and after the overlap startOrder, endOrder: NodeOrder; [aBefore, aAfter, aTop, aBottom, aNesting, aDepth] _ EditSpanSupport.DescribeBand[alpha.start.node, alpha.end.node ! EditSpanSupport.BadBand => { alpha _ beta _ nullSpan; GOTO ErrorReturn }]; [bBefore, bAfter, bTop, bBottom, bNesting, bDepth] _ EditSpanSupport.DescribeBand[beta.start.node, beta.end.node ! EditSpanSupport.BadBand => { alpha _ beta _ nullSpan; GOTO ErrorReturn }]; [overlap, head, tail, startOrder, endOrder] _ EditSpanSupport.SliceOrder[alpha, beta, aBefore, aBottom, bBefore, bBottom]; IF overlap THEN { -- bands overlap FreeSlices[]; SELECT TRUE FROM head = nullSpan AND tail = nullSpan => NULL; head = nullSpan => { --move tail to before alphastart [] _ Move[alphaRoot, betaRoot, EditSpanSupport.NodeLoc[alpha.start], EditSpanSupport.NodeSpan[tail], before, 0, event]; IF endOrder=before THEN { -- alpha end before beta end beta.start _ tail.start; beta.end _ alpha.end } ELSE { -- beta end before alpha end alpha.start _ tail.start; alpha.end _ beta.end } }; tail = nullSpan => { --move head to after alphaend [] _ Move[alphaRoot, betaRoot, EditSpanSupport.NodeLoc[alpha.end], EditSpanSupport.NodeSpan[head], after, 0, event]; IF startOrder=before THEN { -- alpha start before beta start alpha.start _ beta.start; alpha.end _ head.end } ELSE { -- beta start before alpha start beta.start _ alpha.start; beta.end _ head.end } }; startOrder # endOrder => NULL; -- one contained in the other ENDCASE => { --transpose head and tail [] _ Transpose[alphaRoot, betaRoot, EditSpanSupport.NodeSpan[head], EditSpanSupport.NodeSpan[tail], event]; IF startOrder=before THEN { -- alpha start before beta start alpha.start _ beta.start; alpha.end _ head.end; beta.start _ tail.start; beta.end _ alpha.end } ELSE { -- beta start before alpha start beta.start _ alpha.start; beta.end _ head.end; alpha.start _ tail.start; alpha.end _ beta.end } }; } ELSE { -- no overlap, do transpose as two moves aSpan, bSpan: Span; after1, after2: Location; bLoc: Location _ TextNode.MakeNodeLoc[EditSpanSupport.LastOfSlice[bBefore]]; aLoc: Location _ TextNode.MakeNodeLoc[EditSpanSupport.LastOfSlice[aBefore]]; FreeSlices[]; aSpan _ TextNode.MakeNodeSpan[alpha.start.node, alpha.end.node]; bSpan _ TextNode.MakeNodeSpan[beta.start.node, beta.end.node]; [] _ MoveToLimbo[alphaRoot, aSpan, event]; after1 _ afterMoved1; -- repaint hints [] _ MoveToLimbo[betaRoot, bSpan, event]; after2 _ afterMoved1; [] _ Move[betaRoot, TextNode.Root[aSpan.start.node], bLoc, aSpan, after, bNesting, event]; [] _ Move[alphaRoot, TextNode.Root[bSpan.start.node], aLoc, bSpan, after, aNesting, event]; afterMoved1 _ after1; afterMoved2 _ after2 } }; IF alpha.start.where # EditSpanSupport.NodeItself AND beta.start.where # EditSpanSupport.NodeItself THEN { -- remerge starts [alpha, beta] _ EditSpanSupport.ReMerge[alpha, beta, alpha.start.node, event]; [alpha, beta] _ EditSpanSupport.ReMerge[alpha, beta, beta.start.node, event] }; IF alpha.end.where # EditSpanSupport.NodeItself AND beta.end.where # EditSpanSupport.NodeItself THEN { -- remerge ends [alpha, beta] _ EditSpanSupport.ReMerge[alpha, beta, alpha.end.node, event, TRUE]; [alpha, beta] _ EditSpanSupport.ReMerge[alpha, beta, beta.end.node, event, TRUE]; afterAlphaLoc _ beta.end; afterBetaLoc _ alpha.end }; afterMoved1 _ IF afterAlphaLoc.node=NIL THEN [beforeAlpha, 0] ELSE afterAlphaLoc; -- hint for repaint afterMoved2 _ IF afterBetaLoc.node=NIL THEN [beforeBeta, 0] ELSE afterBetaLoc; RETURN [alpha, beta]; EXITS ErrorReturn => { FreeSlices[]; [alpha, beta] _ EditSpanSupport.UndoSplits[alpha, beta, event]; ERROR CannotDoEdit } } }; MoveOnto: PUBLIC PROC [destRoot, sourceRoot: Node, dest, source: Span, saveForPaste: BOOLEAN _ TRUE, event: Event _ NIL] RETURNS [result: Span] = { overlap: BOOLEAN; head, tail, newDest: Span; -- sections of alpha or beta before and after the overlap startOrder, endOrder: NodeOrder; aBefore, aAfter, aTop, aBottom, bBefore, bAfter, bTop, bBottom: EditSpanSupport.Slice; aNesting, bNesting: INTEGER; aDepth, bDepth: NAT; FreeSlices: PROC = INLINE --gfi saver-- { EditSpanSupport.FreeSlice[aBefore]; EditSpanSupport.FreeSlice[aAfter]; EditSpanSupport.FreeSlice[aTop]; EditSpanSupport.FreeSlice[aBottom]; EditSpanSupport.FreeSlice[bBefore]; EditSpanSupport.FreeSlice[bAfter]; EditSpanSupport.FreeSlice[bTop]; EditSpanSupport.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]] }; [dest, source] _ EditSpanSupport.DoSplits[dest, source, event]; [aBefore, aAfter, aTop, aBottom, aNesting, aDepth] _ EditSpanSupport.DescribeBand[dest.start.node, dest.end.node ! EditSpanSupport.BadBand => { dest _ source _ nullSpan; GOTO ErrorReturn }]; [bBefore, bAfter, bTop, bBottom, bNesting, bDepth] _ EditSpanSupport.DescribeBand[source.start.node, source.end.node ! EditSpanSupport.BadBand => { dest _ source _ nullSpan; GOTO ErrorReturn }]; [overlap, head, tail, startOrder, endOrder] _ EditSpanSupport.SliceOrder[dest, source, aBefore, aBottom, bBefore, bBottom]; FreeSlices[]; IF overlap THEN { -- bands overlap. modify dest so doesn't overlap SELECT TRUE FROM head = nullSpan AND tail = nullSpan => GOTO ErrorReturn; head = nullSpan => { -- source start = dest start IF endOrder=before THEN {GOTO ErrorReturn} -- dest end before source end ELSE {dest.start _ tail.start} }; tail = nullSpan => { --source end = dest end IF startOrder=before THEN {dest.end _ head.end} -- dest start before source start -- ELSE {GOTO ErrorReturn} -- source start before dest start -- }; ENDCASE => { -- have both head and tail SELECT TRUE FROM startOrder=before AND endOrder=after => { [] _ Delete[destRoot, tail, event]; dest.end _ head.end }; startOrder=before => dest.end _ head.end; endOrder=after => dest.start _ tail.start; ENDCASE => GOTO ErrorReturn; }; }; [dest, source] _ EditSpanSupport.UndoSplits[dest, source, event]; source _ MoveToLimbo[sourceRoot, source, event]; sourceRoot _ TextNode.Root[source.start.node]; [result, newDest] _ Transpose[sourceRoot, destRoot, source, dest, event]; IF saveForPaste THEN SaveSpanForPaste[newDest, event]; RETURN; EXITS ErrorReturn => { [dest, source] _ EditSpanSupport.UndoSplits[dest, source, event]; ERROR CannotDoEdit } } }; ChangeNesting: PUBLIC PROC [root: Node, span: Span, change: INTEGER, event: Event _ NIL] RETURNS [new: Span] = { before, after, top, bottom: EditSpanSupport.Slice; nesting: INTEGER; depth: NAT; FreeSlices: PROC = { EditSpanSupport.FreeSlice[before]; EditSpanSupport.FreeSlice[after]; EditSpanSupport.FreeSlice[top]; EditSpanSupport.FreeSlice[bottom]; before _ after _ top _ bottom _ NIL }; { -- for exit IF CheckForNil[span] THEN RETURN [nullSpan]; [span, ] _ EditSpanSupport.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] _ EditSpanSupport.DescribeBand[span.start.node, span.end.node ! EditSpanSupport.BadBand => { span _ nullSpan; GOTO ErrorReturn } ]; IF nesting+change > 1 THEN GOTO ErrorReturn; -- cannot do it SELECT EditSpanSupport.NeedNestingChange[before, after, top, bottom, nesting+change, depth] FROM needUnNest => { afterNode: Node = EditSpanSupport.LastOfSlice[after]; span: Span = TextNode.MakeNodeSpan[afterNode, TextNode.LastWithin[afterNode]]; FreeSlices[]; [] _ UnNest[root, span, event]; LOOP }; needNest => { span: Span = TextNode.MakeNodeSpan[top[depth], TextNode.LastWithin[bottom[depth]]]; IF EditSpanSupport.SliceLength[top] = depth+1 THEN GOTO ErrorReturn; FreeSlices[]; [] _ Nest[root, span, event]; LOOP }; ENDCASE => { -- do it notify: REF NodeNesting Change; notify _ NEW[NodeNesting Change _ [NodeNesting[ root, span.start.node, span.end.node, change]]]; EditNotify.Notify[notify, before]; EditSpanSupport.DeletePrefix[top, depth]; EditSpanSupport.DeletePrefix[bottom, depth]; EditSpanSupport.ReplaceBand[before, after, top, bottom, nesting+change, event]; FreeSlices[]; EditNotify.Notify[notify, after]; UndoEvent.Note[event, UndoChangeNesting, notify] }; EXIT; ENDLOOP; RETURN [span]; EXITS ErrorReturn => { FreeSlices[]; [span, ] _ EditSpanSupport.UndoSplits[span, nullSpan, event]; ERROR CannotDoEdit } } }; UndoChangeNesting: PROC [undoRef: REF, currentEvent: Event] = { saved: REF Change _ NARROW[undoRef]; WITH saved SELECT FROM x: REF Change.NodeNesting => { [] _ ChangeNesting[x.root, TextNode.MakeNodeSpan[x.first, x.last], -x.change, currentEvent] }; ENDCASE => ERROR }; Insert: PUBLIC PROC [root, old: Node, where: Place _ after, event: Event _ NIL] RETURNS [new: Node] = { IF old=NIL THEN RETURN [NIL]; new _ TextNode.NewTextNode[]; Inherit[old, new]; DoInsertNode[root, old, new, where, event] }; InsertTextNode: PUBLIC PROC [root, old: Node, where: Place _ after, inherit: BOOLEAN _ FALSE, event: Event _ NIL] RETURNS [new: Node] = { IF old=NIL THEN RETURN [NIL]; new _ TextNode.NewTextNode[]; IF inherit THEN Inherit[old, new]; DoInsertNode[root, old, new, where, event] }; Inherit: PUBLIC PROC [old, new: Node, allprops: BOOL _ FALSE] = { CopyProp: PROC [name: ATOM, value: REF] RETURNS [BOOL] = { IF allprops OR NodeProps.Is[name, $Inheritable] THEN { newvalue: REF ~ NodeProps.CopyInfo[name, value]; IF newvalue # NIL THEN NodeProps.PutProp[new, name, newvalue]; }; RETURN [FALSE]; }; new.formatName _ old.formatName; IF allprops OR old.hasprefix OR old.haspostfix OR old.hasstyledef THEN [] _ NodeProps.MapProps[old, CopyProp, FALSE, FALSE]; IF new # NIL AND old # NIL THEN new.comment _ old.comment; }; DoInsertNode: PROC [root, old, new: Node, where: Place, event: Event] = { dest, parent: Node; child: BOOLEAN; notify: REF InsertingNode Change; IF new = NIL OR old = NIL THEN RETURN; parent _ IF where = child THEN old ELSE TextNode.Parent[old]; SELECT where FROM sibling => { dest _ TextNode.LastWithin[old]; child _ FALSE }; after => { dest _ old; child _ FALSE }; child => { dest _ old; child _ TRUE }; ENDCASE => { IF parent.child = old THEN { dest _ parent; child _ TRUE } ELSE { dest _ TextNode.LastWithin[TextNode.Previous[old, parent]]; child _ FALSE } }; notify _ NEW[InsertingNode Change _ [InsertingNode[root, new, dest]]]; EditNotify.Notify[notify, before]; SELECT TRUE FROM child => { -- insert as first child of dest IF dest.child # NIL THEN { new.next _ dest.child; new.last _ FALSE } ELSE { new.next _ dest; new.last _ TRUE }; dest.child _ new }; where = sibling => { -- insert as next sibling of old; don't adopt children new.next _ old.next; new.last _ old.last; old.last _ FALSE; old.next _ new }; ENDCASE => { -- insert as next sibling of dest new.next _ dest.next; new.last _ dest.last; dest.last _ FALSE; dest.next _ new; IF where = after AND (new.child _ dest.child) # NIL THEN { -- adopt dest's children TextNode.LastSibling[new.child].next _ new; dest.child _ NIL } }; EditNotify.Notify[notify, after]; UndoEvent.Note[event, UndoInsertNode, notify] }; UndoInsertNode: PROC [undoRef: REF, currentEvent: Event] = { saved: REF Change _ NARROW[undoRef]; WITH saved SELECT FROM x: REF Change.InsertingNode => { [] _ Delete[x.root, TextNode.MakeNodeSpan[x.new, x.new], currentEvent] }; ENDCASE => ERROR }; Split: PUBLIC PROC [root: Node, loc: Location, event: Event _ NIL] RETURNS [new: Node] = { old: Node = loc.node; IF old=NIL THEN RETURN [NIL]; new _ Insert[root, old, after, event]; IF loc.where # EditSpanSupport.NodeItself AND old # NIL THEN { [] _ TextEdit.MoveText[destRoot: root, sourceRoot: root, dest: new, destLoc: 0, source: old, start: loc.where, event: event] } }; Merge: PUBLIC PROC [root, node: Node, event: Event _ NIL] RETURNS [loc: Location] = { pred: Node _ TextNode.StepBackward[node]; start: INT; IF pred = NIL OR TextNode.Parent[pred] = NIL OR node = NIL THEN ERROR CannotDoEdit; [start, ] _ TextEdit.CopyText[root, root, pred, INT.LAST, node, 0, INT.LAST, event]; [] _ Delete[root, TextNode.MakeNodeSpan[node, node], event]; RETURN [[pred, start]] }; ChangeLooks: PUBLIC PROC [root: Node, span: Span, remove, add: Looks, event: Event _ NIL] = { DoChange: PROC [node: Node, start, len: INT] RETURNS [stop: BOOL _ FALSE] = { TextEdit.ChangeLooks[root, node, remove, add, start, len, event]; }; EditSpanSupport.Apply[span, DoChange] }; ChangeCaps: PUBLIC PROC [root: Node, span: Span, how: TextEdit.CapChange _ allCaps, event: Event _ NIL] = { DoChange: PROC [node: Node, start, len: INT] RETURNS [stop: BOOL _ FALSE] = { TextEdit.ChangeCaps[root, node, start, len, how, event]; }; IF CheckForNil[span] THEN RETURN; EditSpanSupport.Apply[span, DoChange] }; END. –EditSpanImpl.mesa Copyright Σ 1985, 1986, 1987, 1988 by Xerox Corporation. All rights reserved. written by Bill Paxton, June 1981 last edit by Bill Paxton, December 23, 1982 2:47 pm Last Edited by: Maxwell, January 5, 1983 12:46 pm Last Edited by: Paul Rovner, August 10, 1983 4:25 pm Michael Plass, October 15, 1987 3:41:49 pm PDT Doug Wyatt, February 17, 1988 12:17:11 pm PST Editing operations on spans replace dest span by copy of source span result is the new copy of source result is the new copy of source dest cannot be within source result is moved span pure text move split source and dest, if necessary, so can deal with entire nodes check for dest already in correct position check for dest inside source dest inside source newAlpha is new location of alpha span; ditto for newBeta now check for alpha beta adjacent or overlapping as special cases move beta nodes to before alpha nodes move alpha nodes to before beta nodes check for overlap like Replace, but moves source instead of copying it result is moved span get slices for dest and source check for overlap Nesting moves span to a deeper nesting level in tree New nodes; split & merge empty copy of old node is inserted in tree in position determined by "where" empty text node is inserted in tree inserts copy of loc.node is inserted directly after loc.node (as sibling) new adopts children of old (if any) if loc.where # NodeItself and loc.node is a text node, then text after loc.where moves to new node text before loc.where stays in old node returns the new node copies text of node to end of previous node then deletes node returns location of join in the merged node Change looks Caps and Lowercase Κά˜codešœ™KšœN™NKšœ!™!Kšœ3™3Kšœ1™1Kšœ4™4Kšœ.™.Kšœ-™-K™—šΟk ˜ Kšœ œ˜#Kšœ œn˜|Kšœœš˜―Kšœ œ$˜3Kšœ œ›˜©Kšœ œ~˜ŒKšœ œ˜—K˜šΠln œœœ˜KšœP˜WKšœ ˜Kšœœœ ˜Kšœœ˜!KšΟn œœœœ˜"Kšœ œ ˜Kšœ œ ˜K™—headšœ™š Ÿ œœœœœ˜;Kšœœœœ˜6K˜—K˜š Ÿœœœ@œœœ˜wKšœ˜Kšœ(™(Kšœ ™ K˜šœœΟc˜6Kšœ^˜dK˜—K˜*K˜.Kšœœ˜K˜IKšœœ"˜6K˜—K˜š Ÿ œœœœœ  œ˜Ošœ˜Kšœ ˜#Kšœ.˜1Kšœ,˜/Kšœ$˜'Kšœ0˜3Kšœ-˜-Kšœ˜—K˜—K˜š Ÿ œœ@œ0œœ  œ˜·K˜!K˜%Kšœ œ˜"Kšœ œ"˜.Kšœ œ˜&Kšœ œ&˜4Kšœœ˜Kšœœ7˜K˜:K˜I—KšœB˜HK˜—K˜šŸ œœœœ˜>Kšœœœ˜!Kšœ!˜&šœ.œ-œ  ˜qK˜Kšœœ˜Kšœœ%˜-K˜*K˜—Kšœ9˜=K˜—K˜š Ÿœœœ(œœœ˜aKšœœœ˜ Kšœ˜$šœ-œ,œ  ˜oK˜Kšœœ˜Kšœœ#˜+Kšœœ+˜?K˜3K˜—šœ˜K˜(Kšœœ˜0K˜—K˜—K˜Kšœœ˜'š Ÿœœœœ  œ˜[Kšœ œ œ œ&˜PK˜9K˜—K˜šŸœœœ  œ˜JK˜Kšœœ=˜HK˜—K˜šŸ œœœ  œ˜:Kšœ4œ˜8K˜—K˜šŸ œœ œ!˜AKšœ$œ ˜4K˜—K˜šŸ œœœœ˜3Kšœ œ˜Kšœ œœœ ˜)šœ œ˜Kšœœ œ ˜6Kšœœ˜—K˜—K˜š Ÿ œœ(œœ  œ˜hKšœœ.˜>K˜—K˜š Ÿœœœ[œœœ˜§Kšœ ™ Kš œœ œœœ ˜?Kšœ#œ ˜;KšœN˜TK˜*K˜.Kšœœ˜K˜IK˜—K˜š Ÿœœ œœœ  œ˜cšœ*˜3Kšœ$˜'Kšœ0œ0˜c—K˜—K˜šŸ œœkœ˜—K˜K˜%Kšœ œ˜Kšœ œ˜&Kšœ œ&˜4Kšœœ˜K˜xKšœB˜HK˜—K˜š Ÿœœœ[œœœ˜§Kšœ™Kšœ™K˜GKšœœ˜Kšœœ˜ Kšœ! +˜LK˜šŸ œœ˜K˜#K˜"K˜ K˜$K˜#K˜"Kšœ7œ˜;K˜—šŸ œœ˜(K˜NK˜ K˜3K˜—šŸ œœ˜K˜Wšœ.œ ˜UKšœœœ˜Kšœ ˜1K˜ Kšœ˜K˜—K˜ K˜7K˜—Kšœœœ ˜.šœ#œ˜+Kšœ™KšœN˜TKšœ˜—šœ˜Kšœ0˜0šœ ˜ K˜/K˜K˜HK˜-Kšœ˜—Kšœœ˜—KšœB™BK˜`K˜8K˜4K˜šœ  ˜Kšœ*™*šœœœœœœœ!˜½šœ 4˜;šœ ˜šœ˜šœœœ˜ K˜?Kš˜—Kšœ˜—šœ˜šœœœ˜ K˜AKš˜—Kšœ˜—Kšœœ˜—K˜—šœ˜šœ V˜YKšœ™˜2˜CKšœ0œ˜E——šœœ ˜0K˜LK˜$Kšœœ <˜UK˜K˜—K˜KKšœ;œ<œœ ˜’Kšœ™šœœ ˜AK˜GK˜ K˜*Kšœ˜K˜—K˜šœU˜_KšœCœ˜JKšœœ˜#Kšœ˜—šœM˜Ošœ˜Kšœ3œ˜8K˜—šœ ˜Kšœœ˜šœ œ#˜/K˜EK˜D—K˜"K˜ZK˜MK˜(K˜ K˜!K˜-K˜——Kšœ˜Kš˜—Kšœ˜——šœ(˜*šœ ˜Kšœ œœ˜šœ1œ +˜eKšœœ˜ K˜QK˜—šœ/œ (˜`Kšœœ˜ KšœOœ˜UK˜—šœœœ (˜@K˜AK˜—K˜—šœœ1œ/œ˜pK˜AK˜——Kšœ ˜0Kš œœ œœœ  ˜ZKšœ ˜šœ˜K˜ K˜>Kšœ˜K˜—K˜—K˜—K˜šŸ œœ œ˜;Kšœœ œ ˜$šœœ˜šœœ˜˜$K˜GK˜ —K˜—Kšœœ˜—K˜—K˜šŸ œœœ˜3Kšœ"œ˜&Kšœ˜%Kšœ9™9K˜VKšœœ˜Kšœœ˜Kšœ6 !˜WK˜&šŸ œœ˜K˜ŒK˜‹KšœHœ˜LK˜—šœ  ˜ Kšœœœœ˜Lšœœ ˜7K˜#K˜!Kšœ œ˜$Kšœ œ$˜1Kšœ œ˜"Kšœ œ"˜.KšœBœ˜F˜\K˜[—K˜YK˜WKšœ˜K˜—Kšœ>  ˜^K˜6K˜2K˜ K˜4K˜0K˜KšœA™Ašœœ ˜@Kšœ%™%K˜wK˜—šœœœ ˜EKšœ%™%K˜wK˜—šœ <˜DKšœ œ˜Kšœ 9˜KK˜ ˜4K˜[Kšœœ˜/—˜4K˜XKšœœ˜/—Kšœ™˜-K˜L—šœ ˜ šœ ˜K˜ šœœ˜Kšœœœ˜,šœ  ˜5˜K˜X—šœ˜šœ ˜#K˜.K˜—šœ ˜#K˜/K˜——Kšœ˜—šœ ˜2K˜tšœ˜šœ  ˜'K˜/K˜—šœ  ˜'K˜.K˜——Kšœ˜—Kšœœ ˜<šœ ˜&˜$K˜G—šœœ  ˜KšœA ˜QK˜?K˜ZK˜[K˜+K˜——K˜—šœ0œ/œ ˜|K˜NK˜MK˜—šœ.œ-œ ˜vKšœLœ˜RKšœKœ˜QK˜3K˜—Kš œœœœœ ˜eKš œœœœœ˜NKšœ˜šœ˜K˜MKšœ˜K˜—K˜—K˜—K˜šŸœœœ@œœœœ˜“Kšœ4™4Kšœ™Kšœ œ˜Kšœ 9˜TK˜ K˜VKšœœ˜Kšœœ˜šŸ œœœ  œ˜)K˜ŒK˜‹KšœHœ˜LK˜—šœ  ˜ Kšœœœœ ˜Cšœœ ˜3šœ@˜FK˜—K˜—K˜?˜4K˜YKšœœ˜0—˜4K˜\Kšœœ˜0—Kšœ™˜-K˜M—K˜ Kšœ™šœ œ 0˜Bšœœ˜Kšœœœ ˜8šœ ˜1šœ˜Kšœœ ˜5Kšœ˜—Kšœ˜—šœ ˜,šœ˜Kšœ $˜?Kšœœ !œ˜=—Kšœ˜—šœ ˜'šœœ˜šœœ˜)K˜8Kšœ˜—Kšœ)˜)Kšœ*˜*Kšœœ ˜—Kšœ˜——K˜—K˜AK˜0K˜.K˜IKšœœ"˜6Kšœ˜šœ˜KšœBœ˜UK˜—K˜—K˜—K˜—šœ™š Ÿ œœœ"œœ˜XKšœ˜Kšœ,™,K˜2Kšœ œ˜Kšœœ˜ šŸ œœ˜K˜ˆKšœ œ˜$K˜—šœ  ˜ Kšœœœ ˜,Kšœ<  ˜\šœ 3˜6˜.K˜XKšœœ˜(—Kšœœœ ˜<šœV˜`˜K˜5K˜NK˜ K˜Kšœ˜K˜—˜ K˜SKšœ,œœ ˜DK˜ K˜Kšœ˜K˜—Kšœ ˜Kšœœ˜šœ œ#˜/K˜0—K˜"K˜)K˜,K˜OK˜ K˜!K˜1K˜—Kšœ˜Kšœ˜—Kšœ˜šœ˜KšœLœ˜_K˜—K˜—K˜—K˜šŸœœ œ˜?Kšœœ œ ˜$šœœ˜šœœ˜K˜\K˜—Kšœœ˜—K˜—K˜—šœ™š Ÿœœœ8œœ˜gKšœL™LKš œœœœœ˜K˜K˜K˜+K˜—K˜šŸœœœ2œœœœ˜‰Kšœ#™#Kš œœœœœ˜K˜Kšœ œ˜"K˜+K˜—K˜š Ÿœœœœœ˜Aš Ÿœœœ œœœ˜:šœ œ"œ˜6Kšœ œ#˜0Kšœ œœ(˜>Kšœ˜—Kšœœ˜K˜—K˜ Kš œ œœœ˜FKšœ'œœ˜5Kš œœœœœ˜:K˜—K˜šŸ œœ7˜IK˜Kšœœ˜Kšœœ˜!Kš œœœœœœ˜&Kšœ œœœ˜=šœ˜Kšœ6œ˜>Kšœœ˜'Kšœœ˜&šœ˜ šœ˜Kšœœ˜$KšœGœ˜R—Kšœ˜——Kšœ œ:˜FK˜"šœœ˜šœ   ˜+šœ˜Kšœ%œ˜0Kšœœ˜*—K˜Kšœ˜—šœ 6˜KK˜)Kšœ œ˜!Kšœ˜—šœ !˜.K˜+Kšœ œ˜#š œœœœ ˜SK˜+Kšœ œ˜K˜—Kšœ˜——K˜!K˜.K˜—K˜šŸœœ œ˜˜PKšœ-˜-—K˜—K˜—K˜š Ÿœœœ#œœ˜UKšœ+™+Kšœ™Kšœ+™+K˜)Kšœœ˜ Kšœœœœœœœœ˜SKš œ0œœ œœ ˜TKšœ<˜