DIRECTORY EditNotify USING [Change, Notify], EditSpan USING [Nest, UnNest], EditSpanSupport USING [AdoptChildren, Apply, BadBand, CompareSliceOrder, CopySpan, CreateDest, DeletePrefix, DescribeBand, DestSlices, DoSplits, DoSplits2, FreeSlice, LastOfSlice, NeedNestingChange, NodeLoc, NodeSpan, ReMerge, ReplaceBand, Slice, SliceLength, SliceOrder, Splice, UndoSplits, UndoSplits2], NodeProps USING [CopyInfo, Is, MapProps, PutProp], TextEdit USING [ChangeCaps, ChangeLooks, CopyText, DeleteText, DestSpanProc, MoveText, MoveTextOnto, ReplaceText, TransposeProc, TransposeText, TwoSpanProc], TextNode USING [LastWithin, Level, MakeNodeLoc, MakeNodeSpan, NewTextNode, Parent, Previous, Root, StepBackward, StepForward], Tioga USING [CapChange, Location, Looks, Node, NodeItself, nullLocation, nullSpan, Order, Place, Span], UndoEvent USING [Event, EventRep, Note]; EditSpanImpl: CEDAR PROGRAM IMPORTS EditSpan, EditSpanSupport, EditNotify, NodeProps, TextEdit, TextNode, UndoEvent EXPORTS EditSpan, Tioga = BEGIN OPEN EditSpan, Tioga; Event: TYPE ~ UndoEvent.Event; EventRep: PUBLIC TYPE ~ UndoEvent.EventRep; 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] = { RETURN [ dest.start.node = dest.end.node AND dest.start.where # NodeItself AND dest.end.where # NodeItself AND source.start.node = source.end.node AND source.start.where # NodeItself AND source.end.where # NodeItself ] }; TwoSpanText: PROC [destRoot, sourceRoot: Node, dest, source: Span, saveForPaste: BOOLEAN, textProc: TextEdit.TwoSpanProc, event: Event] RETURNS [result: Span] = { 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 # NodeItself AND span.end.where # 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 # NodeItself AND del.end.where # 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 ¬ NIL; SaveTextForPaste: PROC [node: Node, start, len: INT, event: Event] = { 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] = { SaveOldPaste[event]; paste ¬ NEW[Change.ChangingSpanForPaste ¬ [ChangingSpanForPaste[span]]]; }; SaveOldPaste: PROC [event: Event] = { 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.ChangingSpanForPaste ~ paste; RETURN[IF savedPaste=NIL THEN nullSpan ELSE savedPaste.span]; }; MoveToLimbo: PROC [root: Node, span: Span, event: Event] RETURNS [result: Span] = { 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] = { RETURN [dest.where # NodeItself AND source.start.node = source.end.node AND source.start.where # NodeItself AND source.end.where # 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, 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[ dest: dest.node, first: source.start.node, last: source.end.node, pred: EditSpanSupport.LastOfSlice[sBefore], nesting: sNesting, afterDest: (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 # NodeItself THEN { -- undo prior splits start, end: BOOLEAN ¬ FALSE; IF source.start.where # 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 # 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 # NodeItself AND source.end.where # NodeItself THEN { afterLoc ¬ Merge[TextNode.Root[afterSource], afterSource, event] }; afterMoved2 ¬ afterMoved1; -- save previous hint IF afterSource=NIL THEN afterMoved1 ¬ [beforeSource, 0] ELSE afterMoved1 ¬ afterLoc; -- hint for repaint RETURN [source]; EXITS ErrorReturn => { FreeSlices[]; [, source] ¬ EditSpanSupport.UndoSplits2[dest, source, event]; ERROR CannotDoEdit } } }; UndoMoveNodes: PROC [undoRef: REF Change, currentEvent: Event] = { WITH undoRef SELECT FROM x: REF Change.MovingNodes => { [] ¬ Move[destRoot: TextNode.Root[x.pred], sourceRoot: TextNode.Root[x.first], dest: TextNode.MakeNodeLoc[x.pred], source: TextNode.MakeNodeSpan[x.first, x.last], where: after, nesting: x.nesting, event: 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: Order; [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 # NodeItself AND beta.start.where # 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 # NodeItself AND beta.end.where # 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 }; IF afterAlphaLoc.node=NIL THEN afterMoved1 ¬ [beforeAlpha, 0] ELSE afterMoved1 ¬ afterAlphaLoc; -- hint for repaint IF afterBetaLoc.node=NIL THEN afterMoved2 ¬ [beforeBeta, 0] ELSE afterMoved2 ¬ 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: Order; aBefore, aAfter, aTop, aBottom, bBefore, bAfter, bTop, bBottom: EditSpanSupport.Slice; aNesting, bNesting: INTEGER; aDepth, bDepth: NAT; 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[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[ first: span.start.node, last: span.end.node, change: 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 Change, currentEvent: Event] = { WITH undoRef SELECT FROM x: REF Change.NodeNesting => [] ¬ ChangeNesting[root: TextNode.Root[x.first], span: TextNode.MakeNodeSpan[x.first, x.last], change: -x.change, event: 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.format ¬ old.format; new.comment ¬ old.comment; IF allprops OR old.hasPrefix OR old.hasPostfix OR old.hasStyleDef THEN [] ¬ NodeProps.MapProps[old, CopyProp, FALSE, FALSE]; }; DoInsertNode: PROC [root, old, new: Node, where: Place, event: Event] = { dest, parent: Node; child: BOOLEAN ¬ FALSE; 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[new: new, dest: dest]]]; EditNotify.Notify[notify, before]; SELECT TRUE FROM child => { -- insert as first child of dest new.next ¬ dest.child; new.parent ¬ dest; dest.child ¬ new; }; where = sibling => { -- insert as next sibling of old; don't adopt children new.next ¬ old.next; new.parent ¬ old.parent; old.next ¬ new; }; ENDCASE => { -- insert as next sibling of dest new.next ¬ dest.next; new.parent ¬ dest.parent; dest.next ¬ new; IF where=after THEN { EditSpanSupport.AdoptChildren[node: new, child: dest.child]; dest.child ¬ NIL; }; }; EditNotify.Notify[notify, after]; UndoEvent.Note[event, UndoInsertNode, notify] }; UndoInsertNode: PROC [undoRef: REF Change, currentEvent: Event] = { WITH undoRef SELECT FROM x: REF Change.InsertingNode => [] ¬ Delete[root: TextNode.Root[x.new], del: TextNode.MakeNodeSpan[x.new, x.new], event: 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 # 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: 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, 1991, 1992 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 Bier, January 6, 1989 2:23:06 pm PST Michael Plass, October 15, 1987 3:41:49 pm PDT Doug Wyatt, February 26, 1992 12:48 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 afterMoved1 _ IF afterSource=NIL THEN [beforeSource, 0] ELSE afterLoc; -- hint for repaint 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 afterMoved1 _ IF afterAlphaLoc.node=NIL THEN [beforeAlpha, 0] ELSE afterAlphaLoc; -- hint for repaint, comment out, Bier, January 6, 1989 afterMoved2 _ IF afterBetaLoc.node=NIL THEN [beforeBeta, 0] ELSE afterBetaLoc; 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 Κβ–(cedarcode) style•NewlineDelimiter ™codešœ™Kšœ ΟeœO™ZKšœ!™!Kšœ3™3Kšœ1™1Kšœ4™4K™$Kšœ.™.Kšœ*™*K™—šΟk ˜ Kšœ žœ˜#Kšœ žœ˜Kšœžœœ˜±Kšœ žœ$˜3Kšœ žœ˜žKšœ žœq˜Kšœžœ\˜gKšœ žœ˜(—K˜šΟn œžœžœ˜KšžœP˜WKšžœ˜Kšœžœžœ˜K˜Kšœžœ˜Kšœ žœžœ˜+Kšœžœ˜!K˜KšŸ œžœžœžœ˜"Kšœ žœ ˜Kšœ žœ ˜K™—headšœ™š Ÿ œžœžœžœžœ˜;Kšžœžœžœžœ˜6K˜—K˜š Ÿœžœžœ@žœžœžœ˜wKšžœ˜Kšœ(™(Kšœ ™ K˜šžœžœΟc˜6Kšžœ^˜dK˜—K˜*K˜.Kšœžœ˜K˜IKšžœžœ"˜6K˜—K˜šŸ œžœžœžœ˜:šžœ˜Kšœ ž˜#Kšœž˜!Kšœž˜Kšœ$ž˜'Kšœ ž˜#Kšœ˜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˜—šŸœžœžœ˜FKšœ žœ žœ žœ&˜PK˜9K˜—K˜šŸœžœ˜5K˜Kšœžœ=˜HK˜—K˜šŸ œžœ˜%Kšœ4žœ˜8K˜—K˜šŸ œžœ žœ!˜AKšœ$žœ ˜4K˜—K˜šŸ œžœžœžœ˜3Kšœ žœ%˜4Kš žœžœ žœžœ žœ˜=K˜—K˜šŸ œžœ(žœ˜SKšžœžœ.˜>K˜—K˜š Ÿœžœžœ[žœžœžœ˜§Kšœ ™ Kš žœžœ žœžœžœ ˜?Kšžœ#žœ ˜;KšžœN˜TK˜*K˜.Kšœžœ˜K˜IK˜—K˜šŸœžœ žœžœ˜Nšžœž˜#Kšœ$ž˜'Kšœ žœ ˜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˜BKšœ,˜,Kšœ2˜2—K˜"K˜ZK˜MK˜(K˜ K˜!K˜-K˜——Kšžœ˜Kšž˜—Kšœ˜——šžœ˜šžœ ˜Kšœ žœžœ˜šžœ!žœ +˜UKšœžœ˜ K˜QK˜—šžœžœ (˜PKšœžœ˜ KšœOžœ˜UK˜—šžœžœžœ (˜@K˜AK˜—K˜—šžœžœ!žœžœ˜PK˜AK˜——Kšœ ˜0Kš œžœ žœžœžœ  ™ZKš žœ žœžœ!žœ ˜hKšžœ ˜šžœ˜K˜ K˜>Kšžœ˜K˜—K˜—K˜—K˜šŸ œžœ žœ!˜Bšžœ žœž˜šœžœ˜šœO˜OKšœT˜TKšœ7˜7—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˜—šžœ žœžœ ˜\K˜NK˜MK˜—šžœžœžœ ˜VKšœLžœ˜RKšœKžœ˜QK˜3K˜—Kš œžœžœžœžœ 7™‰Kš žœžœžœ žœ ˜sKš œžœžœžœžœ™NKšžœžœžœžœ˜\Kšžœ˜šžœ˜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šœ5˜5K˜NK˜ K˜Kšžœ˜K˜—˜ K˜SKšžœ,žœžœ ˜DK˜ K˜Kšžœ˜K˜—Kšžœ ˜Kšœžœ˜šœ žœ#˜/Kšœ?˜?—K˜"K˜)K˜,K˜OK˜ K˜!K˜1K˜—Kšžœ˜Kšžœ˜—Kšžœ˜šžœ˜KšœLžœ˜_K˜—K˜—K˜—K˜šŸœžœ žœ!˜Fšžœ žœž˜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šœ'žœžœ˜5—K˜—K˜šŸ œžœ7˜IKšœ˜Kšœžœžœ˜Kšœžœ˜!Kš žœžœžœžœžœžœ˜&Kšœ žœžœžœ˜=šžœž˜Kšœ6žœ˜>Kšœžœ˜'Kšœžœ˜&šžœ˜ šžœ˜Kšžœžœ˜$KšžœGžœ˜R—Kšœ˜——Kšœ žœ?˜KK˜"šžœžœž˜šœ   ˜+K˜K˜K˜Kšœ˜—šœ 6˜KK˜K˜K˜Kšœ˜—šžœ !˜.K˜K˜K˜šžœ žœ˜Kšœ<˜