DIRECTORY Atom USING [DottedPairNode, GetPName, PropList], CheckNode USING [CheckRope, CheckRuns], EditNotify USING [Change, Notify], NodeAddrs USING [MapTextAddrs, MoveTextAddr, PutTextAddr, Replace, UnpinAll], NodeProps USING [GetProp, PutProp], Rope USING [Balance, Cat, Concat, Fetch, Flatten, FromChar, FromProc, FromRefText, InlineSize, Replace, ROPE, Size, Substr], RopeEdit USING [AlphaNumericChar, LowerCase, ReplaceByTEXT, UpperCase], RopeReader USING [FreeRopeReader, Get, GetRope, GetRopeReader, Ref, SetPosition], Rosary USING [CatSegments, Fetch, FromItem, FromProcProc, MapRuns, ROSARY, RunActionType, Size], TextEdit USING [CapChange, CharSet, end, MapPropsAction, ModifyPropsAction, String, Text], TextLooks USING [allLooks, ChangeLooks, Concat, CreateRun, FetchLooks, Flatten, Looks, LooksStats, noLooks, Replace, ReplaceByRun, Runs, Substr], TextNode USING [countMax, Location, NewTextNode, Node, Root], UndoEvent USING [Event, Note, SubEvent]; TextEditImpl: CEDAR MONITOR IMPORTS Atom, CheckNode, EditNotify, NodeAddrs, NodeProps, Rope, RopeEdit, RopeReader, Rosary, TextEdit, TextLooks, TextNode, UndoEvent EXPORTS TextEdit, UndoEvent = BEGIN Change: PUBLIC TYPE = EditNotify.Change; Node: TYPE = TextNode.Node; ROPE: TYPE = Rope.ROPE; ROSARY: TYPE = Rosary.ROSARY; Runs: TYPE = TextLooks.Runs; Looks: TYPE = TextLooks.Looks; noLooks: Looks = TextLooks.noLooks; allLooks: Looks = TextLooks.allLooks; Event: TYPE = UndoEvent.Event; CapChange: TYPE ~ TextEdit.CapChange; MapPropsAction: TYPE ~ TextEdit.MapPropsAction; ModifyPropsAction: TYPE ~ TextEdit.ModifyPropsAction; CharSet: TYPE ~ TextEdit.CharSet; Location: TYPE ~ TextNode.Location; Text: TYPE ~ TextEdit.Text; String: TYPE ~ TextEdit.String; maxLen: INT ~ INT.LAST; Assertion: TYPE ~ BOOL[TRUE..TRUE]; scratch1, scratch2, scratch3: REF Change.ChangingText _ NIL; Alloc: ENTRY PROC RETURNS [scratch: REF Change.ChangingText] = { 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 _ NEW[Change.ChangingText]; }; Free: ENTRY PROC [scratch: REF Change.ChangingText] = { 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; }; Size: PUBLIC PROC [node: Node] RETURNS [INT] = { RETURN [IF node=NIL THEN 0 ELSE Rope.InlineSize[node.rope]] }; GetRope: PUBLIC PROC [text: Node] RETURNS [ROPE] = { RETURN [IF node=NIL THEN NIL ELSE node.rope] }; GetRuns: PUBLIC PROC [node: Node] RETURNS [Runs] = { RETURN [IF node=NIL THEN NIL ELSE node.runs] }; GetRosary: PROC [node: Node, name: ATOM] RETURNS [ROSARY] ~ { WITH NodeProps.GetProp[node, name] SELECT FROM rosary: ROSARY => RETURN[rosary]; ENDCASE => RETURN[NIL]; }; GetCharSets: PROC [node: Node] RETURNS [ROSARY] ~ INLINE { RETURN[IF node.hascharsets THEN GetRosary[node, $CharSets] ELSE NIL]; }; GetCharProps: PROC [node: Node] RETURNS [ROSARY] ~ INLINE { RETURN[IF node.hascharprops THEN GetRosary[node, $CharProps] ELSE NIL]; }; Fetch: PUBLIC PROC [text: Node, index: INT] RETURNS [charSet: CharSet, char: CHAR, looks: Looks] = { [charSet, char] _ FetchChar[text, index]; looks _ FetchLooks[text, index]; }; FetchChar: PUBLIC PROC [text: Node, index: INT] RETURNS [charSet: CharSet _ 0, char: CHAR] = { charSets: ROSARY ~ GetCharSets[text]; IF charSets#NIL THEN WITH Rosary.Fetch[charSets, index] SELECT FROM refCharSet: REF CharSet => charSet _ refCharSet^; ENDCASE; char _ Rope.Fetch[text.rope, index]; }; FetchLooks: PUBLIC PROC [text: Node, index: INT] RETURNS [Looks] = { RETURN [TextLooks.FetchLooks[text.runs, index]]; }; ClipLocation: PROC [loc: Location, size: INT] RETURNS [Location] ~ INLINE { loc.where _ MIN[MAX[0, loc.where], size]; RETURN[loc]; }; ClipText: PROC [text: Text, size: INT] RETURNS [Text] ~ INLINE { text.start _ MIN[MAX[0, text.start], size]; text.len _ MIN[MAX[0, text.len], size-text.start]; RETURN[text]; }; ClipString: PROC [string: String] RETURNS [String] ~ INLINE { size: NAT ~ string.text.length; string.start _ MIN[string.start, size]; string.len _ MIN[string.len, size-string.start]; RETURN[string]; }; rosaryOfNil: ROSARY ~ Rosary.FromItem[NIL, maxLen]; RosaryReplace: PROC [dest: ROSARY, destSize: INT, destStart: INT, destLen: INT, source: ROSARY, sourceStart: INT, sourceLen: INT] RETURNS [rosary: ROSARY] ~ { notNil: Rosary.RunActionType ~ { quit _ item#NIL }; d: ROSARY ~ IF dest=NIL THEN rosaryOfNil ELSE dest; d0: INT ~ 0; d1: INT ~ destStart; d2: INT ~ d1+destLen; d3: INT ~ destSize; s: ROSARY ~ IF source=NIL THEN rosaryOfNil ELSE source; s0: INT ~ sourceStart; s1: INT ~ s0+sourceLen; rosary _ Rosary.CatSegments[[d, d0, d1-d0], [s, s0, s1-s0], [d, d2, d3-d2]]; IF NOT Rosary.MapRuns[[rosary], notNil] THEN rosary _ NIL; }; RosaryMapRuns: PROC [rosary: ROSARY, start, len: INT, action: Rosary.RunActionType] ~ { IF rosary=NIL THEN [] _ action[NIL, len] ELSE [] _ Rosary.MapRuns[[rosary, start, len], action]; }; DoReplace: PROC [root: Node, dest: Text, sourceRope: ROPE, sourceRuns: Runs, sourceCharSets: ROSARY, sourceCharProps: ROSARY, sourceStart, sourceLen: INT, event: Event] RETURNS [result: Text] = { destRope: ROPE ~ dest.node.rope; destRuns: Runs ~ dest.node.runs; destCharSets: ROSARY ~ GetCharSets[dest.node]; destCharProps: ROSARY ~ GetCharProps[dest.node]; destSize: INT ~ Size[dest.node]; -- node size before replacement size: INT ~ destSize-dest.len+sourceLen; -- node size after replacement replaceRope, newRope: ROPE _ NIL; replaceRuns, newRuns: Runs _ NIL; special: BOOL _ FALSE; alreadysaved: BOOL ~ AlreadySaved[dest.node, event]; notify: REF Change.ChangingText ~ IF alreadysaved THEN Alloc[] ELSE NEW[Change.ChangingText]; notify^ _ [ChangingText[ root: root, text: dest.node, start: dest.start, newlen: sourceLen, oldlen: dest.len, oldRope: destRope, oldRuns: destRuns, oldCharSets: destCharSets, oldCharProps: destCharProps ]]; EditNotify.Notify[notify, before]; IF sourceLen > 0 THEN { replaceRope _ Rope.Substr[sourceRope, sourceStart, sourceLen]; replaceRuns _ TextLooks.Substr[sourceRuns, sourceStart, sourceLen]; }; SELECT TRUE FROM (dest.len=0) => { -- doing an insert SELECT TRUE FROM (dest.start=0) => { -- insert at start newRope _ Rope.Concat[replaceRope, destRope]; newRuns _ TextLooks.Concat[replaceRuns, destRuns, sourceLen, destSize]; special _ TRUE; } (dest.start=destSize) => { -- insert at end newRope _ Rope.Concat[destRope, replaceRope]; newRuns _ TextLooks.Concat[destRuns, replaceRuns, destSize, sourceLen]; special _ TRUE; }; ENDCASE; (sourceLen=0) => { -- doing a delete SELECT TRUE FROM (dest.start=0) => { -- delete from start newRope _ Rope.Substr[destRope, dest.len, size]; newRuns _ TextLooks.Substr[destRuns, dest.len, size]; special _ TRUE; } (dest.end=destSize) => { -- delete from end newRope _ Rope.Substr[destRope, 0, size]; newRuns _ TextLooks.Substr[destRuns, 0, size]; special _ TRUE; }; ENDCASE; }; ENDCASE; IF NOT special THEN { newRope _ Rope.Replace[base: destRope, start: dest.start, len: dest.len, with: replaceRope]; newRuns _ TextLooks.Replace[base: destRuns, start: dest.start, len: dest.len, replace: replaceRuns, baseSize: destSize, repSize: sourceLen] }; dest.node.rope _ newRope; dest.node.runs _ newRuns; IF sourceCharSets#NIL OR destCharSets#NIL THEN { newCharSets: ROSARY ~ RosaryReplace[dest: destCharSets, destSize: destSize, destStart: dest.start, destLen: dest.len, source: sourceCharSets, sourceStart: sourceStart, sourceLen: sourceLen]; NodeProps.PutProp[dest.node, $CharSets, newCharSets]; }; IF sourceCharProps#NIL OR destCharProps#NIL THEN { newCharProps: ROSARY ~ RosaryReplace[dest: destCharProps, destSize: destSize, destStart: dest.start, destLen: dest.len, source: sourceCharProps, sourceStart: sourceStart, sourceLen: sourceLen]; NodeProps.PutProp[dest.node, $CharProps, newCharProps]; }; NodeAddrs.Replace[dest.node, dest.start, dest.len, sourceLen]; BumpCount[dest.node]; EditNotify.Notify[notify, after]; IF alreadysaved THEN Free[notify] ELSE UndoEvent.Note[event, UndoChangeText, notify]; RETURN [[dest.node, dest.start, sourceLen]]; }; UndoChangeText: PROC [undoRef: REF Change, currentEvent: Event] = { WITH undoRef SELECT FROM x: REF Change.ChangingText => { [] _ DoReplace[ root: x.root, dest: [x.text, 0, Size[x.text]], sourceRope: x.oldRope, sourceRuns: x.oldRuns, sourceCharSets: x.oldCharSets, sourceCharProps: x.oldCharProps, sourceStart: 0, sourceLen: Rope.Size[x.oldRope], event: currentEvent ]; }; ENDCASE => ERROR; }; DoDelete: PROC [root: Node, text: Text, event: Event] RETURNS [result: Text] ~ { IF text.len=0 THEN RETURN[text] ELSE RETURN DoReplace[ root: root, dest: text, sourceRope: NIL, sourceRuns: NIL, sourceCharSets: NIL, sourceCharProps: NIL, sourceStart: 0, sourceLen: 0, event: event ]; }; DeleteText: PUBLIC PROC [root: Node, text: Text, event: Event] = { text _ ClipText[text, Size[text.node]]; IF text.len=0 THEN NULL ELSE [] _ DoDelete[root, text, event]; }; ReplaceText: PUBLIC PROC [destRoot, sourceRoot: Node, dest: Text, source: Text, event: Event] RETURNS [result: Text] = { dest _ ClipText[dest, Size[dest.node]]; source _ ClipText[source, Size[source.node]]; IF source.len=0 THEN RETURN DoDelete[destRoot, dest, event] ELSE RETURN DoReplace[ root: destRoot, dest: dest, sourceRope: source.node.rope, sourceRuns: source.node.runs, sourceCharSets: GetCharSets[source.node], sourceCharProps: GetCharProps[source.node], sourceStart: source.start, sourceLen: source.len, event: event ]; }; CopyText: PUBLIC PROC [destRoot, sourceRoot: Node, dest: Location, source: Text, event: Event] RETURNS [result: Text] = { RETURN ReplaceText[destRoot: destRoot, sourceRoot: sourceRoot, dest: [dest.node, dest.where, 0], source: source, event: event]; }; MoveText: PUBLIC PROC [destRoot, sourceRoot: Node, dest: Location, source: Text, event: Event] RETURNS [result: Text] = { dest _ ClipLocation[dest, Size[dest.node]]; source _ ClipText[source, Size[source.node]]; IF source.len=0 THEN RETURN [[dest.node, dest.where, 0]]; IF source.node=dest.node THEN { -- move text within a single node MoveAndPinAddrs: PROC [addr: REF, location: INT] RETURNS [quit: BOOL _ FALSE] = { IF location IN [source.start..source.end) THEN { new: INT _ location-source.start+dest.where; IF dest.where>source.start THEN new _ new-source.len; -- move toward end of node NodeAddrs.PutTextAddr[n: dest.node, addr: addr, location: new, pin: TRUE]; }; }; IF dest.where IN [source.start..source.end] THEN RETURN [source]; [] _ NodeAddrs.MapTextAddrs[dest.node, MoveAndPinAddrs]; result _ CopyText[destRoot, sourceRoot, dest, source, event]; IF dest.where { -- adjacent betaResult _ MoveText[root, root, [node, alpha.start], beta, event]; alphaResult _ [node, alpha.start+beta.len, alpha.len]; }; beta.start { -- overlapping IF beta.end { -- disjoint alphaResult _ MoveText[root, root, [node, beta.end], alpha, event]; beta.start _ beta.start-alpha.len; -- moving alpha behind beta shifts beta left betaResult _ MoveText[root, root, [node, alpha.start], beta, event]; }; } ELSE { -- transpose between two different nodes alphaResult _ MoveText[betaRoot, alphaRoot, [beta.node, beta.start], alpha, event]; beta.start _ beta.start+alpha.len; -- moving alpha in front of beta shifts beta right betaResult _ MoveText[alphaRoot, betaRoot, [alpha.node, alpha.start], beta, event]; }; IF switched THEN RETURN[betaResult, alphaResult]; }; MoveTextOnto: PUBLIC PROC [destRoot, sourceRoot: Node, dest, source: Text, event: Event] RETURNS [result: Text] = { dest _ ClipText[dest, Size[dest.node]]; source _ ClipText[source, Size[source.node]]; IF dest.node=source.node THEN { -- same node, check for overlap rootsMatch: Assertion ~ (destRoot=sourceRoot); root: Node ~ destRoot; node: Node ~ dest.node; IF dest.end>source.end THEN { -- delete part of dest after source afterStart: INT ~ MAX[dest.start, source.end]; afterLen: INT ~ dest.end-afterStart; DeleteText[root, [node, afterStart, afterLen], event]; }; IF dest.start IF destItem^=charSet THEN item _ destItem; ENDCASE; ENDLOOP; }; IF item=NIL THEN item _ NEW[CharSet _ charSet]; RETURN [Rosary.FromItem[item, replaceSize]]; }; }; ReplaceByRope: PUBLIC PROC [root: Node, dest: Text, rope: ROPE, inherit: BOOL, looks: Looks, charSet: CharSet, event: Event] RETURNS [result: Text] = { destSize: INT ~ Size[dest.node]; ropeSize: INT ~ Rope.Size[rope]; dest _ ClipText[dest, destSize]; IF dest.len#0 OR ropeSize#0 THEN { destRope: ROPE ~ dest.node.rope; destRuns: Runs ~ dest.node.runs; destCharSets: ROSARY ~ GetCharSets[dest.node]; destCharProps: ROSARY ~ GetCharProps[dest.node]; alreadysaved: BOOL ~ AlreadySaved[dest.node, event]; notify: REF Change.ChangingText ~ ( IF alreadysaved THEN Alloc[] ELSE NEW[Change.ChangingText] ); notify^ _ [ChangingText[ root: root, text: dest.node, start: dest.start, newlen: ropeSize, oldlen: dest.len, oldRope: destRope, oldRuns: destRuns, oldCharSets: destCharSets, oldCharProps: destCharProps ]]; EditNotify.Notify[notify, before]; dest.node.rope _ Rope.Replace[base: destRope, start: dest.start, len: dest.len, with: rope]; dest.node.runs _ TextLooks.ReplaceByRun[destRuns, dest.start, dest.len, ropeSize, destSize, inherit, looks]; NodeAddrs.Replace[dest.node, dest.start, dest.len, ropeSize]; BumpCount[dest.node]; IF dest.node.hascharsets OR charSet#0 THEN { sourceCharSets: ROSARY ~ IF charSet=0 THEN NIL ELSE CharSetsForReplace[charSet: charSet, replaceSize: ropeSize, destCharSets: destCharSets, destSize: destSize, destStart: dest.start, destLen: dest.len]; newCharSets: ROSARY ~ RosaryReplace[dest: destCharSets, destSize: destSize, destStart: dest.start, destLen: dest.len, source: sourceCharSets, sourceStart: 0, sourceLen: ropeSize]; NodeProps.PutProp[dest.node, $CharSets, newCharSets]; }; IF dest.node.hascharprops THEN { newCharProps: ROSARY ~ RosaryReplace[dest: destCharProps, destSize: destSize, destStart: dest.start, destLen: dest.len, source: NIL, sourceStart: 0, sourceLen: ropeSize]; NodeProps.PutProp[dest.node, $CharProps, newCharProps]; }; EditNotify.Notify[notify, after]; IF alreadysaved THEN Free[notify] ELSE UndoEvent.Note[event, UndoChangeText, notify]; }; RETURN [[dest.node, dest.start, ropeSize]]; }; InsertRope: PUBLIC PROC [root: Node, dest: Location, rope: ROPE, inherit: BOOL _ TRUE, looks: Looks _ noLooks, charSet: CharSet _ 0, event: Event _ NIL] RETURNS [result: Text] = { RETURN ReplaceByRope[root: root, dest: [dest.node, dest.where, 0], rope: rope, inherit: inherit, looks: looks, charSet: charSet, event: event] }; AppendRope: PUBLIC PROC [root: Node, dest: Node, rope: ROPE, inherit: BOOL _ TRUE, looks: Looks _ noLooks, charSet: CharSet _ 0, event: Event _ NIL] RETURNS [result: Text] = { RETURN ReplaceByRope[root: root, dest: [dest, maxLen], rope: rope, inherit: inherit, looks: looks, charSet: charSet, event: event] }; ReplaceByChar: PUBLIC PROC [root: Node, dest: Text, char: CHAR, inherit: BOOL _ TRUE, looks: Looks _ noLooks, charSet: CharSet _ 0, event: Event _ NIL] RETURNS [result: Text] = { RETURN ReplaceByRope[root: root, dest: dest, rope: Rope.FromChar[char], inherit: inherit, looks: looks, charSet: charSet, event: event]; }; InsertChar: PUBLIC PROC [root: Node, dest: Location, char: CHAR, inherit: BOOL _ TRUE, looks: Looks _ noLooks, charSet: CharSet _ 0, event: Event _ NIL] RETURNS [result: Text] = { RETURN ReplaceByChar[root: root, dest: [dest.node, dest.where, 0], char: char, inherit: inherit, looks: looks, charSet: charSet, event: event] }; AppendChar: PUBLIC PROC [root: Node, dest: Node, char: CHAR, inherit: BOOL _ TRUE, looks: Looks _ noLooks, charSet: CharSet _ 0, event: Event _ NIL] RETURNS [result: Text] = { RETURN ReplaceByChar[root: root, dest: [dest, maxLen, 0], char: char, inherit: inherit, looks: looks, charSet: charSet, event: event] }; RopeFromString: PROC [string: String] RETURNS [rope: ROPE _ NIL] ~ { string _ ClipString[string]; IF string.len>0 THEN { i: NAT _ string.start; p: PROC RETURNS [CHAR] ~ { c: CHAR ~ string.text[i]; i _ i+1; RETURN[c] }; rope _ Rope.FromProc[string.len, p]; }; }; ReplaceByString: PUBLIC PROC [root: Node, dest: Text, string: String, inherit: BOOL _ TRUE, looks: Looks _ noLooks, charSet: CharSet _ 0, event: Event _ NIL] RETURNS [result: Text] = { RETURN ReplaceByRope[root: root, dest: dest, rope: RopeFromString[string], inherit: inherit, looks: looks, charSet: charSet, event: event]; }; InsertString: PUBLIC PROC [root: Node, dest: Location, string: String, inherit: BOOL _ TRUE, looks: Looks _ noLooks, charSet: CharSet _ 0, event: Event _ NIL] RETURNS [result: Text] = { RETURN ReplaceByString[root: root, dest: [dest.node, dest.where, 0], string: string, inherit: inherit, looks: looks, charSet: charSet, event: event] }; AppendString: PUBLIC PROC [root: Node, dest: Node, string: String, inherit: BOOL _ TRUE, looks: Looks _ noLooks, charSet: CharSet _ 0, event: Event _ NIL] RETURNS [result: Text] = { RETURN ReplaceByString[root: root, dest: [dest, maxLen, 0], string: string, inherit: inherit, looks: looks, charSet: charSet, event: event] }; ChangeLooks: PUBLIC PROC [root: Node, text: Text, remove, add: Looks, event: Event] = { size: INT ~ Size[text.node]; text _ ClipText[text, size]; IF text.len>0 THEN { oldRuns: Runs ~ text.node.runs; newRuns: Runs ~ TextLooks.ChangeLooks[oldRuns, size, remove, add, text.start, text.len]; IF newRuns=oldRuns THEN NULL -- no change ELSE { alreadysaved: BOOL ~ AlreadySaved[text.node, event]; notify: REF Change.ChangingText ~ IF alreadysaved THEN Alloc[] ELSE NEW[Change.ChangingText]; notify^ _ [ChangingText[root, text.node, text.start, text.len, text.len, text.node.rope, oldRuns, GetCharSets[text.node], GetCharProps[text.node]]]; EditNotify.Notify[notify, before]; text.node.runs _ newRuns; BumpCount[text.node]; EditNotify.Notify[notify, after]; IF alreadysaved THEN Free[notify] ELSE UndoEvent.Note[event, UndoChangeText, notify]; }; }; }; AddLooks: PUBLIC PROC [root: Node, text: Text, add: Looks, event: Event] = { ChangeLooks[root, text, noLooks, add, event] }; RemoveLooks: PUBLIC PROC [root: Node, text: Text, remove: Looks, event: Event] = { ChangeLooks[root, text, remove, noLooks, event] }; SetLooks: PUBLIC PROC [root: Node, text: Text, new: Looks, event: Event] = { ChangeLooks[root, text, allLooks, new, event] }; ClearLooks: PUBLIC PROC [root: Node, text: Text, event: Event] = { ChangeLooks[root, text, allLooks, noLooks, event] }; PropList: TYPE ~ Atom.PropList; GetCharProp: PUBLIC PROC [node: Node, index: INT, name: ATOM] RETURNS [value: REF] ~ { RETURN [GetPropFromList[GetCharPropList[node, index], name]]; }; PutCharProp: PUBLIC PROC [node: Node, index: INT, name: ATOM, value: REF, nChars: INT, event: Event, root: Node] ~ { size: INT ~ Size[node]; oldCharProps: ROSARY ~ GetCharProps[node]; p: PROC[q: PROC[REF, INT]] ~ { action: Rosary.RunActionType ~ { oldList: PropList ~ NARROW[item]; q[PutPropOnList[oldList, name, value], repeat]; }; RosaryMapRuns[oldCharProps, index, nChars, action]; }; replaceCharProps: ROSARY ~ Rosary.FromProcProc[p]; newCharProps: ROSARY ~ RosaryReplace[dest: oldCharProps, destSize: size, destStart: index, destLen: nChars, source: replaceCharProps, sourceStart: 0, sourceLen: nChars]; PutProp[node, $CharProps, newCharProps, event, root]; }; GetCharPropList: PUBLIC PROC [node: Node, index: INT] RETURNS [PropList] ~ { charProps: ROSARY ~ GetCharProps[node]; IF charProps=NIL THEN RETURN[NIL] ELSE RETURN[NARROW[Rosary.Fetch[charProps, index]]]; }; PutCharPropList: PUBLIC PROC [node: Node, index: INT, propList: PropList, nChars: INT, event: Event, root: Node] ~ { size: INT ~ Size[node]; oldCharProps: ROSARY ~ GetCharProps[node]; replaceCharProps: ROSARY ~ Rosary.FromItem[propList, nChars]; newCharProps: ROSARY ~ RosaryReplace[dest: oldCharProps, destSize: size, destStart: index, destLen: nChars, source: replaceCharProps, sourceStart: 0, sourceLen: nChars]; PutProp[node, $CharProps, newCharProps, event, root]; }; GetPropFromList: PUBLIC PROC [propList: PropList, key: ATOM] RETURNS [value: REF] ~ { FOR list: PropList _ propList, list.rest UNTIL list=NIL DO IF list.first.key=key THEN RETURN[list.first.val]; ENDLOOP; RETURN[NIL]; }; PutPropOnList: PUBLIC PROC [propList: PropList, key: ATOM, value: REF] RETURNS [new: PropList] ~ { RemoveKeyFrom: PROC [p: PropList] RETURNS [PropList] = { IF p = NIL THEN RETURN [NIL] ELSE IF p.first.key = key THEN RETURN [p.rest] ELSE { rest: PropList _ RemoveKeyFrom[p.rest]; IF rest # p.rest THEN RETURN [CONS[p.first, rest]] ELSE RETURN [p] } }; new _ RemoveKeyFrom[propList]; IF value # NIL THEN new _ CONS[NEW[Atom.DottedPairNode _ [key, value]], new]; }; MapCharProps: PUBLIC PROC [node: Node, index: INT, action: MapPropsAction] RETURNS [quit: BOOL] ~ { FOR list: PropList _ GetCharPropList[node, index], list.rest UNTIL list=NIL DO IF action[NARROW[list.first.key], list.first.val].quit THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; ModifyCharProps: PUBLIC PROC [node: Node, name: ATOM, index: INT, nChars: INT, action: ModifyPropsAction, event: Event, root: Node] RETURNS [quit: BOOL] ~ { prefixNIL: INT ~ INT.LAST; prefixSize: INT _ prefixNIL; oldCharProps: ROSARY ~ GetCharProps[node]; i: INT _ index; p: PROC[q: PROC[REF, INT]] ~ { runAction: Rosary.RunActionType ~ { oldList: PropList ~ NARROW[item]; oldValue: REF ~ GetPropFromList[oldList, name]; stop: BOOL _ FALSE; newValue: REF _ NIL; [stop, newValue] _ action[oldValue, i, repeat]; IF newValue # oldValue AND prefixSize = prefixNIL THEN prefixSize _ i; IF prefixSize # prefixNIL THEN { newList: PropList ~ IF newValue = oldValue THEN oldList ELSE PutPropOnList[oldList, name, newValue]; q[newList, repeat]; }; i _ i + repeat; IF stop THEN RETURN [TRUE]; }; RosaryMapRuns[oldCharProps, index, nChars, runAction]; }; replaceCharProps: ROSARY ~ Rosary.FromProcProc[p]; -- sets prefixSize and i as side effect IF prefixSize # prefixNIL THEN { replaceSize: INT ~ i-prefixSize; newCharProps: ROSARY ~ RosaryReplace[dest: oldCharProps, destSize: Size[node], destStart: prefixSize, destLen: replaceSize, source: replaceCharProps, sourceStart: 0, sourceLen: replaceSize]; IF Rosary.Size[replaceCharProps] # replaceSize THEN ERROR; PutProp[node, $CharProps, newCharProps, event, root]; }; }; ChangeFormat: PUBLIC PROC [node: Node, formatName: ATOM, event: Event _ NIL, root: Node _ NIL] = { notify: REF Change.ChangingFormat; oldFormatName: ATOM _ node.formatName; IF node=NIL THEN RETURN; IF root=NIL THEN root _ TextNode.Root[node]; IF oldFormatName = formatName THEN RETURN; -- no change notify _ NEW[Change.ChangingFormat _ [ChangingFormat[root, node, formatName, oldFormatName]]]; EditNotify.Notify[notify, before]; node.formatName _ formatName; EditNotify.Notify[notify, after]; IF event#NIL THEN UndoEvent.Note[event, UndoChangeFormat, notify]; }; UndoChangeFormat: PROC [undoRef: REF, currentEvent: Event] = { WITH undoRef SELECT FROM x: REF Change.ChangingFormat => { ChangeFormat[x.node, x.oldFormatName, currentEvent, x.root]; }; ENDCASE => ERROR; }; quote: ROPE ~ "\""; ChangeStyle: PUBLIC PROC [node: Node, name: ROPE, event: Event _ NIL, root: Node _ NIL] = { newprefix: ROPE _ NIL; IF node=NIL THEN RETURN; IF Rope.Size[name] > 0 THEN newprefix _ Rope.Cat[quote, name, quote, " style"]; PutProp[node, $Prefix, newprefix, event, root]; }; PutProp: PUBLIC PROC [node: Node, name: ATOM, value: REF, event: Event _ NIL, root: Node _ NIL] = { IF node=NIL THEN NULL ELSE { notify: REF Change.ChangingProp; oldval: REF ~ NodeProps.GetProp[node, name]; IF oldval=value THEN RETURN; IF root=NIL THEN root _ TextNode.Root[node]; notify _ NEW[Change.ChangingProp _ [ChangingProp[root, node, Atom.GetPName[name], name, value, oldval]]]; EditNotify.Notify[notify, before]; NodeProps.PutProp[node, name, value]; EditNotify.Notify[notify, after]; IF event#NIL THEN UndoEvent.Note[event, UndoPutProp, notify]; }; }; UndoPutProp: PROC [undoRef: REF Change, currentEvent: Event] = { WITH undoRef SELECT FROM x: REF Change.ChangingProp => PutProp[node: x.node, name: x.propAtom, value: x.oldval, event: currentEvent, root: x.root]; ENDCASE => ERROR; }; GetProp: PUBLIC PROC [node: Node, name: ATOM] RETURNS [value: REF] = { RETURN [NodeProps.GetProp[node, name]]; }; FromRope: PUBLIC PROC [rope: ROPE] RETURNS [new: Node] = { new _ TextNode.NewTextNode[]; new.rope _ rope; new.last _ TRUE; new.runs _ TextLooks.CreateRun[Rope.Size[new.rope]]; }; FromString: PUBLIC PROC [string: REF READONLY TEXT] RETURNS [new: Node] = { new _ TextNode.NewTextNode[]; new.last _ TRUE; new.rope _ Rope.FromRefText[string]; new.runs _ TextLooks.CreateRun[Rope.Size[new.rope]]; }; DocFromNode: PUBLIC PROC [child: Node] RETURNS [new: Node] = { new _ TextNode.NewTextNode[]; new.child _ child; new.last _ TRUE; child.next _ new; child.last _ TRUE; }; ChangeCaps: PUBLIC PROC [root: Node, dest: Text, how: CapChange, event: Event] = { notify: REF Change.ChangingText; oldRope, destRope: ROPE; rdr: RopeReader.Ref; initCap: BOOL _ TRUE; destSize, start, len: INT; alreadysaved: BOOL; destSize _ Rope.Size[oldRope _ destRope _ dest.node.rope]; dest _ ClipText[dest, destSize]; start _ dest.start; IF (len _ dest.len)=0 THEN RETURN; notify _ IF (alreadysaved _ AlreadySaved[dest.node, event]) THEN Alloc[] ELSE NEW[Change.ChangingText]; notify^ _ [ChangingText[root, dest.node, start, len, len, destRope, dest.node.runs]]; EditNotify.Notify[notify, before]; rdr _ RopeReader.GetRopeReader[]; UNTIL len=0 DO c: CHAR; maxText: NAT = 1000; num: NAT _ MIN[len, maxText]; new: REF TEXT _ 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: BOOL _ 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.ReplaceByTEXT[destRope, new, 0, num, start, num, destSize]; IF Rope.Size[destRope] # destSize THEN ERROR; len _ len-num; start _ start+num; ENDLOOP; dest.node.rope _ destRope; RopeReader.FreeRopeReader[rdr]; EditNotify.Notify[notify, after]; IF alreadysaved THEN Free[notify] ELSE UndoEvent.Note[event, UndoChangeText, notify]; }; AllCaps: PUBLIC PROC [root: Node, dest: Text, event: Event] = { ChangeCaps[root, dest, allCaps, event] }; AllLower: PUBLIC PROC [root: Node, dest: Text, event: Event] = { ChangeCaps[root, dest, allLower, event] }; InitialCaps: PUBLIC PROC [root: Node, dest: Text, event: Event] = { ChangeCaps[root, dest, initCaps, event] }; BumpCount: PROC [text: Node] = { countLimit: INT _ 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; }; debug: BOOL _ FALSE; Flatten: PUBLIC PROC [text: Node] RETURNS [BOOL] = { looksSize, looksPieces, looksDepth: INT _ 0; ropeFlag, looksFlag: BOOL _ FALSE; rope: ROPE _ text.rope; runs: Runs _ text.runs; ropeSize: INT ~ Rope.Size[rope]; [looksSize, looksPieces, looksDepth] _ TextLooks.LooksStats[runs]; IF ropeSize=0 AND looksSize=0 THEN RETURN [FALSE]; 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 Rope.Balance[rope] ELSE Rope.Flatten[rope]; text.runs _ TextLooks.Flatten[runs]; IF debug THEN { CheckNode.CheckRope[text.rope]; CheckNode.CheckRuns[text.runs] }; RETURN [TRUE]; }; RETURN [FALSE]; }; maxSearchDepth: NAT _ 20; AlreadySaved: PUBLIC PROC [text: Node, event: Event] RETURNS [BOOL] = { k: NAT _ maxSearchDepth; IF event = NIL THEN RETURN [TRUE]; FOR l: UndoEvent.SubEvent _ event.subevents, l.next UNTIL l=NIL DO undoRef: REF Change ~ l.undoRef; WITH undoRef SELECT FROM x: REF Change.ChangingText => IF x.text = text THEN RETURN[TRUE]; ENDCASE; IF (k _ k-1) = 0 THEN EXIT; ENDLOOP; RETURN [FALSE]; }; END. $TempTextEditImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. 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 Michael Plass, April 1, 1985 4:14:29 pm PST Doug Wyatt, September 9, 1986 5:18:44 pm PDT Cache of notify records Fetch info operations -- returns the looks for the character at the given location -- use reader's if getting looks for sequence of locations Text Editing Operations must replace all of text since only save first change per event -- replace the dest text by a copy of the source text -- addrs that are in the replaced text move to destStart -- addrs that are after the replaced text are adjusted Move empty source to destination. Move addr to new location and pin so it won't be changed by Replace. Source is already in the right place. Move toward start of node: Copy has shifted the source to the right. Move toward end of node: Delete will shift the result to the left. Try to find a matching item in destCharSets adjacent to the replacement. Operation to add or delete looks -- first remove then add in the given range Character Properties Note: Character properties are implemented using a ROSARY (an immutable sequence of REFs; see Rosary.mesa), appearing in the CharProps property of the node. The values in this rosary are of type Atom.PropList, but don't use Atom.PutPropOnList, since we regard these lists as immutable. The values on the property lists are externalized and internalized using the procedures registered with NodeProps, but no warranty is expressed or implied for props that are not immutable. This property list mechanism regards a PropList as an immutable value. We don't use the procedures from Atom since 1) they might smash an existing PropList, and 2) they hold a global monitor in AtomImpl. Changing Style / Format of node Operations to create a text node -- create a text node with looks from a normal rope -- copies the contents of the string Caps 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 Don't keep searching too long; give up and make a new event instead. - mfp Ê$Ù˜codešœ™Kšœ Ïmœ7™BKšœ"™"Kšœ4™4K™0K™+K™,—K™šÏk ˜ Kšœžœ&˜0Kšœ žœ˜'Kšœ žœ˜"Kšœ žœ>˜MKšœ žœ˜#Kšœžœ^žœ˜|Kšœ žœ9˜GKšœ žœA˜QKšœžœ7žœ˜`Kšœ žœL˜ZKšœ žœ‚˜‘Kšœ žœ/˜=Kšœ žœ˜(—K˜KšÐbl œžœž˜Kšžœ€˜‡Kšžœ˜šž˜K˜Kšœžœžœ˜(K˜Kšœžœ˜Kšžœžœžœ˜Kšžœžœ žœ˜Kšœžœ˜Kšœžœ˜K˜#K˜%Kšœžœ˜Kšœ žœ˜%Kšœžœ˜/Kšœžœ˜5Kšœ žœ˜!Kšœ žœ˜#Kšœžœ˜Kšœžœ˜Kšœžœžœžœ˜Kš œ žœžœžœžœ˜#—headšœ™šœžœžœ˜K˜CK˜—šžœžœž˜šœ¡˜$šžœžœž˜šœ¡˜&K˜-K˜GKšœ žœ˜Kšœ˜—šœ¡˜+K˜-K˜GKšœ žœ˜Kšœ˜—Kšžœ˜——šœ¡˜$šžœžœž˜šœ¡˜(K˜0K˜5Kšœ žœ˜Kšœ˜—šœ¡˜+K˜)K˜.Kšœ žœ˜Kšœ˜—Kšžœ˜—Kšœ˜—Kšžœ˜—šžœžœ žœ˜K•StartOfExpansionM[base: ROPE, start: INT _ 0, len: INT _ 2147483647, with: ROPE _ NIL]˜\K–†[base: TextLooks.Runs, start: INT, len: INT, replace: TextLooks.Runs, baseSize: INT, repSize: INT, tryFlat: BOOL _ TRUE]˜‹K˜—K˜K˜š žœžœžœžœžœ˜0Kšœ žœ«˜¾Kšœ5˜5Kšœ˜—š žœžœžœžœžœ˜2Kšœžœ­˜ÁKšœ7˜7Kšœ˜—K˜>K˜K˜!šžœž˜Kšœ ˜ Kšžœ/˜3—Kšžœ&˜,Kšœ˜K˜—š œžœ žœ!˜Cšžœ žœž˜šœžœ˜Kšœ?™?šœ˜Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜—Kšœ˜—Kšžœžœ˜—Kšœ˜K˜—š œžœ(žœ˜PKšžœ žœžœ˜šžœžœ ˜Kšœ ˜ Kšœ ˜ Kšœ žœ˜Kšœ žœ˜Kšœžœ˜Kšœžœ˜Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ˜—Kšœ˜K˜—K˜š  œžœžœ+˜BKšœ'˜'Kšžœ žœžœžœ"˜>Kšœ˜K˜—š  œžœžœFžœ˜xKšœ5™5Kšœ8™8Kšœ6™6Kšœ'˜'Kšœ-˜-šžœ ˜Kšžœžœ ˜+šžœžœ ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ)˜)Kšœ+˜+Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜——˜K˜——š œžœžœJžœ˜yKšžœy˜˜K˜——š œžœžœJžœ˜yKšœ+˜+Kšœ-˜-šžœžœžœ˜9K™!—šžœ˜šžœ¡!˜(š œžœžœ žœžœžœžœ˜QKšœD™Dšžœ žœžœ˜0Kšœžœ$˜,Kšžœžœ¡˜PKšœDžœ˜JK˜—Kšœ˜—šžœ žœžœžœ ˜AK™%—Kšœ8˜8Kšœ=˜=šžœ˜šžœ'˜+Kšœ ¡ œ+™D—šžœ&˜*Kšœ ¡ œ+™B——Kšœ&˜&Kšœ˜K˜—šžœ¡%˜,š  œžœžœ žœžœžœžœ˜Kšžœ žœžœ˜0Kšœžœ$˜,KšœT˜TK˜—Kšœ˜—Kšœ4˜4Kšœ=˜=Kšœ&˜&K˜——Kšœ˜K˜—š  œžœžœDžœ$˜‰Kšœ žœžœ˜Kšœ*˜*Kšœ'˜'šžœ˜šžœ¡!˜(Kšœ-˜-K˜0šžœžœžœ˜MKšžœžœ¡!˜+Kšžœ=žœ˜H—šžœžœž˜šœ¡ ˜%K˜DK˜6Kšœ˜—šœ¡˜(šžœ˜Kšžœ,¡˜Fšžœ¡8˜?K˜>K˜7Kšœ;˜;K˜:K˜+K˜——Kšœ˜—šžœ¡ ˜KšœC˜CKšœ#¡,˜OKšœD˜DK˜——Kšœ˜—šžœ¡(˜/KšœS˜SKšœ#¡2˜UKšœS˜SK˜——Kšžœ žœžœ˜1Kšœ˜K˜—š  œžœžœ@žœ˜sK˜Ušžœžœ¡˜?Kšœ.˜.Kšœ.˜.šžœžœ¡#˜AKšœ žœžœ˜.Kšœ žœ˜$Kšœ6˜6K˜—šžœžœ¡$˜FKšœ žœ˜Kšœ žœžœ%˜9Kšœ8˜8Kšœ'¡,˜SK˜—Kšžœ žœžœ˜?KšžœB˜FK˜—šžœ¡˜K˜"KšœP˜PK˜—˜K˜——K˜š œžœ!žœžœ žœžœžœ˜‹Kšžœ žœžœžœ˜šžœ˜Kšœžœ žœ˜šžœžœžœ˜KšœH™HKšœžœ˜Kšœžœžœžœ3˜Jš žœ žœžœžœž˜%Kšœžœ˜š žœžœžœžœžœž˜IKšœ žœ žœžœ˜CKšžœ˜—Kšžœ˜—Kšœ˜—Kšžœžœžœžœ˜/Kšžœ&˜,K˜—Kšœ˜K˜—š   œžœžœ žœ žœ0žœ˜—Kšœ žœ˜ Kšœ žœ˜ K˜ šžœ žœ žœ˜"Kšœ žœ˜ Kšœ ˜ Kšœžœ˜.Kšœžœ˜0Kšœžœ˜4šœžœ˜#Kšžœžœ˜Kšžœžœ˜Kšœ˜—šœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜"K˜\K˜lK˜=K˜šžœžœ žœ˜,Kš œžœžœ žœžœžœ—˜ÊKšœ žœ ˜³Kšœ5˜5Kšœ˜—šžœžœ˜ Kšœžœlžœ'˜ªKšœ7˜7Kšœ˜—K˜!Kšžœžœ ˜!Kšžœ/˜3K˜—Kšžœ%˜+Kšœ˜K˜—š  œžœžœ$žœ žœžœ?žœžœ˜°Kšœžœ‹˜•K˜—š  œžœžœ žœ žœžœ?žœžœ˜¬Kšœžœ˜‰K˜—š  œžœžœ žœ žœžœ?žœžœ˜²Kšžœ‚˜ˆKšœ˜K˜—š  œžœžœ$žœ žœžœ?žœžœ˜°Kšœžœ‹˜•K˜—š  œžœžœ žœ žœžœ?žœžœ˜¬Kšœžœ‚˜ŒK˜—š  œžœžœžœžœ˜DK˜šžœžœ˜Kšœžœ˜Kš œžœžœžœ žœžœ˜JK˜$K˜—K˜K˜—š œžœžœ4žœžœ?žœžœ˜¹Kšžœ…˜‹Kšœ˜K˜—š  œžœžœ8žœžœ?žœžœ˜¶Kšœžœ‘˜›K˜—š  œžœžœ4žœžœ?žœžœ˜²Kšœžœˆ˜’K˜——šœ ™ š  œžœžœ?˜WKšœ+™+Kšœžœ˜K˜šžœ žœ˜K˜K˜YKšžœžœžœ¡ ˜)šžœ˜Kšœžœ"˜4Kš œžœžœžœ žœžœ˜]K˜”K˜"K˜K˜K˜!Kšžœžœ ˜!Kšžœ/˜3Kšœ˜—K˜—K˜K˜—š œžœžœ3˜HKšœ3˜3K˜—š  œžœžœ6˜NKšœ6˜6K˜—š œžœžœ3˜HKšœ4˜4K˜—š  œžœžœ'˜>Kšœ8˜8K˜K˜——šœ™šœ3žœ£™ÜK˜—KšœÌ™ÌK™Kšœ žœ˜K™š  œžœžœžœžœžœ žœ˜VKšžœ7˜=Kšœ˜K˜—š  œžœžœžœžœ žœ žœ˜tKšœžœ˜Kšœžœ˜*š œžœžœžœžœ˜šœ ˜ Kšœžœ˜!Kšœ/˜/Kšœ˜—Kšœ3˜3Kšœ˜—Kšœžœ˜2Kšœžœ•˜©Kšœ5˜5Kšœ˜K˜—š  œžœžœžœžœ˜LKšœ žœ˜'Kš žœ žœžœžœžœ˜!Kšžœžœžœ"˜4Kšœ˜K˜—š  œžœžœžœžœ˜tKšœžœ˜Kšœžœ˜*Kšœžœ%˜=Kšœžœ•˜©Kšœ5˜5Kšœ˜K˜—š  œžœžœžœžœ žœ˜Ušžœ&žœžœž˜:Kšžœžœžœ˜2Kšžœ˜—Kšžœžœ˜ K˜K˜—š   œžœžœžœ žœžœ˜bš  œžœžœ˜8Kš žœžœžœžœžœ˜Kšžœžœžœžœ ˜.šžœ˜Kšœ'˜'Kšžœžœžœžœ˜2Kšžœžœ˜K˜—Kšœ˜—Kšœ˜Kš žœ žœžœžœžœ+˜MK˜K˜—š   œžœžœžœžœžœ˜cšžœ:žœžœž˜NKš žœžœ'žœžœžœ˜IKšžœ˜—Kšžœžœ˜Kšœ˜K˜—š œžœžœžœ žœ žœ7žœžœ˜œKšœ žœžœžœ˜Kšœ žœ ˜Kšœžœ˜*Kšœžœ ˜š œžœžœžœžœ˜šœ#˜#Kšœžœ˜!Kšœ žœ"˜/Kš œžœžœ žœžœ˜(Kšœ/˜/Kšžœžœžœ˜Fšžœžœ˜ Kšœžœžœ žœ(˜dKšœ˜Kšœ˜—Kšœ˜Kšžœžœžœžœ˜Kšœ˜—Kšœ6˜6Kšœ˜—Kšœžœ¡'˜Zšžœžœ˜ Kšœ žœ˜ Kšœžœª˜¾Kšžœ-žœžœ˜:Kšœ5˜5Kšœ˜—Kšœ˜K˜——šœ™š   œžœžœžœžœžœ˜bKšœžœ˜"Kšœžœ˜&Kšžœžœžœžœ˜Kšžœžœžœ˜,Kšžœžœžœ¡ ˜7šœ žœ˜$K˜9—K˜"K˜K˜!Kšžœžœžœ1˜BKšœ˜K˜—š œžœ žœ˜>šžœ žœž˜šœžœ˜!Kšœ<˜K˜Kšœžœ˜#Kšœžœ˜$Kšœ˜K˜——šœ™š  œžœžœ;˜RKšœžœ˜ Kšœžœ˜Kšœ˜Kšœ žœžœ˜Kšœžœ˜Kšœžœ˜K˜:K˜ Kšœ˜Kšžœžœžœ˜"šœ žœ1žœ˜HKšžœžœ˜—K˜UK˜"K˜!šžœž˜Kšœžœ˜Kšœ žœ˜Kšœžœžœ˜Kš œžœžœžœžœ˜K˜-Kšžœž˜šœ žœ žœ˜K˜1šžœžœžœ ž˜K˜1Kšžœ˜ ——šœ žœžœžœ ž˜#K˜1Kšžœ˜—šœ žœžœžœ ž˜$K˜1Kšžœ˜—˜ Kšœžœžœ˜šžœžœžœ ž˜šžœž˜#šžœ˜Kšœ žœžœžœ¡˜8Kšœžœ˜—šžœ˜Kšœ žœžœžœ¡˜9Kšœžœ˜—šžœ˜ K˜ Kšœžœ!˜3——Kšžœ˜ ——Kšžœžœ˜K˜K˜KšœO˜OKšžœ žœžœ˜-K˜!Kšžœ˜—K˜K˜K˜!Kšžœžœ ˜!Kšžœ/˜3Kšœ˜K˜—š œžœžœ'˜;Kšœ-˜-K˜—š œžœžœ'˜