DIRECTORY Atom USING [GetPName, MakeAtom], Checksum USING [ComputeChecksum], Convert USING [AppendInt, RopeFromInt], EditNotify USING [AddNotifyProc, Change, ChangeSet], Imager USING [Error], ImagerFont USING [Find, Font, Name, Scale], IO USING [PutFR], MessageWindow USING [Append], NodeProps USING [CopyInfoProc, GetProp, Is, Register], NodeStyle USING [DataEntry, DataList, FontAlphabets, FontFace, GetStyleName, IntegerValue, MaxNestingLevel, RealCode, Ref, SetReal, StyleBody], NodeStyleFont USING [], NodeStyleOps USING [ExtObjPair, LocalStyle, LocalStyleRec, OfStyle], NodeStyleWorks USING [BadStyleFile, ExecuteLooksInStyle, ExecuteNameInStyle, ExecuteObjectInStyle, ForceLowerName, ForceLowerRope, FreeFrame, GetFrame, GetStyleDict, RunStyle, StyleError, StyleParamKey, Where, WhoIsExecuting], Real USING [Round], RefText USING [Append, AppendChar, AppendRope, AppendTextRope, ObtainScratch, ReleaseScratch], Rope USING [Cat, Concat, Equal, Find, FromRefText, Match, ROPE, Size], TextLooks USING [Looks, noLooks], TextNode USING [FirstChild, Level, LocNumber, Parent, Ref], TJaM USING [Frame, NumberRep, Object, Push, PushRope, Stop, TryToLoad], VFonts USING [DefaultFont]; NodeStyleOpsImpl: CEDAR MONITOR IMPORTS Atom, Checksum, Convert, EditNotify, Imager, ImagerFont, IO, MessageWindow, NodeProps, NodeStyle, NodeStyleWorks, Real, RefText, Rope, TextNode, TJaM, VFonts EXPORTS NodeStyleFont, NodeStyleOps ~ BEGIN OPEN NodeStyle, NodeStyleOps; ROPE: TYPE ~ Rope.ROPE; Frame: TYPE ~ TJaM.Frame; Object: TYPE ~ TJaM.Object; Font: TYPE ~ ImagerFont.Font; Create: PUBLIC PROC RETURNS [Ref] ~ { RETURN [NEW[StyleBody]]; }; Copy: PUBLIC PROC [dest, source: Ref] ~ { dest^ _ source^; }; s1, s2, s3: Ref _ NIL; -- the small cache of style refs Alloc: PUBLIC ENTRY PROC RETURNS [s: Ref] ~ { ENABLE UNWIND => NULL; SELECT TRUE FROM s3 # NIL => { s _ s3; s3 _ NIL }; s2 # NIL => { s _ s2; s2 _ NIL }; s1 # NIL => { s _ s1; s1 _ NIL }; ENDCASE => { s _ Create[] }; }; Free: PUBLIC ENTRY PROC [s: Ref] ~ { ENABLE UNWIND => NULL; SELECT TRUE FROM s3 # NIL => { s3 _ s }; s2 # NIL => { s2 _ s }; s1 # NIL => { s1 _ s }; ENDCASE => NULL; }; LoadStyle: PUBLIC PROC [name: ATOM] RETURNS [ok: BOOL] ~ { frame: Frame _ NodeStyleWorks.GetFrame[NIL, NIL, screen]; [] _ NodeStyleWorks.GetStyleDict[frame, name, screen]; NodeStyleWorks.FreeFrame[frame, NIL, screen]; RETURN [TRUE]; }; DefineStyle: PUBLIC PROC [name: ATOM, def: ROPE] RETURNS [ok: BOOL] ~ { frame: Frame _ NodeStyleWorks.GetFrame[NIL, NIL, screen]; IF def = NIL THEN NodeStyleWorks.BadStyleFile[frame, name] ELSE { [] _ NodeStyleWorks.GetStyleDict[frame, name, screen, def]; NodeStyleWorks.FreeFrame[frame, NIL, screen]; }; RETURN [TRUE]; }; ReloadStyle: PUBLIC PROC [name: ATOM] RETURNS [ok: BOOL] ~ { ForceLowerName: PROC [name: ATOM] RETURNS [ATOM] ~ { RETURN [Atom.MakeAtom[NodeStyleWorks.ForceLowerRope[Atom.GetPName[name]]]]; }; name _ NodeStyleWorks.ForceLowerName[name]; FOR kind: NodeStyleOps.OfStyle IN NodeStyleOps.OfStyle DO frame: Frame _ NodeStyleWorks.GetFrame[NIL, NIL, kind]; ok _ NodeStyleWorks.RunStyle[frame, name]; IF ok THEN NodeStyleWorks.FreeFrame[frame, NIL, kind] ELSE NodeStyleWorks.BadStyleFile[frame, name]; ENDLOOP; }; defaultStyleName: PUBLIC ATOM; defaultStylesForExtensions: PUBLIC LIST OF ExtObjPair; SetDefaultStyle: PUBLIC PROC [name: ROPE] ~ { defaultStyleName _ Atom.MakeAtom[NodeStyleWorks.ForceLowerRope[name]]; defaultStyle.name[style] _ defaultStyleName; FlushCaches[]; }; SetExtensionStyles: PUBLIC PROC [value: LIST OF ROPE] ~ { defaultStylesForExtensions _ NIL; UNTIL value=NIL OR value.rest=NIL DO ext: ATOM _ Atom.MakeAtom[NodeStyleWorks.ForceLowerRope[value.first]]; -- the extension styleObject: Object _ Rope.Cat["\"", NodeStyleWorks.ForceLowerRope[value.rest.first], "\" style"]; defaultStylesForExtensions _ CONS[[ext, styleObject], defaultStylesForExtensions]; value _ value.rest.rest; ENDLOOP; FlushCaches[]; }; localStyleNumber: INT _ 0; ReadSpecs: PROC [name: ATOM, specs: ROPE] RETURNS [value: REF] ~ { GenLocalName: ENTRY PROC RETURNS [gen: ROPE] ~ { localStyleNumber _ localStyleNumber + 1; gen _ Rope.Concat["LocalStyle-", Convert.RopeFromInt[localStyleNumber]]; }; localStyle: LocalStyle _ NEW[LocalStyleRec]; localStyleName: ROPE ~ GenLocalName[]; localStyle.name _ Atom.MakeAtom[localStyleName]; localStyle.def _ specs; [] _ DefineStyle[localStyle.name, specs]; RETURN [localStyle]; }; WriteSpecs: PROC [name: ATOM, value: REF] RETURNS [specs: ROPE] ~ { localStyle: LocalStyle _ NARROW[value]; RETURN [IF localStyle=NIL THEN NIL ELSE localStyle.def]; }; CopyInfoProc: PROC [name: ATOM, value: REF] RETURNS [new: REF] ~ { RETURN [value] }; defaultStyle: PUBLIC Ref _ NIL; rootFormatName: ATOM; defaultFormatName: ATOM; ApplyAll: PUBLIC PROC [ref: Ref, node: TextNode.Ref, kind: OfStyle _ screen] ~ { [] _ DoApplyAll[ref, node, kind]; }; DoApplyAll: PROC [ref: Ref, node: TextNode.Ref, kind: OfStyle] RETURNS [depth: CARDINAL] ~ { found: BOOL; parent: TextNode.Ref; alt: ATOM; IF node = NIL THEN { ref^ _ defaultStyle^; RETURN [0] }; [found, depth] _ FindInApplyAllCache[ref, node, kind]; IF found THEN RETURN [depth+1]; parent _ TextNode.Parent[node]; alt _ IF parent=NIL THEN rootFormatName ELSE defaultFormatName; depth _ DoApplyAll[ref, parent, kind]; ApplyForNode[ref, node, alt, kind]; EnterInApplyAllCache[ref, node, depth]; RETURN [depth+1]; }; ApplyForNode: PUBLIC PROC [ref: Ref, node: TextNode.Ref, alt: ATOM, kind: OfStyle] ~ { ENABLE NodeStyleWorks.Where => { loc1: INT ~ TextNode.LocNumber[at: [node, 0], skipCommentNodes: FALSE]; loc2: INT ~ TextNode.LocNumber[at: [node, Rope.Size[node.rope]], skipCommentNodes: FALSE]; msg: ROPE ~ IO.PutFR["%g..%g", [integer[loc1]], [integer[loc2]]]; RESUME[msg]; }; ext: ATOM; ref.isComment _ IF node # NIL THEN node.comment ELSE FALSE; ref.print _ (kind = print); ref.nestingLevel _ MIN[TextNode.Level[node], MaxNestingLevel]; IF node.hasstyledef THEN { localStyle: LocalStyle _ NARROW[NodeProps.GetProp[node, $StyleDef]]; IF localStyle # NIL THEN ref.name[style] _ localStyle.name; }; IF node.hasprefix THEN { ApplyObject[ref, NodeProps.GetProp[node, $Prefix], kind ! NodeStyleWorks.WhoIsExecuting => {RESUME[$Prefix]}]; } ELSE IF ref.nestingLevel=0 -- root node -- AND -- check for file extension default (ext _ NARROW[NodeProps.GetProp[node, $FileExtension]]) # NIL THEN FOR list: LIST OF ExtObjPair _ defaultStylesForExtensions, list.rest UNTIL list = NIL DO IF list.first.fileExtension # ext THEN LOOP; ApplyObject[ref, list.first.styleObject, kind]; EXIT; ENDLOOP; ApplyFormat[ref, node.formatName, alt, kind]; IF node.haspostfix THEN { ApplyObject[ref, NodeProps.GetProp[node, $Postfix], kind ! NodeStyleWorks.WhoIsExecuting => {RESUME[$Postfix]}]; }; }; ac: REF ApplyCacheRecord _ NEW[ApplyCacheRecord]; ApplyCacheRecord: TYPE ~ RECORD [ applyCacheDepth: CARDINAL _ 0, -- next free entry applyCacheResults: REF ApplyCacheResults, applyCacheNodes: REF ApplyCacheNodes, applyCacheProbes, applyCacheHits, applyCacheSaves: INT _ 0 ]; applyCacheSize: CARDINAL ~ 8; -- number of levels deep in tree ApplyCacheNodes: TYPE ~ ARRAY [0..applyCacheSize) OF TextNode.Ref; ApplyCacheResults: TYPE ~ ARRAY [0..applyCacheSize) OF StyleBody; InitApplyCacheRecord: PROC ~ { ac.applyCacheResults _ NEW[ApplyCacheResults]; ac.applyCacheNodes _ NEW[ApplyCacheNodes]; }; RemoveAllFromApplyAllCache: PUBLIC PROC ~ { FlushApplyAllCache[] }; FlushApplyAllCache: PUBLIC ENTRY PROC [init: BOOL _ FALSE] ~ { ENABLE UNWIND => NULL; ClearApplyAllCache[init]; }; ClearApplyAllCache: PROC [init: BOOL] ~ { nodes: REF ApplyCacheNodes _ ac.applyCacheNodes; FOR i: CARDINAL IN [0..applyCacheSize) DO nodes[i] _ NIL; ENDLOOP; ac.applyCacheDepth _ 0; }; RemoveNodeFromApplyAllCache: PUBLIC ENTRY PROC [node: TextNode.Ref] ~ { ENABLE UNWIND => NULL; nodes: REF ApplyCacheNodes _ ac.applyCacheNodes; FOR i: CARDINAL IN [0..ac.applyCacheDepth) DO IF nodes[i]=node THEN { -- clear from here on FOR j: CARDINAL IN [i..applyCacheSize) DO nodes[j] _ NIL; ENDLOOP; ac.applyCacheDepth _ i; EXIT }; ENDLOOP; }; FindInApplyAllCache: ENTRY PROC [ref: Ref, node: TextNode.Ref, kind: OfStyle] RETURNS [found: BOOL, depth: CARDINAL] ~ { ENABLE UNWIND => NULL; nodes: REF ApplyCacheNodes _ ac.applyCacheNodes; print: BOOL ~ (kind=print); -- if true, then find result with print true also ac.applyCacheProbes _ ac.applyCacheProbes+1; FOR i: CARDINAL DECREASING IN [0..ac.applyCacheDepth) DO IF nodes[i]=node AND print=ac.applyCacheResults[i].print THEN { -- found it ac.applyCacheHits _ ac.applyCacheHits+1; ac.applyCacheSaves _ ac.applyCacheSaves+i+1; ref^ _ ac.applyCacheResults[i]; RETURN [TRUE, i] }; ENDLOOP; RETURN [FALSE, 0]; }; EnterInApplyAllCache: ENTRY PROC [ref: Ref, node: TextNode.Ref, depth: CARDINAL] ~ { ENABLE UNWIND => NULL; nodes: REF ApplyCacheNodes _ ac.applyCacheNodes; IF depth >= applyCacheSize THEN RETURN; nodes[depth] _ node; ac.applyCacheResults[depth] _ ref^; FOR i: CARDINAL IN [depth+1..applyCacheSize) DO nodes[i] _ NIL; ENDLOOP; ac.applyCacheDepth _ depth+1; }; Change: TYPE ~ EditNotify.Change; Notify: PROC [change: REF READONLY Change] ~ { DoNode: PROC [node: TextNode.Ref] ~ { IF TextNode.FirstChild[node] # NIL THEN FlushApplyAllCache[] ELSE RemoveNodeFromApplyAllCache[node] }; WITH change SELECT FROM x: REF READONLY Change.InsertingNode => IF TextNode.FirstChild[x.new] # NIL THEN FlushApplyAllCache[]; x: REF READONLY Change.MovingNodes => FlushApplyAllCache[]; x: REF READONLY Change.NodeNesting => FlushApplyAllCache[]; x: REF READONLY Change.ChangingFormat => DoNode[x.node]; x: REF READONLY Change.ChangingProp => { IF NodeProps.Is[x.propAtom, $Visible] THEN DoNode[x.node]; }; ENDCASE => ERROR; -- not expecting notify for any other kinds of changes }; warnDefault: BOOL _ FALSE; -- set this to TRUE to see warnings about missing format definitons ApplyFormat: PUBLIC PROC [ref: Ref, name, alt: ATOM, kind: OfStyle] ~ { names: REF RuleCacheNames _ rc.ruleCacheNames; inputs: REF RuleCacheBodies _ rc.ruleCacheInputs; input: StyleBody; initloc, loc: CARDINAL; FindInRuleCache: ENTRY PROC RETURNS [BOOL] ~ { ENABLE UNWIND => NULL; rc.ruleCacheProbes _ rc.ruleCacheProbes+1; DO -- search cache SELECT names[loc] FROM name => IF inputs[loc] = ref^ THEN { ref^ _ rc.ruleCacheResults[loc]; rc.ruleCacheHits _ rc.ruleCacheHits+1; RETURN [TRUE] }; NIL => RETURN [FALSE]; -- this is an unused entry ENDCASE; SELECT (loc _ loc+1) FROM ruleCacheSize => IF (loc _ 0)=initloc THEN RETURN [FALSE]; initloc => RETURN [FALSE]; ENDCASE; ENDLOOP; }; PutInRuleCache: ENTRY PROC ~ { ENABLE UNWIND => NULL; IF rc.ruleCacheCount = ruleCacheMax THEN ClearRuleCache[]; loc _ initloc; DO -- search cache for place to put the entry SELECT names[loc] FROM name => IF inputs[loc] = input THEN RETURN; -- already in cache NIL => EXIT; -- this is an unused entry ENDCASE; SELECT (loc _ loc+1) FROM ruleCacheSize => IF (loc _ 0) = initloc THEN ERROR; -- cache full initloc => ERROR; -- cache full ENDCASE; ENDLOOP; rc.ruleCacheCount _ rc.ruleCacheCount+1; inputs[loc] _ input; rc.ruleCacheResults[loc] _ ref^; names[loc] _ name; }; IF name = NIL AND (name _ alt) = NIL THEN RETURN; loc _ initloc _ HashStyle[ref, , name] MOD ruleCacheSize; IF FindInRuleCache[] THEN RETURN; input _ ref^; -- save the input value of the record IF NodeStyleWorks.ExecuteNameInStyle[ref, kind, name] THEN PutInRuleCache[] ELSE IF name # alt THEN { ApplyFormat[ref, alt, NIL, kind]; IF warnDefault THEN { frame: TJaM.Frame ~ NodeStyleWorks.GetFrame[ref, NodeStyle.GetStyleName[ref], kind]; TJaM.Push[frame, alt]; TJaM.PushRope[frame, "format used instead of"]; TJaM.Push[frame, name]; NodeStyleWorks.StyleError[frame, 3 ! TJaM.Stop => CONTINUE]; NodeStyleWorks.FreeFrame[frame: frame, styleName: NodeStyle.GetStyleName[ref], kind: kind]; }; }; }; rc: REF RuleCacheInfoRecord _ NEW[RuleCacheInfoRecord]; RuleCacheInfoRecord: TYPE ~ RECORD [ ruleCacheCount: CARDINAL _ 0, -- number of entries currently in use ruleCacheNames: REF RuleCacheNames, ruleCacheInputs: REF RuleCacheBodies, ruleCacheResults: REF RuleCacheBodies, ruleCacheProbes, ruleCacheHits: INT _ 0 ]; ruleCacheSize: CARDINAL ~ 64; -- should be a power of 2 ruleCacheMax: CARDINAL ~ (ruleCacheSize*4)/5; -- don't fill too full RuleCacheNames: TYPE ~ ARRAY [0..ruleCacheSize) OF ATOM; RuleCacheBodies: TYPE ~ ARRAY [0..ruleCacheSize) OF StyleBody; InitRuleCacheInfo: PROC ~ { rc.ruleCacheNames _ NEW[RuleCacheNames]; rc.ruleCacheInputs _ NEW[RuleCacheBodies]; rc.ruleCacheResults _ NEW[RuleCacheBodies]; }; FlushRuleCache: ENTRY PROC [init: BOOL _ FALSE] ~ { ENABLE UNWIND => NULL; ClearRuleCache[]; }; ClearRuleCache: PROC [init: BOOL _ FALSE] ~ { names: REF RuleCacheNames _ rc.ruleCacheNames; IF NOT init AND rc.ruleCacheCount = 0 THEN RETURN; rc.ruleCacheCount _ 0; FOR i: CARDINAL IN [0..ruleCacheSize) DO names[i] _ NIL; ENDLOOP; }; HashStyle: PROC [ref: Ref, looks: TextLooks.Looks _ TextLooks.noLooks, anotherRef: REF _ NIL] RETURNS [CARDINAL] ~ TRUSTED { Bits: TYPE ~ MACHINE DEPENDENT RECORD [ REF, REF, REF, REF, RealCode, RealCode, RealCode, RealCode, TextLooks.Looks]; bits: Bits _ [ref.name[style], ref.name[fontPrefix], ref.name[fontFamily], anotherRef, ref.real[fontSize], ref.real[leftIndent], ref.real[leading], 0, looks]; RETURN [Checksum.ComputeChecksum[3145, SIZE[Bits], @bits]]; }; ApplyLooks: PUBLIC PROC [ref: Ref, looks: TextLooks.Looks, kind: OfStyle] ~ { lks: REF LooksCacheLooks _ lc.looksCacheLooks; inputs: REF LooksCacheBodies _ lc.looksCacheInputs; initloc, loc: CARDINAL; input: StyleBody; FindInLooksCache: ENTRY PROC RETURNS [BOOL] ~ { ENABLE UNWIND => NULL; lc.looksCacheProbes _ lc.looksCacheProbes+1; DO -- search cache SELECT lks[loc] FROM looks => IF inputs[loc] = ref^ THEN { ref^ _ lc.looksCacheResults[loc]; lc.looksCacheHits _ lc.looksCacheHits+1; RETURN [TRUE] }; TextLooks.noLooks => RETURN [FALSE]; -- this is an unused entry ENDCASE; SELECT (loc _ loc+1) FROM looksCacheSize => IF (loc _ 0)=initloc THEN RETURN [FALSE]; initloc => RETURN [FALSE]; ENDCASE; ENDLOOP; }; PutInLooksCache: ENTRY PROC ~ { ENABLE UNWIND => NULL; IF lc.looksCacheCount = looksCacheMax THEN ClearLooksCache[]; loc _ initloc; DO -- search cache SELECT lks[loc] FROM looks => IF inputs[loc] = input THEN RETURN; -- already in cache TextLooks.noLooks => EXIT; -- this is an unused entry ENDCASE; SELECT (loc _ loc+1) FROM looksCacheSize => IF (loc _ 0)=initloc THEN ERROR; -- cache full initloc => ERROR; -- cache full ENDCASE; ENDLOOP; lc.looksCacheResults[loc] _ ref^; lks[loc] _ looks; inputs[loc] _ input; lc.looksCacheCount _ lc.looksCacheCount+1; }; IF looks = TextLooks.noLooks THEN RETURN; loc _ initloc _ HashStyle[ref, looks] MOD looksCacheSize; IF FindInLooksCache[] THEN RETURN; input _ ref^; -- save the input value of the record IF NodeStyleWorks.ExecuteLooksInStyle[ref, kind, looks] THEN PutInLooksCache[]; }; lc: REF LooksCacheInfoRecord _ NEW[LooksCacheInfoRecord]; LooksCacheInfoRecord: TYPE ~ RECORD [ looksCacheCount: CARDINAL _ 0, looksCacheLooks: REF LooksCacheLooks, looksCacheInputs: REF LooksCacheBodies, looksCacheResults: REF LooksCacheBodies, looksCacheProbes, looksCacheHits: INT _ 0 ]; looksCacheSize: CARDINAL ~ 16; -- should be a power of 2 looksCacheMax: CARDINAL ~ (looksCacheSize*4)/5; -- don't fill too full LooksCacheLooks: TYPE ~ ARRAY [0..looksCacheSize) OF TextLooks.Looks; LooksCacheBodies: TYPE ~ ARRAY [0..looksCacheSize) OF StyleBody; InitLooksCacheInfo: PROC ~ { lc.looksCacheLooks _ NEW[LooksCacheLooks]; lc.looksCacheInputs _ NEW[LooksCacheBodies]; lc.looksCacheResults _ NEW[LooksCacheBodies]; }; FlushLooksCache: ENTRY PROC [init: BOOL _ FALSE] ~ { ENABLE UNWIND => NULL; ClearLooksCache[]; }; ClearLooksCache: PROC [init: BOOL _ FALSE] ~ { IF NOT init AND lc.looksCacheCount = 0 THEN RETURN; lc.looksCacheCount _ 0; FOR i: CARDINAL IN [0..looksCacheSize) DO lc.looksCacheLooks[i] _ TextLooks.noLooks; ENDLOOP; }; ApplyObject: PUBLIC PROC [ref: Ref, object: Object, kind: OfStyle _ screen] ~ { objects: REF ObjectCacheObjects _ oc.objectCacheObjects; inputs: REF ObjectCacheBodies _ oc.objectCacheInputs; input: StyleBody; initloc, loc: CARDINAL; FindInObjectCache: ENTRY PROC RETURNS [BOOL] ~ { ENABLE UNWIND => NULL; oc.objectCacheProbes _ oc.objectCacheProbes+1; DO -- search cache SELECT objects[loc] FROM object => IF inputs[loc] = ref^ THEN { ref^ _ oc.objectCacheResults[loc]; oc.objectCacheHits _ oc.objectCacheHits+1; RETURN [TRUE] }; nullObject => RETURN [FALSE]; -- this is an unused entry ENDCASE; SELECT (loc _ loc+1) FROM objectCacheSize => IF (loc _ 0)=initloc THEN RETURN [FALSE]; initloc => RETURN [FALSE]; ENDCASE; ENDLOOP; }; PutInObjectCache: ENTRY PROC ~ { ENABLE UNWIND => NULL; IF oc.objectCacheCount = objectCacheMax THEN ClearObjectCache[]; loc _ initloc; DO -- search cache for place to put the entry SELECT objects[loc] FROM object => IF inputs[loc] = input THEN RETURN; -- already in cache nullObject => EXIT; -- this is an unused entry ENDCASE; SELECT (loc _ loc+1) FROM objectCacheSize => IF (loc _ 0)=initloc THEN ERROR; -- cache full initloc => ERROR; -- cache full ENDCASE; ENDLOOP; oc.objectCacheCount _ oc.objectCacheCount+1; inputs[loc] _ input; oc.objectCacheResults[loc] _ ref^; objects[loc] _ object; }; IF object = nullObject THEN RETURN; loc _ initloc _ HashStyle[ref, , object] MOD objectCacheSize; IF FindInObjectCache[] THEN RETURN; input _ ref^; -- save the input value of the record IF NodeStyleWorks.ExecuteObjectInStyle[ref, kind, object] THEN PutInObjectCache[]; }; oc: REF ObjectCacheInfoRecord _ NEW[ObjectCacheInfoRecord]; ObjectCacheInfoRecord: TYPE ~ RECORD [ objectCacheCount: CARDINAL, objectCacheObjects: REF ObjectCacheObjects, objectCacheInputs: REF ObjectCacheBodies, objectCacheResults: REF ObjectCacheBodies, objectCacheProbes, objectCacheHits: INT _ 0 ]; objectCacheSize: CARDINAL ~ 16; -- should be a power of 2 objectCacheMax: CARDINAL ~ (objectCacheSize*4)/5; -- don't fill too full ObjectCacheObjects: TYPE ~ ARRAY [0..objectCacheSize) OF Object; ObjectCacheBodies: TYPE ~ ARRAY [0..objectCacheSize) OF StyleBody; nullObject: Object ~ NIL; InitObjectCacheInfo: PROC ~ { oc.objectCacheObjects _ NEW[ObjectCacheObjects]; oc.objectCacheInputs _ NEW[ObjectCacheBodies]; oc.objectCacheResults _ NEW[ObjectCacheBodies]; }; FlushObjectCache: ENTRY PROC [init: BOOL _ FALSE] ~ { ENABLE UNWIND => NULL; ClearObjectCache[]; }; ClearObjectCache: PROC [init: BOOL _ FALSE] ~ { IF NOT init AND oc.objectCacheCount = 0 THEN RETURN; oc.objectCacheCount _ 0; FOR i: CARDINAL IN [0..objectCacheSize) DO oc.objectCacheObjects[i] _ nullObject; ENDLOOP; }; FlushCaches: PUBLIC ENTRY PROC ~ { ENABLE UNWIND => NULL; ClearCaches[FALSE]; }; ClearCaches: PROC [init: BOOL] ~ { ClearApplyAllCache[init]; ClearRuleCache[init]; ClearLooksCache[init]; ClearObjectCache[init]; }; nonNumeric: PUBLIC ERROR ~ CODE; GetStyleParam: PUBLIC PROC [s: Ref, name: ATOM, styleName: ATOM, kind: OfStyle] RETURNS [r: REAL] ~ { obj: Object _ GetStyleParamObj[s, name, styleName, kind]; WITH obj SELECT FROM x: REF TJaM.NumberRep.int => r _ x.int; x: REF TJaM.NumberRep.real => r _ x.real; ENDCASE => ERROR nonNumeric; RETURN [r]; }; GetStyleParamI: PUBLIC PROC [s: Ref, name: ATOM, styleName: ATOM, kind: OfStyle] RETURNS [i: INTEGER] ~ { obj: Object _ GetStyleParamObj[s, name, styleName, kind]; WITH obj SELECT FROM x: REF TJaM.NumberRep.int => i _ x.int; x: REF TJaM.NumberRep.real => i _ NodeStyle.IntegerValue[x.real]; ENDCASE => ERROR nonNumeric; RETURN [i]; }; GetStyleParamObj: PUBLIC PROC [s: Ref, name: ATOM, styleName: ATOM, kind: OfStyle] RETURNS [obj: Object] ~ { frame: Frame; key: ATOM _ NodeStyleWorks.StyleParamKey[name]; FOR x: DataList _ s.dataList, x.next UNTIL x=NIL DO WITH x SELECT FROM xx: REF NodeStyle.DataEntry.object => IF xx.name = key THEN RETURN[xx.object]; ENDCASE; ENDLOOP; frame _ NodeStyleWorks.GetFrame[s, styleName, kind]; obj _ TJaM.TryToLoad[frame, key].val; NodeStyleWorks.FreeFrame[frame, styleName, kind]; RETURN [obj]; }; StyleNameForNode: PUBLIC PROC [node: TextNode.Ref] RETURNS [name: ATOM] ~ { s: Ref _ Alloc[]; ApplyAll[s, node]; name _ GetStyleName[s]; Free[s]; }; InitializeDefaultStyle: PUBLIC PROC [suggestedStyle: ROPE] ~ { changeSet: EditNotify.ChangeSet; PointsPerInch: REAL ~ 1.0/0.0138370; changeSet[ChangingProp] _ TRUE; changeSet[ChangingFormat] _ TRUE; changeSet[MovingNodes] _ TRUE; changeSet[NodeNesting] _ TRUE; changeSet[InsertingNode] _ TRUE; EditNotify.AddNotifyProc[Notify, after, high, changeSet]; InitApplyCacheRecord[]; InitRuleCacheInfo[]; InitLooksCacheInfo[]; InitObjectCacheInfo[]; ClearCaches[TRUE]; defaultStyle _ Create[]; defaultFormatName _ $default; rootFormatName _ $root; defaultStyle.name[fontFamily] _ $Helvetica; SetReal[defaultStyle, fontSize, 10]; SetReal[defaultStyle, leading, 12]; SetReal[defaultStyle, topLeading, 12]; SetReal[defaultStyle, topIndent, 12]; SetReal[defaultStyle, tabStops, 20]; SetReal[defaultStyle, pageWidth, 8.5*PointsPerInch]; SetReal[defaultStyle, pageLength, 11*PointsPerInch]; SetReal[defaultStyle, leftMargin, 1*PointsPerInch]; SetReal[defaultStyle, rightMargin, 1*PointsPerInch]; SetReal[defaultStyle, topMargin, 1*PointsPerInch]; SetReal[defaultStyle, bottomMargin, 1*PointsPerInch]; SetReal[defaultStyle, lineLength, 6.5*PointsPerInch]; SetReal[defaultStyle, underlineThickness, 1]; SetReal[defaultStyle, underlineDescent, 1]; SetReal[defaultStyle, strikeoutThickness, 1]; SetReal[defaultStyle, strikeoutAscent, 4]; NodeProps.Register[name: $StyleDef, reader: ReadSpecs, writer: WriteSpecs, copier: CopyInfoProc]; SetDefaultStyle[suggestedStyle]; }; PrefixKind: TYPE ~ RECORD [ pattern: ROPE, sizeInName: BOOL, preScaled: BOOL, -- Having pre-scaled fonts was probably a mistake, but we are sort of stuck, for now. faceEncoding: FaceEncoding ]; FaceEncoding: TYPE ~ {boldItalic, bir, bi}; prefixKinds: LIST OF PrefixKind _ LIST[ [pattern: "xerox/tiogafonts/", sizeInName: TRUE, preScaled: TRUE, faceEncoding: bi], [pattern: "xerox/pressfonts/", sizeInName: FALSE, preScaled: FALSE, faceEncoding: bir], [pattern: "xerox/altofonts/", sizeInName: TRUE, preScaled: TRUE, faceEncoding: bi], [pattern: "xerox/xc*/tioga-", sizeInName: TRUE, preScaled: FALSE, faceEncoding: boldItalic], [pattern: "xerox/xc*/", sizeInName: FALSE, preScaled: FALSE, faceEncoding: boldItalic] ]; AppendFontName: PROC [text: REF TEXT, prefix: ATOM, family: ATOM, face: FontFace, size: REAL, alphabets: FontAlphabets] RETURNS [new: REF TEXT, sizeInName: BOOL, preScaled: BOOL] ~ { fam: ROPE ~ Atom.GetPName[family]; pre: ROPE _ IF prefix#NIL THEN Atom.GetPName[prefix] ELSE NIL; faceEncoding: FaceEncoding _ boldItalic; IF Rope.Find[fam, "/"] >= 0 THEN { text _ RefText.AppendRope[text, fam]; RETURN [text, FALSE, FALSE]; }; IF pre = NIL OR Rope.Size[pre] = 0 THEN pre _ "xerox/tiogafonts/"; sizeInName _ preScaled _ FALSE; FOR p: LIST OF PrefixKind _ prefixKinds, p.rest UNTIL p=NIL DO IF Rope.Match[p.first.pattern, pre, FALSE] THEN { sizeInName _ p.first.sizeInName; preScaled _ p.first.preScaled; faceEncoding _ p.first.faceEncoding; }; ENDLOOP; text _ RefText.AppendRope[text, pre]; text _ RefText.AppendRope[text, fam]; SELECT faceEncoding FROM boldItalic => { IF face=Bold OR face=BoldItalic THEN text _ RefText.AppendTextRope[text, "-bold"]; IF face=Italic OR face=BoldItalic THEN text _ RefText.AppendTextRope[text, "-italic"]; IF sizeInName THEN { text _ RefText.AppendChar[text, '-]; text _ Convert.AppendInt[text, Real.Round[size]]; }; }; bir => { IF sizeInName THEN ERROR; text _ RefText.AppendTextRope[text, SELECT face FROM Bold => "-BRR", Italic => "-MIR", BoldItalic => "-BIR", ENDCASE => "-MRR"]; }; bi => { IF sizeInName THEN { text _ Convert.AppendInt[text, Real.Round[size]]; }; IF face=Bold OR face=BoldItalic THEN text _ RefText.AppendTextRope[text, "B"]; IF face=Italic OR face=BoldItalic THEN text _ RefText.AppendTextRope[text, "I"]; }; ENDCASE => ERROR; new _ text; }; FontNameFromStyleParams: PUBLIC PROC [prefix: ATOM, family: ATOM, face: FontFace, size: REAL, alphabets: FontAlphabets] RETURNS [name: ROPE, scale: REAL] ~ { scratch: REF TEXT ~ RefText.ObtainScratch[100]; text: REF TEXT _ scratch; sizeInName, preScaled: BOOL _ FALSE; [text, sizeInName, preScaled] _ AppendFontName[text, prefix, family, face, size, alphabets]; name _ Rope.FromRefText[text]; IF preScaled THEN scale _ 1.0 ELSE scale _ size; RefText.ReleaseScratch[scratch]; }; nTrys: NAT ~ 5; tryDelta: ARRAY [0..nTrys) OF REAL _ [0, -0.999, 0.999, -1.998, 1.998]; families: LIST OF LIST OF ATOM _ LIST[ LIST[$Tioga, $Laurel, $TimesRoman], LIST[$TimesRoman, $Classic], LIST[$Helvetica, $Modern, $TimesRoman], LIST[$Modern, $Helvetica], LIST[$Classic, $TimesRoman, $Modern], LIST[$Gacha, $Helvetica] ]; SubstituteFamilies: PROC [family: ATOM] RETURNS [LIST OF ATOM] ~ { fam: ROPE ~ Atom.GetPName[family]; FOR f: LIST OF LIST OF ATOM _ families, f.rest UNTIL f=NIL DO k: ATOM _ f.first.first; IF family = k OR Rope.Equal[fam, Atom.GetPName[k], FALSE] THEN RETURN [f.first]; ENDLOOP; RETURN [NIL]; }; FontReplacementProc: PROC [prefix: ATOM, family: ATOM, face: FontFace, size: REAL, alphabets: FontAlphabets] RETURNS [font: ImagerFont.Font] _ NIL; horribleHackForTioga10: BOOL _ TRUE; defaultPrintFont: ImagerFont.Font _ NIL; candidates: LIST OF ROPE _ LIST[ "xerox/xc1-2-2/modern", "xerox/pressfonts/helvetica-mrr" ]; GetDefaultFont: PROC RETURNS [ImagerFont.Font] ~ { FOR each: LIST OF ROPE _ candidates, each.rest WHILE defaultPrintFont = NIL DO defaultPrintFont _ ImagerFont.Find[each.first, substituteQuietly ! Imager.Error => CONTINUE]; ENDLOOP; RETURN [defaultPrintFont] }; FontFromStyleParams: PUBLIC PROC [prefix: ATOM, family: ATOM, face: FontFace, size: REAL, alphabets: FontAlphabets] RETURNS [font: ImagerFont.Font _ NIL] ~ { didSubstitution: BOOL _ FALSE; font _ CheckStyleFontCache[prefix, family, face, size, alphabets]; IF font = NIL THEN { IF FontReplacementProc # NIL THEN { font _ FontReplacementProc[prefix, family, face, size, alphabets]; }; IF font = NIL THEN { scratch: REF TEXT ~ RefText.ObtainScratch[100]; text: REF TEXT _ scratch; sizeInName, preScaled: BOOL _ FALSE; trialFace: FontFace _ face; WHILE font = NIL DO families: LIST OF ATOM _ NIL; trialFamily: ATOM _ family; firstFamily: ATOM _ family; IF horribleHackForTioga10 AND (firstFamily=$tioga OR firstFamily=$Tioga) AND (size NOT IN [9.5..10.5]) THEN { trialFamily _ firstFamily _ $TimesRoman; }; WHILE font = NIL AND trialFamily # NIL DO FOR try: NAT IN [0..nTrys) WHILE font = NIL DO trialSize: REAL _ size + tryDelta[try]; text.length _ 0; [text, sizeInName, preScaled] _ AppendFontName[text, prefix, trialFamily, trialFace, trialSize, alphabets]; font _ ImagerFont.Find[Rope.FromRefText[text], noSubstitute ! Imager.Error => CONTINUE]; IF font = NIL THEN didSubstitution _ TRUE; IF NOT sizeInName THEN EXIT; ENDLOOP; IF font = NIL THEN { IF families = NIL THEN families _ SubstituteFamilies[firstFamily]; IF families # NIL THEN { families _ families.rest; }; trialFamily _ IF families=NIL THEN NIL ELSE families.first; }; ENDLOOP; IF trialFace = Regular THEN EXIT; trialFace _ IF trialFace = BoldItalic THEN Bold ELSE IF trialFace = Bold AND face = BoldItalic THEN Italic ELSE Regular; ENDLOOP; IF font = NIL THEN { IF sizeInName THEN { font _ VFonts.DefaultFont[font]; preScaled _ TRUE } -- Probably not for printing ELSE { font _ GetDefaultFont[]; preScaled _ FALSE } }; IF NOT preScaled THEN { font _ ImagerFont.Scale[font, size] }; RefText.ReleaseScratch[scratch]; }; EnterStyleFontCache[[prefix, family, face, size, alphabets, font]]; }; IF didSubstitution THEN { text: REF TEXT _ RefText.ObtainScratch[100]; text _ RefText.AppendRope[text, "Substituting font "]; text _ RefText.AppendRope[text, ImagerFont.Name[font]]; text _ RefText.AppendRope[text, " for "]; text _ AppendFontName[text, prefix, family, face, size, alphabets].new; MessageWindow.Append[Rope.FromRefText[text], TRUE]; RefText.ReleaseScratch[text]; }; }; styleFontCacheSize: NAT _ 5; styleFontCache: LIST OF StyleFontCacheRec _ NIL; styleFontCacheHits: INT _ 0; styleFontCacheMisses: INT _ 0; StyleFontCacheRec: TYPE ~ RECORD [prefix: ATOM, family: ATOM, face: FontFace, size: REAL, alphabets: FontAlphabets, font: ImagerFont.Font]; FlushStyleFontCache: ENTRY PROC ~ { styleFontCache _ NIL }; CheckStyleFontCache: ENTRY PROC [prefix: ATOM, family: ATOM, face: FontFace, size: REAL, alphabets: FontAlphabets] RETURNS [ImagerFont.Font] ~ { prev: LIST OF StyleFontCacheRec _ NIL; FOR c: LIST OF StyleFontCacheRec _ styleFontCache, c.rest UNTIL c = NIL DO IF c.first.prefix = prefix AND c.first.family = family AND c.first.face = face AND c.first.size = size AND c.first.alphabets = alphabets THEN { IF prev # NIL THEN { prev.rest _ c.rest; c.rest _ styleFontCache; styleFontCache _ c; }; styleFontCacheHits _ styleFontCacheHits + 1; RETURN [c.first.font]; }; prev _ c; ENDLOOP; styleFontCacheMisses _ styleFontCacheMisses + 1; RETURN [NIL] }; EnterStyleFontCache: ENTRY PROC [styleFontCacheRec: StyleFontCacheRec] ~ { new: LIST OF StyleFontCacheRec _ NIL; prev: LIST OF StyleFontCacheRec _ NIL; i: NAT _ 2; FOR p: LIST OF StyleFontCacheRec _ styleFontCache, p.rest DO IF p = NIL THEN {new _ LIST[styleFontCacheRec]; EXIT}; IF i >= styleFontCacheSize AND p.rest#NIL THEN { new _ p.rest; p.rest _ NIL; new.rest _ NIL; new.first _ styleFontCacheRec; EXIT; }; i _ i + 1; ENDLOOP; new.rest _ styleFontCache; styleFontCache _ new; }; END. ,NodeStyleOpsImpl.mesa Copyright Σ 1985, 1986 by Xerox Corporation. All rights reserved. written by Bill Paxton, January 1981 Paxton, December 21, 1982 9:55 am Maxwell, January 6, 1983 9:50 am Russ Atkinson, March 7, 1985 3:29:21 am PST Michael Plass, November 25, 1987 11:35:30 am PST Rick Beach, March 27, 1985 10:54:03 am PST Doug Wyatt, December 16, 1986 5:44:54 pm PST Style Operations create a style body copy a style body get from a small cache don't free more than once or disaster! Local Styles Apply Style to Node NodeStyleObsolete.EvalFreeVars[ref, node]; ApplyAll Cache -- when clearing, go all the way to applyCacheSize rather than stopping at ac.applyCacheDepth Update ApplyAll Cache due to Editing Operations if change invalidates one node only, remove that node else clear entire cache -- Change.NodeNesting used to do the following, but since changing the nesting can change the bottomLeading, for instance, I made this more conservative. -mfp x: REF READONLY Change.NodeNesting => IF x.first = x.last -- only changing one node AND TextNode.FirstChild[x.first] = NIL -- node has no children THEN SELECT x.change FROM 1 => -- increasing nesting in tree IF TextNode.Next[x.first] = NIL THEN RemoveNodeFromApplyAllCache[x.first] ELSE FlushApplyAllCache; -1 => -- decreasing nesting in tree RemoveNodeFromApplyAllCache[x.first]; ENDCASE => FlushApplyAllCache ELSE FlushApplyAllCache; Style Rule Cache Looks Cache Object Cache Flush Caches Style Parameter Extensions May raise NodeStyleOps.nonNumeric or TJaM.Error[undefkey]. May raise NodeStyleOps.nonNumeric or TJaM.Error[undefkey]. Miscellaneous Does an ApplyAll and then returns the style name Initialization register the notify proc that updates the style caches when edits occur initialize all the style caches establish the default styles wired into Tioga provide some basic style attribute values in case no style gets loaded successfully register the special handling procedures for the local style property: StyleDef NodeStyleFontImpl Plug with debugger for experimentation purposes. Address fault if none of the default candidates were found This loop tries different faces. This loop tries different families. This loop tries different sizes of screen fonts. Last chance! Style Font Cache Move to front NB: The following code forces a styleFontCache size of >= 2 Κ'F˜codešœ™KšœB™BKšœ$™$Kšœ!™!K™ K™+K™0J™*K™,—K™šΟk ˜ Kšœœ˜ Kšœ œ˜!Kšœœ˜'Kšœ œ$˜4Kšœœ ˜Kšœ œ˜+Kšœœ ˜Kšœœ ˜Kšœ œ'˜6Kšœ œ€˜Kšœœ˜Kšœ œ2˜DKšœœΞ˜βKšœœ ˜KšœœQ˜^Kšœœ0œ˜FKšœ œ˜!Kšœ œ-˜;Kšœœ=˜GKšœœ˜—K˜KšΠblœœ˜Kšœ:œb˜₯Kšœ˜#šœœœ˜%K™Kšœœœ˜Kšœœ˜Kšœœ˜Kšœœ˜—headšœ™šΟnœœœœ ˜%Kšœ™Kšœœ ˜Kšœ˜K˜—šŸœœœ˜)Kšœ™K˜K˜K˜—KšœœΟc ˜7š Ÿœœœœœ ˜-Kšœ™Kšœœœ˜šœœ˜Kšœœœ˜!Kšœœœ˜!Kšœœœ˜!Kšœ˜—Kšœ˜K˜—šŸœœœœ ˜$Kšœ&™&Kšœœœ˜šœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜—Kšœ˜K˜—š Ÿ œœœœœœ˜:Kšœ'œœ ˜9K˜6Kšœ œ ˜-Kšœœ˜Kšœ˜K˜—šŸ œœœœœœœ˜GKšœ'œœ ˜9šœ˜ Kšœ)˜-šœ˜Kšœ;˜;Kšœ œ ˜-Kšœ˜——Kšœœ˜Kšœ˜K˜—š Ÿ œœœœœœ˜<š Ÿœœœœœ˜4JšœE˜KJ˜—K˜+šœœ˜9Kšœ'œœ˜7K˜*Kšœœ!œœ*˜dKšœ˜—K˜K˜——™ Kšœœœ˜Kšœœœœ ˜6K˜šŸœœœœ˜-KšœF˜FK˜,Kšœ˜Kšœ˜K˜—š Ÿœœœ œœœ˜9Kšœœ˜!š œœœ œ˜$Kšœœ> ˜WKšœb˜bKšœœ1˜RK˜Kšœ˜—Kšœ˜Kšœ˜K˜—Kšœœ˜š Ÿ œœœ œœ œ˜Bš Ÿ œœœœœ˜0Kšœ(˜(KšœH˜HK˜—Kšœœ˜,Kšœœ˜&Kšœ0˜0K˜Kšœ)˜)Kšœ˜Kšœ˜K˜—š Ÿ œœœ œœ œ˜CKšœœ˜'Kš œœ œœœœ˜8Kšœ˜K˜—š Ÿ œœœ œœœ˜BKšœ ˜——šœ™Kšœœœ˜Kšœœ˜Kšœœ˜K˜šŸœœœ;˜PK˜!K˜K˜—šŸ œœ/œ œ˜\Kšœœ˜ K˜Kšœœ˜ Kšœœœœ˜8K˜6Kšœœœ ˜K˜Kš œœœœœ˜?K˜&K˜#K˜'Kšœ ˜Kšœ˜K˜—šŸ œœœ%œ˜Všœ˜ Kšœœ7œ˜GKšœœJœ˜ZKšœœœ3˜AKšœ˜ Kšœ˜—Kšœœ˜ Kš œœœœœœ˜;Kšœ˜Kšœœ(˜>K™*šœœ˜Kšœœ%˜DKšœœœ#˜;Kšœ˜—šœ˜šœ˜Kšœ\œ ˜nKšœ˜—šœœ œœ #œœ-œ˜•š œœœ4œœ˜XKšœ œœ˜,Kšœ/˜/Kšœ˜Kšœ˜———Kšœ-˜-šœœ˜Kšœ]œ ˜pKšœ˜—šœ˜K˜———™Kšœœœ˜1šœœœ˜!Kšœœ ˜2Kšœœ˜)Kšœœ˜%Kšœ3œ˜=—Kšœœ  ˜>Kšœœœœ˜BKšœœœœ ˜BK˜šŸœœ˜Kšœœ˜.Kšœœ˜*Kšœ˜K˜—šŸœœœ˜CK˜—š Ÿœœœœœœ˜>Kšœœœ˜K˜K˜K˜—šŸœœœ˜)Kšœœ&˜0Kšœ]™]Kš œœœœ œœ˜BK˜K˜K˜—šŸœœœœ˜GKšœœœ˜Kšœœ&˜0šœœœœ˜.šœœ ˜-šœœœ˜)Kšœ œ˜Kšœ˜—Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜K˜—š Ÿœœœ0œ œ œ˜yKšœœœ˜Kšœœ&˜0Kšœœ 1˜MK˜,š œœ œœ˜8šœœ%œ  ˜KK˜(K˜,K˜Kšœœ˜Kšœ˜—Kšœ˜—Kšœœ˜Kšœ˜K˜—šŸœœœ'œ˜TKšœœœ˜Kšœœ&˜0Kšœœœ˜'K˜K˜#Kš œœœœ œœ˜HK˜K˜K˜——™/Kšœœ˜!šŸœœ œœ ˜.Kšœ5™5Kšœ™šŸœœ˜%Kšœœœ˜šœœ ™šœ ™"Kšœœœ%™IKšœ™—šœ ™#K™%—Kšœ™—Kšœ™——Kšœœœ)˜8šœœœ˜(Kšœ$œ˜:Kšœ˜—Kšœœ 6˜H—K˜K˜——™šœ œœ C˜^K˜—šŸ œœœœ˜GKšœœ$˜.Kšœœ&˜1K˜Kšœœ˜š Ÿœœœœœ˜.Kšœœœ˜K˜*šœ ˜šœ ˜šœœœ˜$K˜ K˜&Kšœœ˜—Kšœœœ ˜1Kšœ˜—šœ˜Kš œœœœœ˜:Kšœ œœ˜Kšœ˜—Kšœ˜—Kšœ˜—šŸœœœ˜Kšœœœ˜Kšœ"œ˜:K˜šœ *˜-šœ ˜Kšœœœœ ˜?Kšœœ ˜'Kšœ˜—šœ˜Kšœœœœ  ˜AKšœ œ  ˜Kšœ˜—Kšœ˜—K˜(K˜K˜ K˜K˜—Kš œœœœœœ˜1Kšœ'œ˜9Kšœœœ˜!Kšœ %˜3šœ3˜5Kšœ˜šœœ œ˜Kšœœ˜!šœ œ˜KšœT˜TKšœ˜Kšœ/˜/Kšœ˜Kšœ2œ˜K˜šŸœœ˜Kšœœ˜(Kšœœ˜*Kšœœ˜+Kšœ˜K˜—š Ÿœœœœœ˜3Kšœœœ˜K˜K˜K˜—šŸœœœœ˜-Kšœœ$˜.Kš œœœœœ˜2K˜Kš œœœœ œœ˜AKšœ˜K˜—šŸ œœDœœœœœ˜}Kšœœœ œœœœœœ;˜uKšœž˜žKšœ!œ˜;šœ˜K˜———™ šŸ œœœ6˜MKšœœ&˜.Kšœœ(˜3Kšœœ˜K˜K˜š Ÿœœœœœ˜/Kšœœœ˜K˜,šœ ˜šœ ˜šœ œœ˜%K˜!K˜(Kšœœ˜—Kšœœœ ˜?Kšœ˜—šœ˜Kš œœœœœ˜;Kšœ œœ˜Kšœ˜—Kšœ˜—šœ˜K˜——šŸœœœ˜Kšœœœ˜Kšœ$œ˜=K˜šœ ˜šœ ˜Kšœ œœœ ˜@Kšœœ ˜5Kšœ˜—šœ˜Kšœœœœ  ˜@Kšœ œ  ˜Kšœ˜—Kšœ˜—K˜!K˜K˜K˜*K˜K˜—Kšœœœ˜)Kšœ&œ˜9Kšœœœ˜"Kšœ %˜3Kšœ5œ˜OK˜K˜—Kšœœœ˜9šœœœ˜%Kšœœ˜Kšœœ˜%Kšœœ˜'Kšœœ˜(Kšœ"œ˜)Kšœ˜—Kšœœ ˜8Kšœœ ˜FKšœœœœ˜EKšœœœœ ˜@K˜šŸœœ˜Kšœœ˜*Kšœœ˜,Kšœœ˜-K˜K˜—š Ÿœœœœœ˜4Kšœœœ˜K˜K˜K˜—šŸœœœœ˜.Kš œœœœœ˜3K˜Kš œœœœ,œ˜]Kšœ˜K˜——™ šŸ œœœ7˜OKšœ œ,˜8Kšœœ*˜5K˜Kšœœ˜š Ÿœœœœœ˜0Kšœœœ˜K˜.šœ ˜šœ˜šœ œœ˜&K˜"K˜*Kšœœ˜—Kšœœœ ˜8Kšœ˜—šœ˜Kš œœœœœ˜K˜ Kšœœ˜$K˜KšœG™GKšœœ˜Kšœœ˜!Kšœœ˜Kšœœ˜Kšœœ˜ K˜9K˜Kšœ™K˜K˜K˜K˜Kšœ œ˜K˜Kšœ-™-K˜K˜K˜K˜KšœS™SK˜+K˜$K˜#K˜&K˜%K˜$K˜4K˜4K˜3K˜4K˜2K˜5K˜5Kšœ-˜-Kšœ+˜+Kšœ-˜-Kšœ*˜*K˜KšœO™OKšœa˜aK˜Kšœ ˜ Kšœ˜——šœ™šœ œœ˜Kšœ œ˜Kšœ œ˜Kšœ œ U˜fK˜Kšœ˜K˜—šœœ˜+K˜—šœ œœœ˜'Kšœ+œ œ˜TKšœ+œ œ˜WKšœ*œ œ˜SKšœ*œ œ˜\Kšœ$œ œ˜VKšœ˜K˜—šŸœœœœ œ œœœœœœ œ˜ΆKšœœ˜"Kš œœœœœœœ˜>Kšœ(˜(šœœ˜"Kšœ%˜%Kšœœœ˜Kšœ˜—Kšœœœœ˜BKšœœ˜š œœœ"œœ˜>šœ"œœ˜1Kšœ ˜ Kšœ˜Kšœ$˜$Kšœ˜—Kšœ˜—Kšœ%˜%Kšœ%˜%šœ˜šœ˜Kšœ œœ.˜RKšœ œœ0˜Všœ œ˜Kšœ$˜$Kšœ1˜1Kšœ˜—Kšœ˜—šœ˜Kšœ œœ˜Kšœ$œœ9œ ˜€Kšœ˜—šœ˜šœ œ˜Kšœ1˜1Kšœ˜—Kšœ œœ*˜NKšœ œœ*˜PKšœ˜—Kšœœ˜—Kšœ ˜ Kšœ˜K˜—šŸœœœ œ œœœœ œ˜Kšœ œœ˜/Kšœœœ ˜Kšœœœ˜$Kšœ\˜\Kšœ˜Kšœ œ œ˜0Kšœ ˜ Kšœ˜K˜—Kšœœ˜Kšœ œ œœ%˜Gš œ œœœœœœ˜&Kšœ˜#Kšœ˜Kšœ#˜'Kšœ˜Kšœ!˜%Kšœ˜K˜K˜—šŸœœ œœœœœ˜BKšœœ˜"šœœœœœœœœ˜=Kšœœ˜Kš œ œ#œœœ ˜PKšœ˜—Kšœœ˜ K˜K˜—šŸœœ œ œœœœ˜“K™0K™—šœœœ˜$K˜—Kšœ$œ˜(š œ œœœœ˜ K˜K˜ Kšœ˜—šŸœœœ˜2š œœœœœœ˜NKšœ:™:KšœSœ˜]Kšœ˜—Kšœ˜Kšœ˜K˜—šŸœœœ œ œœœœ˜Kšœœœ˜KšœB˜Bšœœœ˜šœœœ˜#JšœB˜BJšœ˜—šœœœ˜Kšœ œœ˜/Kšœœœ ˜Kšœœœ˜$Kšœ˜šœœ˜K™ Kš œ œœœœ˜Kšœ œ ˜Kšœ œ ˜šœœœœœœœ˜mKšœ(˜(Kšœ˜—š œœœœ˜)K™#š œœœ œœ˜.K™0Kšœ œ˜'Kšœ˜Kšœk˜kKšœNœ˜XKšœœœœ˜*Kšœœ œœ˜Kšœ˜—šœœœ˜Kšœ œœ,˜Bšœ œœ˜Kšœ˜Kšœ˜—Kš œœ œœœœ˜;Kšœ˜—Kšœ˜—Kšœœœ˜!Kšœ œœœœœœœ ˜xKšœ˜—šœœœ˜K™ šœ ˜ Kšœ0œ ˜WKšœ(œ˜3—Kšœ˜—Kšœœ œ)˜>Kšœ ˜ Jšœ˜—KšœC˜CKšœ˜—šœœ˜Kšœœœ˜,Kšœ6˜6Kšœ7˜7Kšœ)˜)KšœG˜GKšœ-œ˜3Kšœ˜Kšœ˜—Kšœ˜——šœ™Kšœœ˜Kšœœœœ˜0Kšœœ˜šœœ˜K˜—š œœœ œ œœ3˜‹K˜—šŸœœœœ˜;K˜—šŸœœœ œ œœœ˜Kšœœœœ˜&š œœœ,œœ˜JKšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜!šœ˜šœœœ˜K™ Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ,˜,Kšœ˜Kšœ˜—Kšœ ˜ Kšœ˜—Kšœ0˜0Kšœœ˜ Kšœ˜K˜—šŸœœœ+˜JKšœœœœ˜%Kšœœœœ˜&Kšœœ˜ K™;šœœœ,˜