DIRECTORY TextEdit, EditNotify, UndoEvent, NodeProps, RopeEdit, RopeFrom, RopeReader, TextLooks, TextNode, NodeAddrs, Atom, Rope, CheckNode; TextEditImpl: CEDAR MONITOR IMPORTS npI:NodeProps, RopeEdit, TextLooks, EditNotify, UndoEvent, NodeAddrs, TextNode, RopeFrom, RopeReader, Rope, Atom, CheckNode EXPORTS TextEdit = BEGIN OPEN TextEdit, EditNotify; Runs: TYPE = TextLooks.Runs; scratch1, scratch2, scratch3: REF ChangingText Change; Create: PROC RETURNS [REF ChangingText Change] = { RETURN [TextNode.pZone.NEW[ChangingText Change]] }; Alloc: ENTRY PROC RETURNS [scratch: REF ChangingText Change] = { ENABLE UNWIND => NULL; IF scratch3 # NIL THEN { scratch _ scratch3; scratch3 _ NIL } ELSE IF scratch2 # NIL THEN { scratch _ scratch2; scratch2 _ NIL } ELSE IF scratch1 # NIL THEN { scratch _ scratch1; scratch1 _ NIL } ELSE scratch _ Create[] }; Free: ENTRY PROC [scratch: REF ChangingText Change] = { ENABLE UNWIND => NULL; IF scratch3 = scratch OR scratch2 = scratch OR scratch1 = scratch THEN ERROR; IF scratch3 = NIL THEN scratch3 _ scratch ELSE IF scratch2 = NIL THEN scratch2 _ scratch ELSE IF scratch1 = NIL THEN scratch1 _ scratch }; DoReplace: PROC [root: Ref, dest: RefTextNode, destStart, destLen, destSize: Offset, sourceRope: ROPE, sourceRuns: Runs, sourceStart, sourceLen: Offset, event: Event] RETURNS [resultStart, resultLen: Offset] = { destRope, replaceRope, newRope: ROPE; size: Offset; notify: REF ChangingText Change; alreadysaved: BOOLEAN; destRuns, replaceRuns, newRuns: Runs; special: BOOLEAN; size _ destSize-destLen+sourceLen; destRuns _ dest.runs; destRope _ dest.rope; notify _ IF (alreadysaved _ AlreadySaved[dest,event]) THEN Alloc[] ELSE TextNode.pZone.NEW[ChangingText Change]; notify^ _ [ChangingText[root,dest,destStart,sourceLen,destLen,destRope,destRuns]]; Notify[notify,before]; IF sourceLen > 0 THEN { replaceRope _ RopeEdit.Substr[sourceRope,sourceStart,sourceLen]; replaceRuns _ TextLooks.Substr[sourceRuns,sourceStart,sourceLen]}; special _ FALSE; IF destLen=0 THEN { -- doing an insert IF destStart=0 THEN { -- insert at start newRope _ RopeEdit.Concat[replaceRope,destRope,sourceLen,destSize]; newRuns _ TextLooks.Concat[replaceRuns,destRuns,sourceLen,destSize]; special _ TRUE } ELSE IF destStart=destSize THEN { -- insert at end newRope _ RopeEdit.Concat[destRope,replaceRope,destSize,sourceLen]; newRuns _ TextLooks.Concat[destRuns,replaceRuns,destSize,sourceLen]; special _ TRUE }} ELSE IF sourceLen=0 THEN { -- doing a delete IF destStart=0 THEN { -- delete from start newRope _ RopeEdit.Substr[destRope,destLen,size]; newRuns _ TextLooks.Substr[destRuns,destLen,size]; special _ TRUE } ELSE IF (destStart+destLen)=destSize THEN { -- delete from end newRope _ RopeEdit.Substr[destRope,0,size]; newRuns _ TextLooks.Substr[destRuns,0,size]; special _ TRUE }}; IF ~special THEN { newRope _ RopeEdit.Replace[destRope,destStart,destLen, replaceRope,destSize,sourceLen]; newRuns _ TextLooks.Replace[destRuns,destStart,destLen, replaceRuns,destSize,sourceLen] }; dest.rope _ newRope; dest.runs _ newRuns; NodeAddrs.Replace[dest,destStart,destLen,sourceLen]; BumpCount[dest]; Notify[notify,after]; IF alreadysaved THEN Free[notify] ELSE UndoEvent.Note[event,UndoChangeText,notify]; RETURN [destStart,sourceLen] }; MoveText: PUBLIC PROC [ destRoot, sourceRoot: Ref, dest: RefTextNode, destLoc: Offset _ 0, source: RefTextNode, start: Offset _ 0, len: Offset _ MaxLen, event: Event _ NIL] RETURNS [resultStart, resultLen: Offset] = { MoveAddrs: PROC [addr: REF, location: Offset] RETURNS [quit: BOOLEAN] = { IF location NOT IN [start..start+len) THEN RETURN [FALSE]; IF dest=source THEN { -- pin addr at new location new: Offset _ destLoc+location-start; IF destLoc > start THEN new _ new-len; -- moving toward end of node NodeAddrs.PutTextAddr[dest, addr, new]; NodeAddrs.PinTextAddr[dest, addr] -- so it won't be changed by Replace -- } ELSE NodeAddrs.MoveTextAddr[ -- move the addr to the new node from: source, to: dest, addr: addr, location: destLoc+location-start]; RETURN [FALSE] }; destSize, sourceSize: Offset; sourceSize _ RopeEdit.Size[source.rope]; start _ MIN[MAX[0,start],sourceSize]; len _ MIN[MAX[0,len],sourceSize-start]; destSize _ IF dest=source THEN sourceSize ELSE RopeEdit.Size[dest.rope]; destLoc _ MIN[MAX[0,destLoc],destSize]; IF source=dest AND destLoc IN [start..start+len] THEN RETURN [start,len]; IF len=0 THEN RETURN [destLoc,0]; [] _ NodeAddrs.MapTextAddrs[source, MoveAddrs]; [resultStart, resultLen] _ CopyText[destRoot, sourceRoot, dest, destLoc, source, start, len, event]; IF dest=source THEN { -- need to adjust locations IF destLoc > start THEN resultStart _ destLoc-len -- moving toward end of node ELSE start _ start+len -- moving toward start of node-- }; DeleteText[sourceRoot, source, start, len, event]; IF dest=source THEN NodeAddrs.UnpinAll[source] }; TransposeText: PUBLIC PROC [ alphaRoot, betaRoot: Ref, alpha: RefTextNode, alphaStart: Offset _ 0, alphaLen: Offset _ MaxLen, beta: RefTextNode, betaStart: Offset _ 0, betaLen: Offset _ MaxLen, event: Event _ NIL] RETURNS [alphaResultStart, alphaResultLen, betaResultStart, betaResultLen: Offset] = { SwitchResults: PROC = { start, len: Offset; start _ betaResultStart; len _ betaResultLen; betaResultStart _ alphaResultStart; betaResultLen _ alphaResultLen; alphaResultStart _ start; alphaResultLen _ len }; alphaSize, betaSize: Offset; switched: BOOLEAN _ FALSE; alphaSize _ RopeEdit.Size[alpha.rope]; alphaStart _ MIN[MAX[0,alphaStart],alphaSize]; alphaResultLen _ alphaLen _ MIN[MAX[0,alphaLen], alphaSize-alphaStart]; betaSize _ IF beta=alpha THEN alphaSize ELSE RopeEdit.Size[beta.rope]; betaStart _ MIN[MAX[0,betaStart],betaSize]; betaResultLen _ betaLen _ MIN[MAX[0,betaLen],betaSize-betaStart]; IF alpha=beta THEN { alphaEnd: Offset; IF alphaStart > betaStart THEN { -- switch them start: Offset _ alphaStart; len: Offset _ alphaLen; root: Ref _ alphaRoot; alphaStart _ betaStart; betaStart _ start; alphaResultLen _ alphaLen _ betaLen; alphaRoot _ betaRoot; betaRoot _ root; betaResultLen _ betaLen _ len; switched _ TRUE }; alphaEnd _ alphaStart+alphaLen; IF alphaEnd = betaStart THEN { -- turn into a move instead [] _ MoveText[alphaRoot,betaRoot,alpha,alphaStart,alpha,betaStart,betaLen,event]; betaResultStart _ alphaStart; alphaResultStart _ alphaStart+betaLen; IF switched THEN SwitchResults; RETURN }; IF alphaEnd > betaStart THEN { -- overlapping overlap, alphaHeadLen, betaTailLen: Offset; alphaHeadLen _ betaStart-alphaStart; betaTailLen _ betaStart+betaLen-alphaEnd; IF alphaHeadLen < 0 OR betaTailLen < 0 THEN RETURN [alphaStart,alphaLen,betaStart,betaLen]; overlap _ alphaEnd-betaStart; [alphaResultStart,alphaResultLen,betaResultStart,betaResultLen] _ TransposeText[alphaRoot,betaRoot,alpha,alphaStart,alphaHeadLen, alpha,alphaEnd,betaTailLen,event]; betaResultLen _ betaResultLen+overlap; alphaResultStart _ alphaResultStart-overlap; alphaResultLen _ alphaResultLen+overlap; IF switched THEN SwitchResults; RETURN }}; [alphaResultStart, alphaResultLen] _ MoveText[ -- move alpha after beta destRoot: betaRoot, sourceRoot: alphaRoot, dest: beta, destLoc: betaStart+betaLen, source: alpha, start: alphaStart, len: alphaLen, event: event]; IF alpha=beta THEN betaStart _ betaStart-alphaLen ELSE alphaResultStart _ alphaResultStart-betaLen; [betaResultStart, betaResultLen] _ MoveText[ -- move beta to old alpha location destRoot: alphaRoot, sourceRoot: betaRoot, dest: alpha, destLoc: alphaStart, source: beta, start: betaStart, len: betaLen, event: event]; IF switched THEN SwitchResults }; ReplaceByChar: PUBLIC PROC [ root: Ref, dest: RefTextNode, char: CHAR, start: Offset _ 0, len: Offset _ MaxLen, inherit: BOOLEAN _ TRUE, looks: Looks _ noLooks, event: Event _ NIL] RETURNS [resultStart, resultLen: Offset] = { notify: REF ChangingText Change; destRope, newRope: ROPE; destSize: Offset; alreadysaved: BOOLEAN; IF dest=NIL THEN RETURN [0,0]; destSize _ RopeEdit.Size[destRope _ dest.rope]; start _ MIN[MAX[0,start],destSize]; len _ MIN[MAX[0,len],destSize-start]; notify _ IF (alreadysaved _ AlreadySaved[dest,event]) THEN Alloc[] ELSE TextNode.pZone.NEW[ChangingText Change]; notify^ _ [ChangingText[root,dest,start,1,len,destRope,dest.runs]]; Notify[notify,before]; newRope _ RopeEdit.ReplaceByChar[destRope,char,start,len,destSize]; dest.runs _ TextLooks.ReplaceByRun[dest.runs,start,len,1,destSize,inherit,looks]; dest.rope _ newRope; NodeAddrs.Replace[dest,start,len,1]; BumpCount[dest]; Notify[notify,after]; IF alreadysaved THEN Free[notify] ELSE UndoEvent.Note[event,UndoChangeText,notify]; RETURN [start,1] }; ReplaceByString: PUBLIC PROC [ root: Ref, dest: RefTextNode, string: REF READONLY TEXT, stringStart: NAT _ 0, stringNum: NAT _ MaxNat, start: Offset _ 0, len: Offset _ MaxLen, inherit: BOOLEAN _ TRUE, looks: Looks _ noLooks, event: Event _ NIL] RETURNS [resultStart, resultLen: Offset] = { notify: REF ChangingText Change; destRope, newRope: ROPE; alreadysaved: BOOLEAN; destSize: Offset; IF dest=NIL THEN RETURN [0,0]; stringNum _ IF string=NIL OR stringStart >= string.length THEN 0 ELSE MIN[stringNum, string.length-stringStart]; destSize _ RopeEdit.Size[destRope _ dest.rope]; start _ MIN[MAX[0,start],destSize]; len _ MIN[MAX[0,len],destSize-start]; notify _ IF (alreadysaved _ AlreadySaved[dest,event]) THEN Alloc[] ELSE TextNode.pZone.NEW[ChangingText Change]; notify^ _ [ChangingText[root,dest,start,stringNum,len,destRope,dest.runs]]; Notify[notify,before]; newRope _ RopeEdit.ReplaceByString[destRope,string,stringStart,stringNum, start,len,destSize]; dest.runs _ TextLooks.ReplaceByRun[dest.runs,start,len,stringNum, destSize,inherit,looks]; dest.rope _ newRope; NodeAddrs.Replace[dest,start,len,stringNum]; BumpCount[dest]; Notify[notify,after]; IF alreadysaved THEN Free[notify] ELSE UndoEvent.Note[event,UndoChangeText,notify]; RETURN [start,stringNum] }; UndoChangeText: PROC [undoRef: REF, currentEvent: Event] = TRUSTED { saved: REF Change _ NARROW[undoRef]; WITH x:saved SELECT FROM ChangingText => [] _ ReplaceByText[x.root,x.text,0,MaxLen, x.oldRope,x.oldRuns,0,MaxLen,currentEvent]; ENDCASE => ERROR }; ReplaceByText: PUBLIC PROC [ root: Ref, dest: RefTextNode, destStart: Offset _ 0, destLen: Offset _ MaxLen, sourceRope: ROPE, sourceRuns: Runs, sourceStart: Offset _ 0, sourceLen: Offset _ MaxLen, event: Event _ NIL] RETURNS [resultStart, resultLen: Offset] = { sourceSize, destSize: Offset; IF dest=NIL THEN RETURN [0,0]; sourceSize _ RopeEdit.Size[sourceRope]; sourceStart _ MIN[MAX[0,sourceStart],sourceSize]; IF sourceLen # 0 THEN { IF sourceStart >= sourceSize THEN sourceStart _ sourceSize; sourceLen _ MIN[MAX[0,sourceLen], sourceSize-sourceStart]}; destSize _ RopeEdit.Size[dest.rope]; destStart _ MIN[MAX[0,destStart],destSize]; destLen _ MIN[MAX[0,destLen],destSize-destStart]; IF destLen=0 AND sourceLen=0 THEN RETURN [destStart,0]; [resultStart,resultLen] _ DoReplace[root,dest,destStart,destLen,destSize, sourceRope,sourceRuns,sourceStart,sourceLen,event] }; ReplaceText: PUBLIC PROC [ destRoot, sourceRoot: Ref, dest: RefTextNode, destStart: Offset _ 0, destLen: Offset _ MaxLen, source: RefTextNode, sourceStart: Offset _ 0, sourceLen: Offset _ MaxLen, event: Event _ NIL] RETURNS [resultStart, resultLen: Offset] = { sourceRope: ROPE; sourceSize, destSize: Offset; sourceRuns: Runs; IF dest=NIL THEN RETURN [0,0]; IF source # NIL THEN { sourceRope _ source.rope; sourceRuns _ source.runs }; sourceSize _ RopeEdit.Size[sourceRope]; sourceStart _ MIN[MAX[0,sourceStart],sourceSize]; IF sourceLen # 0 THEN { IF sourceStart >= sourceSize THEN sourceStart _ sourceSize; sourceLen _ MIN[MAX[0,sourceLen], sourceSize-sourceStart]}; destSize _ RopeEdit.Size[dest.rope]; destStart _ MIN[MAX[0,destStart],destSize]; destLen _ MIN[MAX[0,destLen],destSize-destStart]; IF destLen=0 AND sourceLen=0 THEN RETURN [destStart,0]; [resultStart,resultLen] _ DoReplace[destRoot,dest,destStart,destLen,destSize, sourceRope,sourceRuns,sourceStart,sourceLen,event] }; DeleteText: PUBLIC OneSpanProc = { [] _ ReplaceText[root, NIL, text, start, len, NIL, 0, MaxLen, event] }; CopyText: PUBLIC DestSpanProc = { -- copy the specified text [resultStart,resultLen] _ ReplaceText[destRoot, sourceRoot, dest, destLoc, 0, source, start, len, event] }; MoveTextOnto: PUBLIC PROC [ destRoot, sourceRoot: Ref, dest: RefTextNode, destStart: Offset _ 0, destLen: Offset _ MaxLen, source: RefTextNode, sourceStart: Offset _ 0, sourceLen: Offset _ MaxLen, event: Event _ NIL] RETURNS [resultStart, resultLen: Offset] = { sourceSize, destSize, start, len, end, destEnd: Offset; start _ sourceStart; len _ sourceLen; sourceSize _ Size[source]; start _ MIN[MAX[0,start],sourceSize]; len _ MIN[MAX[0,len],sourceSize-start]; end _ start+len; destSize _ IF dest=source THEN sourceSize ELSE Size[dest]; destStart _ MIN[MAX[0,destStart],destSize]; destLen _ MIN[MAX[0,destLen],destSize-destStart]; destEnd _ destStart+destLen; IF source=dest THEN { -- check for overlapping or adjacent IF start IN [destStart..destEnd) THEN { IF end < destEnd THEN [] _ DeleteText[sourceRoot,source,end,destEnd-end,event]; IF start > destStart THEN [] _ DeleteText[sourceRoot,source,destStart,start-destStart,event]; RETURN [destStart,len] }; IF end IN (destStart..destEnd) THEN { [] _ DeleteText[sourceRoot,source,end,destEnd-end,event]; RETURN [start,len] }; IF start <= destStart AND end >= destEnd THEN RETURN [start,len]; IF end=destStart THEN { [] _ DeleteText[sourceRoot,source,destStart,destLen,event]; RETURN [start,len] }; IF start=destEnd THEN { [] _ DeleteText[sourceRoot,source,destStart,destLen,event]; RETURN [destStart,len] }}; [] _ DeleteText[destRoot,dest,destStart,destLen,event]; IF source=dest AND start > destStart THEN start _ start-destLen; [resultStart,resultLen] _ MoveText[destRoot,sourceRoot,dest,destStart,source,start,len,event] }; ReplaceByRope: PUBLIC PROC [ root: Ref, dest: RefTextNode, rope: ROPE, start: Offset _ 0, len: Offset _ MaxLen, inherit: BOOLEAN _ TRUE, looks: Looks _ noLooks, event: Event] RETURNS [resultStart, resultLen: Offset] = { notify: REF ChangingText Change; destRope, newRope: ROPE; alreadysaved: BOOLEAN; destSize, ropeSize: Offset; IF dest=NIL THEN RETURN [0,0]; ropeSize _ RopeEdit.Size[rope]; destSize _ RopeEdit.Size[destRope _ dest.rope]; start _ MIN[MAX[0,start],destSize]; len _ MIN[MAX[0,len],destSize-start]; notify _ IF (alreadysaved _ AlreadySaved[dest,event]) THEN Alloc[] ELSE TextNode.pZone.NEW[ChangingText Change]; notify^ _ [ChangingText[root,dest,start,ropeSize,len,destRope,dest.runs]]; Notify[notify,before]; newRope _ RopeEdit.Replace[destRope,start,len,rope,destSize,ropeSize]; dest.runs _ TextLooks.ReplaceByRun[dest.runs,start,len,ropeSize,destSize,inherit,looks]; dest.rope _ newRope; NodeAddrs.Replace[dest,start,len,ropeSize]; BumpCount[dest]; Notify[notify,after]; IF alreadysaved THEN Free[notify] ELSE UndoEvent.Note[event,UndoChangeText,notify]; RETURN [start,ropeSize] }; ChangeLooks: PUBLIC PROC [ root: Ref, text: RefTextNode, remove, add: Looks, start: Offset _ 0, len: Offset _ MaxOffset, event: Event _ NIL] = { size: Offset; newRuns: Runs; IF text=NIL THEN RETURN; size _ RopeEdit.Size[text.rope]; start _ MIN[MAX[0,start],size]; len _ MIN[MAX[0,len],size-start]; IF len=0 THEN RETURN; newRuns _ TextLooks.ChangeLooks[text.runs,size,remove,add,start,len]; IF newRuns=text.runs THEN RETURN; -- no change DoChangeLooks[root,text,newRuns,start,len,event] }; DoChangeLooks: PROC [root: Ref, text: RefTextNode, newRuns: Runs, start, len: Offset, event: Event] = { oldRuns: Runs _ text.runs; notify: REF ChangingText Change; alreadysaved: BOOLEAN; notify _ IF (alreadysaved _ AlreadySaved[text,event]) THEN Alloc[] ELSE TextNode.pZone.NEW[ChangingText Change]; notify^ _ [ChangingText[root,text,start,len,len,text.rope,oldRuns]]; Notify[notify,before]; text.runs _ newRuns; BumpCount[text]; Notify[notify,after]; IF alreadysaved THEN Free[notify] ELSE UndoEvent.Note[event,UndoChangeText,notify] }; ChangeType: PUBLIC PROC [node: Ref, typeName: TextNode.TypeName, event: Event _ NIL, root: Ref _ NIL] = { notify: REF ChangingType Change; oldTypeName: TextNode.TypeName _ node.typename; IF node=NIL THEN RETURN; IF root=NIL THEN root _ TextNode.Root[node]; IF oldTypeName = typeName THEN RETURN; -- no change notify _ TextNode.pZone.NEW[ChangingType Change _ [ChangingType[root,node,typeName,oldTypeName]]]; Notify[notify,before]; node.typename _ typeName; Notify[notify,after]; IF event#NIL THEN UndoEvent.Note[event,UndoChangeType,notify] }; UndoChangeType: PROC [undoRef: REF, currentEvent: Event] = TRUSTED { saved: REF Change _ NARROW[undoRef]; WITH x:saved SELECT FROM ChangingType => ChangeType[x.node,x.oldTypeName,currentEvent,x.root]; ENDCASE => ERROR }; prefixAtom: ATOM = npI.PrefixAtom[]; prefixName: ROPE = npI.PrefixName[]; ChangeStyle: PUBLIC PROC [node: Ref, name: ROPE, event: Event _ NIL, root: Ref _ NIL] = { newprefix: ROPE; IF node=NIL THEN RETURN; IF RopeEdit.Size[name] > 0 THEN newprefix _ RopeEdit.Concat["\"",RopeEdit.Concat[name,"\" style"]]; PutProp[node,prefixName,newprefix,event,root] }; PutProp: PUBLIC PROC [node: Ref, name: ROPE, value: REF, event: Event _ NIL, root: Ref _ NIL] = { notify: REF ChangingProp Change; key: ATOM; oldval: REF; IF node=NIL THEN RETURN; IF root=NIL THEN root _ TextNode.Root[node]; key _ Atom.MakeAtom[name]; IF (oldval _ npI.GetProp[node,key]) = value THEN RETURN; notify _ TextNode.pZone.NEW[ChangingProp Change _ [ChangingProp[root,node,name,key,value,oldval]]]; Notify[notify,before]; npI.PutProp[node,key,value]; Notify[notify,after]; IF event#NIL THEN UndoEvent.Note[event,UndoPutProp,notify] }; UndoPutProp: PROC [undoRef: REF, currentEvent: Event] = TRUSTED { saved: REF Change _ NARROW[undoRef]; WITH x:saved SELECT FROM ChangingProp => PutProp[x.node,x.propName,x.oldval,currentEvent,x.root]; ENDCASE => ERROR }; GetProp: PUBLIC PROC [node: Ref, name: ROPE] RETURNS [value: REF] = { key: ATOM; IF node=NIL THEN RETURN; key _ Atom.MakeAtom[name]; RETURN [npI.GetProp[node,key]] }; Fetch: PUBLIC PROC [text: RefTextNode, index: Offset] RETURNS [CHAR, Looks] = { RETURN [RopeEdit.Fetch[text.rope,index], TextLooks.FetchLooks[text.runs,index]] }; FetchChar: PUBLIC PROC [text: RefTextNode, index: Offset] RETURNS [CHAR] = { RETURN [RopeEdit.Fetch[text.rope,index]] }; FetchLooks: PUBLIC PROC [text: RefTextNode, index: Offset] RETURNS [Looks] = { RETURN [TextLooks.FetchLooks[text.runs,index]] }; GetRope: PUBLIC PROC [text: RefTextNode] RETURNS [ROPE] = { RETURN [IF text=NIL THEN NIL ELSE text.rope] }; GetRuns: PUBLIC PROC [text: RefTextNode] RETURNS [Runs] = { RETURN [IF text=NIL THEN NIL ELSE text.runs] }; Size: PUBLIC PROC [text: RefTextNode] RETURNS [Offset] = { RETURN [IF text=NIL THEN 0 ELSE RopeEdit.Size[text.rope]] }; FromRope: PUBLIC PROC [rope: ROPE] RETURNS [new: RefTextNode] = { new _ TextNode.NewTextNode[]; new.rope _ rope; new.last _ TRUE; new.runs _ TextLooks.CreateRun[RopeEdit.Size[rope]] }; FromString: PUBLIC PROC [string: REF READONLY TEXT] RETURNS [new: RefTextNode] = { new _ TextNode.NewTextNode[]; new.last _ TRUE; new.rope _ RopeFrom.String[string]; new.runs _ TextLooks.CreateRun[RopeEdit.Size[new.rope]] }; DocFromNode: PUBLIC PROC [child: Ref] RETURNS [new: Ref] = { new _ TextNode.NewTextNode[]; new.child _ child; new.last _ TRUE; child.next _ new; child.last _ TRUE }; ChangeCaps: PUBLIC PROC [ root: Ref, dest: RefTextNode, start: Offset _ 0, len: Offset _ MaxLen, how: CapChange _ allCaps, event: Event _ NIL] = { notify: REF ChangingText Change; oldRope, destRope: ROPE; rdr: RopeReader.Ref; initCap: BOOLEAN _ TRUE; destSize: Offset; alreadysaved: BOOLEAN; IF dest=NIL THEN RETURN; destSize _ RopeEdit.Size[oldRope _ destRope _ dest.rope]; start _ MIN[MAX[0,start],destSize]; IF (len _ MIN[MAX[0,len],destSize-start])=0 THEN RETURN; notify _ IF (alreadysaved _ AlreadySaved[dest,event]) THEN Alloc[] ELSE TextNode.pZone.NEW[ChangingText Change]; notify^ _ [ChangingText[root,dest,start,len,len,destRope,dest.runs]]; Notify[notify,before]; rdr _ RopeReader.GetRopeReader[]; UNTIL len=0 DO c: CHAR; maxText: NAT = 1000; num: NAT _ MIN[len, maxText]; new: REF TEXT _ TextNode.pZone.NEW[TEXT[num]]; RopeReader.SetPosition[rdr,destRope,start]; SELECT how FROM firstCap => IF num > 0 THEN { new[0] _ RopeEdit.UpperCase[RopeReader.Get[rdr]]; FOR i:NAT IN [1..num) DO new[i] _ RopeEdit.LowerCase[RopeReader.Get[rdr]]; ENDLOOP }; allCaps => FOR i:NAT IN [0..num) DO new[i] _ RopeEdit.UpperCase[RopeReader.Get[rdr]]; ENDLOOP; allLower => FOR i:NAT IN [0..num) DO new[i] _ RopeEdit.LowerCase[RopeReader.Get[rdr]]; ENDLOOP; initCaps => { init: BOOLEAN _ TRUE; FOR i:NAT IN [0..num) DO SELECT c _ RopeReader.Get[rdr] FROM IN ['a..'z] => { new[i] _ IF init THEN c-40B ELSE c; -- force first upper init _ FALSE }; IN ['A..'Z] => { new[i] _ IF init THEN c ELSE c+40B; -- force others lower init _ FALSE }; ENDCASE => { new[i] _ c; init _ c # '\' AND ~RopeEdit.AlphaNumericChar[c] }; ENDLOOP }; ENDCASE => ERROR; new.length _ num; destRope _ RopeEdit.ReplaceByString[destRope,new,0,num,start,num,destSize]; IF RopeEdit.Size[destRope] # destSize THEN ERROR; len _ len-num; start _ start+num; ENDLOOP; dest.rope _ destRope; RopeReader.FreeRopeReader[rdr]; Notify[notify,after]; IF alreadysaved THEN Free[notify] ELSE UndoEvent.Note[event,UndoChangeText,notify] }; BumpCount: PROC [text: RefTextNode] = { countLimit: Offset _ 20; count: [0..TextNode.countMax]; IF text=NIL THEN RETURN; IF(count_text.count) = TextNode.countMax OR (text.count _ count+1) >= countLimit THEN IF Flatten[text] THEN text.count _ 0 ELSE text.count _ text.count/2 }; maxRopeDepth: Offset; debug: BOOL _ FALSE; Flatten: PUBLIC PROC [text: RefTextNode] RETURNS [BOOLEAN] = { ropeSize, ropePieces, ropeDepth: Offset; looksSize, looksPieces, looksDepth: Offset; ropeFlag, looksFlag: BOOLEAN; rope: ROPE _ text.rope; runs: Runs _ text.runs; [ropeSize,ropePieces,ropeDepth] _ RopeEdit.RopeStats[rope]; [looksSize,looksPieces,looksDepth] _ TextLooks.LooksStats[runs]; IF ropeSize=0 AND looksSize=0 THEN RETURN [FALSE]; IF ropeDepth > maxRopeDepth THEN maxRopeDepth _ ropeDepth; -- for debugging ropeFlag _ (ropePieces > 20 AND ropeSize < 5*ropePieces) OR ropeDepth > 100 OR (SELECT ropePieces/8 FROM < 2 => ropeDepth/4 > ropePieces, < 6 => ropeDepth/2 > ropePieces, < 12 => ropeDepth > ropePieces, ENDCASE => ropeDepth > ropePieces/2); looksFlag _ (looksPieces > 20 AND looksSize < 5*looksPieces) OR looksDepth > 100 OR (SELECT looksPieces/8 FROM < 2 => looksDepth/4 > looksPieces, < 6 => looksDepth/2 > looksPieces, < 12 => looksDepth > looksPieces, ENDCASE => looksDepth > looksPieces/2); IF ropeFlag OR looksFlag THEN { IF debug THEN { CheckNode.CheckRope[rope]; CheckNode.CheckRuns[runs] }; text.rope _ IF ropeSize > 256 THEN RopeEdit.SemiFlatten[rope] ELSE RopeEdit.Flatten[rope]; text.runs _ TextLooks.Flatten[runs]; IF debug THEN { CheckNode.CheckRope[text.rope]; CheckNode.CheckRuns[text.runs] }; RETURN [TRUE] }; RETURN [FALSE] }; AlreadySaved: PUBLIC PROC [text: RefTextNode, event: Event] RETURNS [BOOLEAN] = TRUSTED { IF event = NIL THEN RETURN [TRUE]; FOR l: UndoEvent.SubEvent _ event.subevents, l.next UNTIL l=NIL DO IF l.undoRef # NIL THEN WITH x: l.undoRef SELECT FROM ChangingText => IF x.text = text THEN RETURN[TRUE]; ENDCASE; ENDLOOP; RETURN [FALSE] }; Start: PUBLIC PROC = { }; END. d-- TextEditImpl.mesa -- written by Bill Paxton, March 1981 -- last edit by Bill Paxton, December 28, 1982 11:43 am Last Edited by: Maxwell, January 5, 1983 3:31 pm -- **** Cache of notify records **** -- **** Text Editing Operations **** -- if inherit is false, char gets specifed looks -- if inherit is true, char gets looks in following manner: -- if dest length is 0, then gets looks from argument list, else -- if start > 0, then looks and list of previous char, -- else looks of char following replacement -- must replace all of text since only save first change per event -- replace the dest text by a copy of the source text -- names that are in the replaced text move to destStart -- names that are after the replaced text are adjusted -- **** Operation to add or delete looks **** -- first remove then add in the given range -- **** Changing Style / Type of node **** -- **** Fetch info operations **** -- fetches the indexed information -- use readers if want info from contiquous locations -- fetches the indexed information -- use readers if want info from contiquous locations -- returns the looks for the character at the given location -- use reader's if getting looks for sequence of locations -- **** Operations to create a text node **** -- create a text node with looks from a normal rope -- copies the contents of the string -- ***** Cap's and Lowercase -- ***** Miscellaneous -- see if need to flatten -- size = # of characters -- pieces = # terminal nodes in tree -- depth = max depth of tree -- returns TRUE if there is already a ChangingText record for given node in the event -- ***** Initialization Êy˜JšÏc™Jš%™%Jš7™7J™0JšÏk ˜ J˜ J˜ J˜ J˜ J˜ J˜ J˜ J˜ J˜ J˜ J˜J˜J˜ J˜šœž ˜šžœF˜MJ˜5—Jšžœ ˜—Jšž˜Jšžœ˜J˜Jšœžœ˜J˜Jš$™$J˜Jšœžœ˜6J˜šÏnœžœžœžœ˜2Jšžœžœ˜3—J˜šŸœž œžœ žœ˜@Jšžœžœžœ˜Jšžœ žœžœ"žœ˜=Jš žœžœ žœžœ"žœ˜BJš žœžœ žœžœ"žœ˜BJšžœ˜—J˜šŸœž œ žœ˜7Jšžœžœžœ˜Jš žœžœžœžœžœ˜MJšžœ žœžœ˜)Jšžœžœ žœžœ˜.Jšžœžœ žœžœ˜1—J™Jš$™$J˜šŸ œžœ ˜J˜8Jšœ žœ˜#J˜-Jšžœ%˜,Jšœ žœ˜%J˜ Jšœžœ˜ Jšœžœ˜J˜%Jšœ žœ˜J˜"J˜+šœ žœ+žœ˜BJšžœžœ˜-—J˜RJ˜šžœžœ˜J˜@J˜B—Jšœ žœ˜šžœ žœ˜&šžœ žœ˜(J˜CJ˜DJšœ žœ˜—šžœžœžœ˜2J˜CJ˜DJšœ žœ˜——šžœžœ žœ˜,šžœ žœ˜*J˜1J˜2Jšœ žœ˜—šžœžœžœ˜>J˜+J˜,Jšœ žœ˜——šžœ žœ˜˜6J˜ —˜7J˜"——J˜)J˜4J˜J˜Jšžœžœ ˜!Jšžœ-˜1Jšžœ˜J˜—šŸœžœžœ˜J˜J˜'J˜=Jšœžœ˜Jšžœ%˜,š Ÿ œžœžœžœžœ˜IJš žœ žœžœžœžœžœ˜:šžœ žœ˜1Jšœ%˜%Jšžœžœ˜CJšœ'˜'Jšœ"'œ˜K—šžœ ˜=JšœF˜F—Jšžœžœ˜—J˜J˜(Jšœžœžœ˜%Jšœžœžœ˜'Jšœ žœ žœ žœ˜HJšœ žœžœ˜'Jš žœ žœ žœžœžœ ˜IJšžœžœžœ ˜!Jšœ/˜/Jšœd˜dšžœ žœ˜1Jšžœžœ˜NJšžœ œ˜:—Jšœ2˜2Jšžœ žœ˜1J˜—šŸ œžœžœ˜J˜J˜FJšœSžœ˜XJšžœO˜VšŸ œžœ˜J˜J˜-J˜CJ˜1—J˜Jšœ žœžœ˜J˜J˜&Jšœ žœžœ˜.Jšœžœžœ$˜GJšœ žœ žœ žœ˜FJšœ žœžœ˜+Jšœžœžœ ˜Ašžœ žœ˜J˜šžœžœ˜/J˜J˜J˜J˜*J˜$J˜&J˜Jšœ žœ˜—J˜šžœžœ˜:J˜QJ˜J˜&Jšžœ žœ˜Jšžœ˜ —šžœžœ˜-J˜+J˜$J˜)šžœžœž˜+Jšžœ)˜/—J˜˜A˜?J˜"——J˜&J˜,J˜(Jšžœ žœ˜Jšžœ˜ ——šœ/˜GJ˜*J˜'J˜@—Jšžœ žœ˜1Jšžœ-˜1šœ-"˜OJ˜*J˜!J˜<—Jšžœ žœ˜!J˜—šŸ œžœžœ˜Jšœ$žœ˜)J˜(Jšœ žœžœ)žœ˜DJšžœ%˜,Jš2™2š;™;Jš@™@Jš6™6Jš+™+—Jšœžœ˜ Jšœžœ˜J˜Jšœžœ˜Jšžœžœžœžœ˜J˜/Jšœžœžœ˜#Jšœžœžœ˜%šœ žœ+žœ˜BJšžœžœ˜-—J˜CJ˜J˜DJ˜RJ˜J˜$J˜J˜Jšžœžœ ˜!Jšžœ-˜1Jšžœ ˜J˜—šŸœžœžœ˜Jšœ&žœžœžœ˜8Jšœ žœžœ ˜.J˜(Jšœ žœžœ)žœ˜DJšžœ%˜,Jšœžœ˜ Jšœžœ˜Jšœžœ˜J˜Jšžœžœžœžœ˜š œ žœžœžœžœž˜EJšžœ'˜*—J˜/Jšœžœžœ˜#Jšœžœžœ˜%šœ žœ+žœ˜BJšžœžœ˜-—J˜KJ˜˜IJ˜—˜AJ˜—J˜J˜,J˜J˜Jšžœžœ ˜!Jšžœ-˜1Jšžœ˜J˜—šŸœžœ žœžœ˜DJšœžœ žœ ˜$Jšžœ žœž˜˜:J˜+JšB™B—Jšžœžœ˜J˜—šŸ œžœžœ˜J˜NJšœ žœ˜#JšœDžœ˜HJšžœ%˜,J˜Jšžœžœžœžœ˜J˜'Jšœžœžœ˜1šžœžœ˜Jšžœžœ˜;Jšœ žœžœ(˜;—J˜$Jšœ žœžœ˜+Jšœ žœžœ ˜1Jšžœ žœ žœžœ˜7˜IJ˜5J˜——šŸ œžœžœ˜J˜J˜CJ˜IJšœžœ˜Jšžœ%˜,Jš5™5Jš8™8Jš6™6Jšœ žœ˜J˜J˜Jšžœžœžœžœ˜Jšžœ žœžœ8˜LJ˜'Jšœžœžœ˜1šžœžœ˜Jšžœžœ˜;Jšœ žœžœ(˜;—J˜$Jšœ žœžœ˜+Jšœ žœžœ ˜1Jšžœ žœ žœžœ˜7˜MJ˜5J˜——šœ žœ˜"Jšœžœžœ˜GJ˜—šœ žœ˜<˜MJ˜J˜——šŸ œžœžœ˜J˜J˜CJ˜IJšœžœ˜Jšžœ%˜,J˜7J˜%J˜Jšœžœžœ˜%Jšœžœžœ˜'J˜Jšœ žœ žœ žœ ˜:Jšœ žœžœ˜+Jšœ žœžœ ˜1J˜šžœ žœ$˜:šžœžœžœ˜'šžœž˜J˜9—šžœž˜J˜C—Jšžœ˜—šžœžœžœ˜%J˜9Jšžœ˜—Jšžœžœžœžœ ˜Ašžœžœ˜J˜;Jšžœ˜—šžœžœ˜J˜;Jšžœ˜——J˜7Jšžœ žœžœ˜@˜J˜FJ˜——šŸ œžœžœ˜Jšœ$žœ˜)J˜(Jšœ žœžœ'˜>Jšžœ%˜,Jšœžœ˜ Jšœžœ˜Jšœžœ˜J˜Jšžœžœžœžœ˜J˜J˜/Jšœžœžœ˜#Jšœžœžœ˜%šœ žœ+žœ˜BJšžœžœ˜-—J˜JJ˜J˜GJ˜XJ˜J˜+J˜J˜Jšžœžœ ˜!Jšžœ-˜1Jšžœ˜J˜J˜—Jš-™-J˜šŸ œžœžœ˜J˜1Jšœ;žœ˜CJš+™+J˜ J˜Jšžœžœžœžœ˜J˜ Jšœžœžœ˜Jšœžœžœ˜!Jšžœžœžœ˜J˜FJšžœžœžœ ˜.J˜3J˜—šŸ œžœ˜2J˜4J˜Jšœžœ˜ Jšœžœ˜šœ žœ+žœ˜BJšžœžœ˜-—J˜DJ˜J˜J˜J˜Jšžœžœ ˜!Jšžœ/˜3J˜J˜—Jš*™*J˜š Ÿ œžœžœ9žœžœ˜iJšœžœ˜ J˜/Jšžœžœžœžœ˜Jšžœžœžœ˜,Jšžœžœžœ ˜3šœžœ˜1J˜0—J˜J˜J˜Jšžœžœžœ/˜@J˜—šŸœžœ žœžœ˜DJšœžœ žœ ˜$Jšžœ žœž˜J˜EJšžœžœ˜J˜—Jšœ žœ˜$Jšœ žœ˜$J˜š Ÿ œžœžœžœžœžœ˜YJšœ žœ˜Jšžœžœžœžœ˜šžœžœ˜ J˜C—J˜0J˜—šŸœžœžœžœ žœžœžœ˜aJšœžœ˜ Jšœžœ˜ Jšœžœ˜ Jšžœžœžœžœ˜Jšžœžœžœ˜,J˜Jšžœ*žœžœ˜8šœžœ˜1J˜1—J˜J˜J˜Jšžœžœžœ,˜=J˜—šŸ œžœ žœžœ˜AJšœžœ žœ ˜$Jšžœ žœž˜J˜HJšžœžœ˜J˜—š Ÿœžœžœžœžœ žœ˜EJšœžœ˜ Jšžœžœžœžœ˜J˜Jšžœ˜!J˜J˜—Jš"™"J˜š Ÿœžœžœ$žœžœ ˜OJš"™"Jš5™5šžœ"˜(J˜)J˜——š Ÿ œžœžœ$žœžœ˜LJš"™"Jš5™5Jšžœ%˜+J˜—šŸ œžœž˜Jšœ#žœ ˜6Jš<™J˜J˜—Jš-™-J˜š Ÿœžœžœžœžœ˜AJš3™3J˜Jšœžœ˜!J˜6J˜—š Ÿ œžœžœ žœžœžœ˜3Jšžœ˜Jš$™$Jšœ)žœ˜.J˜#J˜:J˜—šŸ œžœžœžœ˜J˜(J˜+Jšœžœ˜Jšœžœ ˜J˜J˜;˜@Jš™Jš$™$Jš™—Jš žœ žœ žœžœžœ˜2Jšžœžœ˜Kšœžœžœž˜Nšœžœž˜J˜ J˜ J˜Jšžœ˜%——šœžœžœž˜Sšœžœž˜J˜"J˜"J˜!Jšžœ ˜'——šžœ žœ žœ˜šžœžœ˜J˜J˜—˜ Jšžœžœ˜1Jšžœ˜—J˜$šžœžœ˜J˜J˜!—Jšžœžœ˜—Jšžœžœ˜J˜—š Ÿ œžœžœ#žœžœžœ˜YJšV™VJš žœ žœžœžœžœ˜"šžœ1žœžœž˜Bšžœ žœž˜šžœžœž˜Jš œžœžœžœžœ˜3Jšžœ˜——Jšžœ˜—Jšžœžœ˜J˜—Jš™J˜šŸœžœžœ˜J˜J˜—Jšžœ˜J˜—…—[dzA