DIRECTORY EditGroup, EditSpan, EditSpanSupport, EditNotify, Rope, UndoEvent, TreeSlice, TiogaLooks, TiogaNode, TiogaNodeOps, TiogaTreeOps; EditGroupImpl: CEDAR PROGRAM IMPORTS EditSpan, EditSpanSupport, TiogaNodeOps, TiogaTreeOps, UndoEvent, EditNotify EXPORTS EditGroup SHARES TiogaNode = BEGIN OPEN EditGroup; Change: TYPE = EditNotify.Change; CannotDoEdit: PUBLIC ERROR = CODE; DeleteGroup: PUBLIC PROC [ root: RefBranchNode, start, end: Ref, saveForPaste: BOOLEAN _ TRUE, event: Event _ NIL] = { MoveGroup[NIL, root, NIL, FALSE, start, end, event]; -- move to limbo IF saveForPaste THEN EditSpan.RecordGroupForPaste[start, end, event] }; MakeCopyOfGroup: PROC [start, end: Ref] RETURNS [copyRoot: RefBranchNode, copyStart, copyEnd: Ref] = { span: TreeSpan; count: NAT _ 0; IF start=NIL OR end=NIL THEN RETURN; FOR n: Ref _ start, TiogaTreeOps.Next[n] UNTIL n=end DO count _ count+1; ENDLOOP; span.start _ [start, TiogaNode.NodeItself]; span.end _ [TiogaTreeOps.LastWithin[end], TiogaNode.NodeItself]; span _ EditSpanSupport.CopySpan[span]; copyStart _ copyEnd _ span.start.node; THROUGH [0..count) DO copyEnd _ TiogaTreeOps.Next[copyEnd]; ENDLOOP; copyRoot _ TiogaTreeOps.Root[copyStart]; copyRoot.deleted _ TRUE }; ReplaceGroup: PUBLIC PROC [ destRoot, sourceRoot: RefBranchNode, destStart, destEnd: Ref, sourceStart, sourceEnd: Ref, saveForPaste: BOOLEAN _ TRUE, event: Event _ NIL] = { IF ~IsGroup[destStart, destEnd] THEN ERROR CannotDoEdit; IF ~IsGroup[sourceStart, sourceEnd] THEN ERROR CannotDoEdit; [sourceRoot, sourceStart, sourceEnd] _ MakeCopyOfGroup[sourceStart, sourceEnd]; [] _ TransposeGroups[sourceRoot, destRoot, sourceStart, sourceEnd, destStart, destEnd, event]; IF saveForPaste THEN EditSpan.RecordGroupForPaste[destStart, destEnd, event] }; CopyGroup: PUBLIC PROC [ destRoot, sourceRoot: RefBranchNode, dest: Ref, contents: BOOL, sourceStart, sourceEnd: Ref, event: Event _ NIL] = { IF ~IsGroup[sourceStart, sourceEnd] THEN ERROR CannotDoEdit; [sourceRoot, sourceStart, sourceEnd] _ MakeCopyOfGroup[sourceStart, sourceEnd]; MoveGroup[destRoot, sourceRoot, dest, contents, sourceStart, sourceEnd, event] }; MoveGroup: PUBLIC PROC [ destRoot, sourceRoot: RefBranchNode, dest: Ref, contents: BOOL, sourceStart, sourceEnd: Ref, event: Event _ NIL] = { sourceParent, beforeSource, afterSource: Ref; notify: REF MovingGroup Change; IF ~IsGroup[sourceStart, sourceEnd] THEN ERROR CannotDoEdit; sourceParent _ TiogaTreeOps.Parent[sourceEnd]; IF (beforeSource _ TiogaTreeOps.Previous[sourceStart, sourceParent])=NIL THEN beforeSource _ sourceParent; afterSource _ TiogaTreeOps.Next[sourceEnd]; IF contents THEN { IF TiogaTreeOps.Contents[dest]=sourceStart THEN RETURN } -- already in position ELSE IF dest=beforeSource THEN RETURN; -- already in position IF dest # NIL THEN { -- check for dest inside source IF IsEmbeddedInside[dest, sourceStart, sourceEnd] THEN ERROR CannotDoEdit; IF IsInSiblings[dest, sourceStart, sourceEnd] THEN ERROR CannotDoEdit }; IF ~contents AND (TiogaNodeOps.IsBranch[dest]#TiogaNodeOps.IsBranch[sourceStart]) THEN ERROR CannotDoEdit; -- cannot mix branch and nonbranch IF contents AND TiogaNodeOps.IsBranch[dest] AND TiogaNodeOps.IsBasic[sourceStart] THEN ERROR CannotDoEdit; IF contents AND (TiogaNodeOps.IsBasic[dest] OR TiogaNodeOps.IsText[dest]) THEN ERROR CannotDoEdit; IF dest=NIL THEN { -- moving to limbo dest _ TiogaNodeOps.NewListNode[TiogaNode.invalidItemClassID]; dest.deleted _ contents _ TRUE }; notify _ NEW[MovingGroup Change _ [MovingGroup[ destRoot,sourceRoot,dest,sourceStart,sourceEnd,beforeSource,(beforeSource=sourceParent)]]]; IF beforeSource=NIL THEN { notify.from _ sourceParent; notify.contents _ TRUE }; EditNotify.Notify[notify, before]; IF beforeSource=sourceParent THEN { -- moving the initial part of the contents/children list WITH sourceParent SELECT FROM br: RefBranchNode => { IF sourceStart=br.child THEN -- moving children rather than contents IF sourceEnd.last THEN br.child _ NIL -- moving all the children ELSE { -- leaving some of the children nxt: RefBranchNode = TiogaNodeOps.NarrowToBranchNode[sourceEnd.next]; IF nxt=NIL THEN ERROR; br.child _ nxt } ELSE IF sourceStart=br.contents THEN -- moving contents rather than children IF sourceEnd.last THEN br.contents _ NIL -- moving all the contents ELSE { -- leaving some of the contents nxt: TiogaNode.RefItemNode = TiogaNodeOps.NarrowToItemNode[sourceEnd.next]; IF nxt=NIL THEN ERROR; br.contents _ nxt } ELSE ERROR }; bx: TiogaNode.RefBoxNode => bx.contents _ afterSource; ls: TiogaNode.RefListNode => ls.contents _ afterSource; ENDCASE => ERROR } ELSE { -- not moving the initial part of the contents list beforeSource.next _ sourceEnd.next; beforeSource.last _ sourceEnd.last }; IF contents THEN { -- it moves to front of contents/children of dest WITH dest SELECT FROM br: RefBranchNode => { WITH sourceStart SELECT FROM br1: RefBranchNode => { -- insert as children rather than contents IF br.child=NIL THEN { sourceEnd.next _ br; sourceEnd.last _ TRUE } ELSE { sourceEnd.next _ br.child; sourceEnd.last _ FALSE }; br.child _ br1 }; itm: TiogaNode.RefItemNode => { -- insert as contents rather than as children IF br.contents=NIL THEN { sourceEnd.next _ br; sourceEnd.last _ TRUE } ELSE { sourceEnd.next _ br.contents; sourceEnd.last _ FALSE }; br.contents _ itm }; ENDCASE => ERROR }; bx: TiogaNode.RefBoxNode => { IF bx.contents=NIL THEN { sourceEnd.next _ bx; sourceEnd.last _ TRUE } ELSE { sourceEnd.next _ bx.contents; sourceEnd.last _ FALSE }; bx.contents _ sourceStart }; ls: TiogaNode.RefListNode => { IF ls.contents=NIL THEN { sourceEnd.next _ ls; sourceEnd.last _ TRUE } ELSE { sourceEnd.next _ ls.contents; sourceEnd.last _ FALSE }; ls.contents _ sourceStart }; ENDCASE => ERROR } ELSE { -- moving to after dest as sibling sourceEnd.next _ dest.next; sourceEnd.last _ dest.last; dest.next _ sourceStart; dest.last _ FALSE }; EditNotify.Notify[notify, after]; UndoEvent.Note[event, UndoMoveGroup, notify]; }; UndoMoveGroup: PROC [undoRef: REF, currentEvent: Event] = TRUSTED { saved: REF Change _ NARROW[undoRef]; WITH x:saved SELECT FROM MovingGroup => { [] _ MoveGroup[x.sourceRoot, x.destRoot, x.from, x.contents, x.sourceStart, x.sourceEnd, currentEvent] }; ENDCASE => ERROR }; MoveGroupOnto: PUBLIC PROC [ destRoot, sourceRoot: RefBranchNode, destStart, destEnd: Ref, sourceStart, sourceEnd: Ref, saveForPaste: BOOLEAN _ TRUE, event: Event _ NIL] = { overlap: BOOLEAN; headStart, headEnd, tailStart, tailEnd: Ref; startOrder, endOrder: TreeSlice.Order; IF ~IsGroup[sourceStart, sourceEnd] THEN ERROR CannotDoEdit; IF ~IsGroup[destStart, destEnd] THEN ERROR CannotDoEdit; IF IsEmbeddedInside[destEnd, sourceStart, sourceEnd] THEN ERROR CannotDoEdit; [overlap, headStart, headEnd, tailStart, tailEnd, startOrder, endOrder] _ GroupOrder[destStart, destEnd, sourceStart, sourceEnd]; IF overlap THEN { -- groups overlap. modify dest so it doesn't overlap source IF headStart = NIL AND tailStart = NIL THEN ERROR CannotDoEdit; -- source same as dest IF headStart = NIL THEN { -- source start = dest start IF endOrder=before THEN ERROR CannotDoEdit -- dest end before source end ELSE destStart _ tailStart } ELSE IF tailStart = NIL THEN { -- source end = dest end IF startOrder=before THEN destEnd _ headEnd -- dest start before source start ELSE ERROR CannotDoEdit } -- source start before d start ELSE { -- have both head and tail IF startOrder=before AND endOrder=after THEN { DeleteGroup[destRoot, tailStart, tailEnd, FALSE, event]; destEnd _ headEnd } ELSE IF startOrder=before THEN destEnd _ headEnd ELSE IF endOrder=after THEN destStart _ tailStart ELSE ERROR CannotDoEdit }}; MoveGroup[NIL, sourceRoot, NIL, FALSE, sourceStart, sourceEnd, event]; -- move source to limbo sourceRoot _ TiogaTreeOps.Root[sourceStart]; [] _ TransposeGroups[sourceRoot, destRoot, sourceStart, sourceEnd, destStart, destEnd, event]; IF saveForPaste THEN EditSpan.RecordGroupForPaste[destStart, destEnd, event] }; TransposeGroups: PUBLIC PROC [ alphaRoot, betaRoot: RefBranchNode, alphaStart, alphaEnd: Ref, betaStart, betaEnd: Ref, event: Event _ NIL] RETURNS [newAlphaStart, newAlphaEnd, newBetaStart, newBetaEnd: Ref] = { afterAlpha, afterBeta, beforeAlpha, beforeBeta, headStart, headEnd, tailStart, tailEnd: Ref; overlap: BOOL; startOrder, endOrder: TreeSlice.Order; IF ~IsGroup[betaStart, betaEnd] THEN ERROR CannotDoEdit; IF ~IsGroup[alphaStart, alphaEnd] THEN ERROR CannotDoEdit; afterAlpha _ TiogaTreeOps.Next[alphaEnd]; afterBeta _ TiogaTreeOps.Next[betaEnd]; beforeAlpha _ TiogaTreeOps.Previous[alphaStart]; beforeBeta _ TiogaTreeOps.Previous[betaStart]; IF afterAlpha=betaStart THEN { -- alpha just before beta; Move alpha after beta MoveGroup[alphaRoot, betaRoot, betaEnd, FALSE, alphaStart, alphaEnd, event] } ELSE IF afterBeta = alphaStart THEN { -- beta just before alpha; Move beta after alpha MoveGroup[alphaRoot, betaRoot, alphaEnd, FALSE, betaStart, betaEnd, event] } ELSE IF IsEmbeddedInside[alphaEnd, betaStart, betaEnd] OR IsEmbeddedInside[betaEnd, alphaStart, alphaEnd] THEN ERROR CannotDoEdit; [overlap, headStart, headEnd, tailStart, tailEnd, startOrder, endOrder] _ GroupOrder[alphaStart, alphaEnd, betaStart, betaEnd]; IF overlap THEN { -- groups overlap IF headStart = NIL AND tailStart = NIL THEN NULL -- alpha same as beta ELSE IF headStart = NIL THEN { -- move tail to before alphastart IF beforeAlpha # NIL THEN MoveGroup[alphaRoot, betaRoot, beforeAlpha, FALSE, tailStart, tailEnd, event] ELSE MoveGroup[alphaRoot, betaRoot, TiogaTreeOps.Parent[alphaEnd], TRUE, tailStart, tailEnd, event]; IF endOrder=before THEN { -- a end before b end betaStart _ tailStart; betaEnd _ alphaEnd } ELSE { -- b end before a end alphaStart _ tailStart; alphaEnd _ betaEnd }} ELSE IF tailStart = NIL THEN { --move head to after alphaend MoveGroup[alphaRoot, betaRoot, alphaEnd, FALSE, headStart, headEnd, event]; IF startOrder=before THEN { -- a start before b start alphaStart _ betaStart; alphaEnd _ headEnd } ELSE { -- b start before a start betaStart _ alphaStart; betaEnd _ headEnd }} ELSE IF startOrder # endOrder THEN ERROR CannotDoEdit -- one contained in the other ELSE { -- transpose head and tail [] _ TransposeGroups[alphaRoot, betaRoot, headStart, headEnd, tailStart, tailEnd, event]; IF startOrder=before THEN { -- a start before b start alphaStart _ betaStart; alphaEnd _ headEnd; betaStart _ tailStart; betaEnd _ alphaEnd } ELSE { -- b start before a start betaStart _ alphaStart; betaEnd _ headEnd; alphaStart _ tailStart; alphaEnd _ betaEnd }}} ELSE { -- do transpose as two moves child: BOOL _ FALSE; alphaDest: Ref; IF beforeAlpha # NIL THEN alphaDest _ beforeAlpha ELSE { child _ TRUE; alphaDest _ TiogaTreeOps.Parent[alphaEnd] }; MoveGroup[betaRoot, alphaRoot, betaEnd, FALSE, alphaStart, alphaEnd, event]; MoveGroup[alphaRoot, betaRoot, alphaDest, child, betaStart, betaEnd, event]; }; RETURN [alphaStart, alphaEnd, betaStart, betaEnd] }; IsInSiblings: PROC [node, start, end: Ref] RETURNS [BOOL] = { FOR n: Ref _ start, TiogaTreeOps.Next[n] DO -- look for prevParent inside group siblings IF n=node THEN RETURN [TRUE]; -- node is inside group IF n=end THEN EXIT; ENDLOOP; RETURN [FALSE] }; IsGroup: PROC [start, end: Ref] RETURNS [BOOL] = INLINE { RETURN [IsInSiblings[end, start, NIL]] }; IsEmbeddedInside: PROC [node, start, end: Ref] RETURNS [BOOL] = { groupParent: Ref = TiogaTreeOps.Parent[end]; nodeParent: Ref _ TiogaTreeOps.Parent[node]; prevParent: Ref; firstParent: BOOL _ TRUE; UNTIL nodeParent=NIL DO IF nodeParent=groupParent THEN { -- prevParent is a sibling of group IF firstParent THEN EXIT; -- node is sibling of group IF IsInSiblings[prevParent, start, end] THEN RETURN [TRUE]; -- node is inside group EXIT }; prevParent _ nodeParent; nodeParent _ TiogaTreeOps.Parent[nodeParent]; ENDLOOP; RETURN [FALSE] }; GroupOrder: PROC [aStart, aEnd, bStart, bEnd: Ref] RETURNS [overlap: BOOL, headStart, headEnd, tailStart, tailEnd: Ref, startOrder, endOrder: TreeSlice.Order] = { CompareOrder: PROC [x, y: Ref] RETURNS [order: TreeSlice.Order] = { FindNext: PROC [x, y: Ref] RETURNS [BOOL] = { DO -- search siblings following x for y IF x=y THEN RETURN [TRUE]; IF x=NIL THEN RETURN [FALSE]; x _ TiogaTreeOps.Next[x]; ENDLOOP }; IF x=y THEN RETURN [same]; IF FindNext[x, y] THEN RETURN [before]; IF FindNext[y, x] THEN RETURN [after]; RETURN [disjoint] }; IF (startOrder _ CompareOrder[aStart, bStart])=disjoint THEN { overlap _ FALSE; RETURN }; IF (endOrder _ CompareOrder[aEnd, bEnd])=disjoint THEN ERROR; SELECT startOrder FROM before => { headStart _ aStart; headEnd _ TiogaTreeOps.Previous[bStart] }; same => headStart _ headEnd _ NIL; after => { headStart _ bStart; headEnd _ TiogaTreeOps.Previous[aStart] }; ENDCASE => ERROR; SELECT endOrder FROM before => { tailStart _ TiogaTreeOps.Next[aEnd]; tailEnd _ bEnd }; same => tailStart _ tailEnd _ NIL; after => { tailStart _ TiogaTreeOps.Next[bEnd]; tailEnd _ aEnd }; ENDCASE => ERROR; overlap _ TRUE }; END... öEditGroupImpl.mesa; written by Bill Paxton, July 1983 edited by Bill Paxton, August 2, 1983 5:20 pm Copies the source group after the dest group and then deletes the dest group. Takes care of overlap between source and dest. If contents is true, copy the group to start of the contents list of dest. Otherwise, copy the group to immediately after dest. If contents is true, move the group to start of the contents list of dest. Otherwise, move the group to immediately after dest. First remove the source from its old location. Now insert source in its new location. Moves the source group after the dest group and then deletes the dest group. Takes care of overlap between source and dest. Transposes the alpha group and the beta group. Takes care of overlap between alpha and beta. Move alpha after beta Move beta to old location of alpha Returns true if node is nested inside group defined by start..end Returns false for case of node being sibling of start or end, since that case of overlap is ok. Ê n˜Jšœ Ïc(™5Jš-™-J˜JšÏk ˜ Jšœ ˜ J˜ J˜J˜ J˜J˜ J˜ J˜ J˜ J˜ J˜ J˜šœž œ˜JšžœM˜TJšžœ ˜Jšžœ ˜—Jšžœžœ ˜J˜Jšœžœ˜!J˜Jšœžœžœžœ˜"J˜šÏn œžœžœ˜Jšœ4žœžœžœ˜[Jšœ žœžœžœ˜EJšžœžœ3˜GJ™—šŸœžœ˜'Jšžœ7˜>Jšœ˜Jšœžœ˜Jš žœžœžœžœžœžœ˜$Jšžœ&žœžœžœ˜QJ˜+J˜@Jšœ&˜&Jšœ&˜&Jšžœ žœ'žœ˜DJšœ(˜(Jšœžœ˜J˜—šŸ œžœžœ˜Jšœ=˜=Jšœ+žœžœžœ˜RJ™MJ™.Jšžœžœžœ˜8Jšžœ"žœžœ˜˜>Jšœžœ˜!—šœ žœ#˜/Jšœ[˜[—Jšžœžœžœ1žœ˜PJ˜"J™.šžœžœ8˜\šžœžœž˜˜šžœžœ'˜DJšžœžœ žœ˜@šžœ˜&J˜EJšžœžœžœžœ˜J˜——šžœžœžœ'˜LJšžœžœžœ˜Cšžœ˜&J˜KJšžœžœžœžœ˜J˜——Jšžœžœ˜ —Jšœ6˜6Jšœ7˜7Jšžœžœ˜——šžœ3˜:Jšœ#˜#Jšœ%˜%—J™&šžœ žœ1˜Dšžœžœž˜˜šžœ žœž˜šœ*˜BJšžœ žœžœ)žœ˜CJšžœ/žœ˜;Jšœ˜—šœ -˜MJšžœ žœžœ)žœ˜FJšžœ2žœ˜>Jšœ˜—Jšžœžœ˜——šœ˜Jšžœ žœžœ)žœ˜FJšžœ2žœ˜>J˜—šœ˜Jšžœ žœžœ)žœ˜FJšžœ2žœ˜>Jšœ˜—Jšžœžœ˜——šžœ"˜)Jšœ7˜7Jšœ%žœ˜-—J˜!J˜-J˜J™—šŸ œžœ žœžœ˜CJšœžœ žœ ˜$Jšžœ žœž˜˜˜(Jšœ@˜@——Jšžœžœ˜J˜—šŸ œžœžœ˜Jšœ=˜=Jšœ+žœžœžœ˜RJ™LJ™.Jšœ žœ˜Jšœ,˜,Jšœ&˜&Jšžœ"žœžœ˜˜>Jšœ(žœ˜,Jšžœ@˜GJ™.J™-Jšœ\˜\Jšœ žœ˜Jšœ&˜&J˜Jšžœžœžœ˜8Jšžœ žœžœ˜:J˜)Jšœ'˜'J˜0Jšœ.˜.šžœžœ0˜OJšœ(žœ ˜M—šžœžœžœ0˜VJšœ)žœ˜L—šžœžœ0ž˜9Jšœ0žœžœ˜H—šœI˜IJšœ5˜5—šžœ žœ˜#Jš žœ žœžœ žœžœžœ˜Fš žœžœ žœžœ!˜@šžœžœž˜Jšœ,žœ˜M—šžœ˜#Jšœžœ˜@—šžœžœ˜/Jšœ+˜+—šžœ˜Jšœ-˜-——š žœžœ žœžœ˜