DIRECTORY Basics USING [bytesPerWord], FileNames USING [CurrentWorkingDirectory], FS USING [Error, StreamOpen], IO USING [Close, EndOf, EndOfStream, GetChar, PutChar, PutRope, STREAM, UnsafeGetBlock, UnsafePutBlock, GetIndex, SetIndex], MessageWindow USING [Append, Blink], Rope USING [Concat, Fetch, FromChar, FromProc, Length, ROPE], RuntimeError USING [BoundsFault], SilDisplayCursors, SilFile, SilKernel, SilUserInput, UserProfile USING [ListOfTokens, Token] ; SilFileImpl: CEDAR MONITOR IMPORTS Rope, IO, FS, FileNames, MessageWindow, RuntimeError, UserProfile, SilKernel, SilDisplayCursors EXPORTS SilFile, SilKernel ~ BEGIN OPEN SilFile; allModelsMacros: ARRAY LibraryFonts OF CharSet; allModelsFontNames: ARRAY PresetFonts OF ROPE; -- names of display fonts allModelsPFontNames: ARRAY PresetFonts OF ROPE;-- names of print fonts allSelection: SilSelection; SilModel: TYPE ~ REF SilModelRec; SilModelRec: PUBLIC TYPE ~ MONITORED RECORD [ fgnd: SilObject _ NIL, --All the foreground objects in the main picture bkgnd: SilObject _ NIL, --All the top-level background objects in the main picture deleted: DeleteArray, macros: CharSet, --All the macros in this file activeName: ROPE, --The name of currently active file modelIsBuilt: BOOL --True if file has run through BUILD prog ]; CharSet: TYPE ~ ARRAY MacroName OF MacroDef; MacroDef: TYPE ~ RECORD [ xMin, yMin: INTEGER, -- Upper Left boundary of macro definition xMax, yMax: INTEGER, -- Lower right boundary of macro definition def: SilObject _ NIL ]; DeleteArray: TYPE ~ ARRAY DeletesToKeep OF SilObject _ deleteInit; DeletesToKeep: TYPE ~ [1..5]; deleteInit: DeleteArray _ [NIL, NIL, NIL, NIL , NIL]; deleteTop: DeletesToKeep ~ 1; deleteBottom: DeletesToKeep ~ 5; ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE ~ IO.STREAM; NewSilModel: PUBLIC PROC [] RETURNS [model: SilModel] ~ { model _ NEW[SilModelRec]; model.activeName _ FileNames.CurrentWorkingDirectory[]; }; InitSil: PUBLIC PROC [] ~ { dummySobr: SilObjectRec; libsOK: BOOLEAN _ TRUE; key, ropeResult: ROPE; ListResult: TYPE ~ LIST OF ROPE; listResult: ListResult; PresetDefaults: TYPE ~ ARRAY PresetFonts OF ListResult; presetDefaults: PresetDefaults ~ [ LIST["Helvetica10B", "Helvetica10"], LIST["Helvetica7", "Helvetica7"], LIST["Template64", "Template64"], LIST["Gates32", "Gates32"] ]; LibraryDefaults: TYPE ~ ARRAY LibraryFonts OF ROPE; libraryDefaults: LibraryDefaults ~ ["sil.lb5", "sil.lb6", "sil.lb7", "sil.lb8", "sil.lb9"]; profilePrefix: ROPE ~ "Sil.Font"; FOR i: PresetFonts IN PresetFonts DO key _ Rope.Concat[profilePrefix, Rope.FromChar['0 + i] ]; listResult _ UserProfile.ListOfTokens[key: key, default: presetDefaults[i] ]; allModelsFontNames[i] _ listResult.first; listResult _ listResult.rest; allModelsPFontNames[i] _ IF listResult=NIL THEN allModelsFontNames[i] ELSE listResult.first; ENDLOOP; MessageWindow.Append["Sil Library Files ", TRUE]; FOR i: LibraryFonts IN LibraryFonts DO key _ Rope.Concat[profilePrefix, Rope.FromChar['0 + i] ]; ropeResult _ UserProfile.Token[key: key, default: libraryDefaults[i] ]; FileToInternalRep[model: NIL, name: ropeResult, macroFile: TRUE, picFont: i ! SilKernel.SilError => { MessageWindow.Append[ropeResult, FALSE]; MessageWindow.Append[" ", FALSE]; libsOK _ FALSE; CONTINUE}]; ENDLOOP; IF libsOK THEN MessageWindow.Append["loading A-OK", FALSE] ELSE MessageWindow.Append["loading failed", FALSE]; dummySobr _ [xMin: 0, yMin: 0, xMax: 0, yMax: 0, color: 0, font: 0, italic: FALSE, value: NIL, selectObj: NIL]; allSelection.lastObject _ LIST[dummySobr]; allSelection.objects _ allSelection.lastObject; }; MainFileToModel: PUBLIC PROC [model: SilModel, name: ROPE, relative: BOOL] ~ { FileToInternalRep[model: model, name: name, relative: relative]; -- raises SilKernel.SilError if filename is bad model.activeName _ name; }; ModelToFile: PUBLIC PROC [model: SilModel, name: ROPE, clipIt: BOOL _ FALSE, large: BOOL _ FALSE, xMin, yMin, xMax, yMax: INTEGER _ 0] RETURNS [wasClipped: BOOL _ FALSE] ~ { wasClipped _ InternalRepToFile[model, name, clipIt, large, xMin, yMin, xMax, yMax]; model.activeName _ name; }; ClearModel: PUBLIC ENTRY PROC [model: SilModel] ~ { IF allSelection.model = model THEN { allSelection _ [model: NIL, objects: allSelection.lastObject, numObjects: 0, xMin: 0, yMin: 0, xMax: 0, yMax: 0]; }; model.fgnd _ model.bkgnd _ NIL; model.deleted _ deleteInit; ClearMacros[model]; }; ClearMacros: PUBLIC PROC [model: SilModel] ~ { FOR c: MacroName IN MacroName DO model.macros[c].def _ NIL; ENDLOOP; FOR s: SilObject _ model.fgnd, s.rest WHILE s # NIL DO IF s.first.font IN InternalFileMacroFonts THEN DeleteObject[model, s]; ENDLOOP; }; CheckSelectionsForMacroDefs: PUBLIC ENTRY PROC [model: SilModel, disallowName: BOOL _ FALSE, name: MacroName _ '!] RETURNS [SelectionOk: BOOL _ TRUE] ~ { FOR s: SilObject _ allSelection.objects, s.first.selectObj WHILE s # allSelection.lastObject DO sobr: SilObjectRec _ s.first; SELECT sobr.font FROM IN InternalBoxFonts, IN InternalLibraryFonts => LOOP; IN InternalFileMacroFonts => { len: INT _ Rope.Length[sobr.value]; FOR i: INT IN [0..len) DO c: CHAR _ Rope.Fetch[sobr.value, i]; IF c =' THEN LOOP; --ignore spaces IF model.macros[c].def = NIL THEN RETURN[FALSE]; IF disallowName AND c = name THEN RETURN[FALSE]; ENDLOOP; }; ENDCASE; ENDLOOP; RETURN[TRUE]; }; DefineMacroFromSelection: PUBLIC PROC [model: SilModel, name: MacroName _ '!, xRef, yRef: INTEGER] ~ { sSel: SilSelection _ CopySelection[]; --CopySelection deletes the final dummy object FOR s: SilObject _ sSel.objects, s.rest WHILE s # sSel.lastObject DO s.first.xMin _ s.first.xMin - xRef; s.first.yMin _ s.first.yMin - yRef; s.first.xMax _ s.first.xMax - xRef; s.first.yMax _ s.first.yMax - yRef; ENDLOOP; model.macros[name].def _ sSel.objects; model.macros[name].xMin _ 0; model.macros[name].yMin _ 0; model.macros[name].xMax _ sSel.xMax - xRef; model.macros[name].yMax _ sSel.yMax - yRef; }; GetObjectList: PUBLIC PROC [model: SilModel _ NIL, mode: PictureType _ none, fontNumber: InternalFonts _ 8, c: CHAR _ '!] RETURNS [oblist: SilObject] ~ { InnerProc: PROC RETURNS [SilObject] = { SELECT mode FROM fgnd => RETURN[model.fgnd]; bkgnd => RETURN[model.bkgnd]; macro => SELECT fontNumber FROM IN InternalFileMacroFonts => RETURN[model.macros[c].def ]; IN InternalLibraryFonts => RETURN[allModelsMacros[fontNumber - internalMacroOffset][c].def ]; ENDCASE => RETURN[NIL]; ENDCASE => RETURN[NIL]; }; internalMacroOffset: FileMacroFonts ~ 4; IF mode=none THEN ERROR; -- temporary debugging statement oblist _ InnerProc [ ! RuntimeError.BoundsFault => GOTO NoSuchChar]; --raised if c is out of range EXITS NoSuchChar => oblist _ NIL; }; GetMacroBoundingBox: PUBLIC PROC [model: SilModel, fontNumber: InternalFonts _ 8, c: CHAR _ '!] RETURNS [xMin, yMin, xMax, yMax: INTEGER] ~ { InnerProc: PROC RETURNS [xMin, yMin, xMax, yMax: INTEGER] = { SELECT fontNumber FROM IN InternalFileMacroFonts => RETURN[model.macros[c].xMin, model.macros[c].yMin, model.macros[c].xMax, model.macros[c].yMax]; IN InternalLibraryFonts => RETURN[allModelsMacros[fontNumber-internalMacroOffset][c].xMin, allModelsMacros[fontNumber-internalMacroOffset][c].yMin, allModelsMacros[fontNumber-internalMacroOffset][c].xMax, allModelsMacros[fontNumber-internalMacroOffset][c].yMax]; ENDCASE => RETURN[0,0,0,0]; }; internalMacroOffset: FileMacroFonts ~ 4; [xMin: xMin, yMin: yMin, xMax: xMax, yMax: yMax] _ InnerProc [ ! RuntimeError.BoundsFault => GOTO NoSuchChar]; --raised if c is out of range EXITS NoSuchChar => RETURN[0,0,0,0]; }; GetSelection: PUBLIC PROC [] RETURNS [selection: SilSelection] ~ { RETURN[allSelection]; }; EvaluateSelection: PUBLIC ENTRY PROC ~ { IF allSelection.numObjects <= 0 THEN RETURN; allSelection.xMin _ allSelection.yMin _ LAST[INTEGER]; allSelection.xMax _ allSelection.yMax _ FIRST[INTEGER]; FOR sob: SilObject _ allSelection.objects, sob.first.selectObj WHILE sob # allSelection.lastObject DO allSelection.xMin _ MIN[allSelection.xMin, sob.first.xMin]; allSelection.yMin _ MIN[allSelection.yMin, sob.first.yMin]; allSelection.xMax _ MAX[allSelection.xMax, sob.first.xMax]; allSelection.yMax _ MAX[allSelection.yMax, sob.first.yMax]; ENDLOOP; }; CopySelection: PUBLIC ENTRY PROC [] RETURNS [copiedSelection: SilSelection] ~ { copiedSelection.model _ NIL; copiedSelection.objects _ NIL; copiedSelection.numObjects _ allSelection.numObjects; copiedSelection.xMin _ allSelection.xMin; copiedSelection.yMin _ allSelection.yMin; copiedSelection.xMax _ allSelection.xMax; copiedSelection.yMax _ allSelection.yMax; FOR s: SilObject _ allSelection.objects, s.first.selectObj WHILE s # allSelection.lastObject DO sobr: SilObjectRec _ s.first; sobr.selectObj _ NIL; copiedSelection.objects _ JoinObjectLists[copiedSelection.objects, LIST[sobr] ]; ENDLOOP; }; ObjectAtPos: PUBLIC PROC [model: SilModel, x, y: INTEGER] RETURNS [sob: SilObject _ NIL] ~ { InnerProc: PROC [obj: SilObject] = { FOR s: SilObject _ obj, s.rest WHILE s # NIL DO IF PointInBBox[x, y, s.first] THEN { newPerim: INTEGER _ BoxPerimeter[s.first]; IF newPerim < currentPerim THEN { sob _ s; currentPerim _ newPerim; }; }; ENDLOOP; }; currentPerim: INTEGER _ LAST[INTEGER]; InnerProc [model.fgnd]; InnerProc [model.bkgnd]; }; DefineSelectWithBox: PUBLIC PROC [model: SilModel, xMin, yMin, xMax, yMax: INTEGER] RETURNS [objXMin: INTEGER _ LAST[INTEGER], objYMin: INTEGER _ LAST[INTEGER], objXMax: INTEGER _ FIRST[INTEGER], objYMax: INTEGER _ FIRST[INTEGER]] ~ { InnerProc: PROC [obj: SilObject] = { FOR s: SilObject _ obj, s.rest WHILE s # NIL DO IF (s.first.xMin < xMin OR s.first.yMin < yMin OR s.first.xMax > xMax OR s.first.yMax > yMax) THEN LOOP; DefineSelectWithObject[model, s]; objXMin _ MIN[objXMin, s.first.xMin]; objYMin _ MIN[objYMin, s.first.yMin]; objXMax _ MAX[objXMax, s.first.xMax]; objYMax _ MAX[objYMax, s.first.yMax]; someBox _ TRUE; ENDLOOP; }; temp: INTEGER; someBox: BOOL _ FALSE; IF xMin > xMax THEN { temp _ xMin; xMin _ xMax; xMax _ temp; }; IF yMin > yMax THEN { temp _ yMin; yMin _ yMax; yMax _ temp; }; InnerProc [model.fgnd]; InnerProc [model.bkgnd]; IF someBox THEN RETURN ELSE RETURN[xMin, yMin, xMax, yMax]; }; DefineSelectWithObject: PUBLIC ENTRY PROC [model: SilModel, sob: SilObject] ~ { IF sob=NIL THEN RETURN; SELECT TRUE FROM model = allSelection.model AND NOT ObjectIsSelected[model, sob] => { sob.first.selectObj _ allSelection.objects; allSelection.objects _ sob; allSelection.numObjects _ allSelection.numObjects + 1; IF sob.first.selectObj = allSelection.lastObject THEN { allSelection.xMin _ sob.first.xMin; allSelection.yMin _ sob.first.yMin; allSelection.xMax _ sob.first.xMax; allSelection.yMax _ sob.first.yMax; } ELSE { allSelection.xMin _ MIN[allSelection.xMin, sob.first.xMin]; allSelection.yMin _ MIN[allSelection.yMin, sob.first.yMin]; allSelection.xMax _ MAX[allSelection.xMax, sob.first.xMax]; allSelection.yMax _ MAX[allSelection.yMax, sob.first.yMax]; }; }; model # allSelection.model => { LocalDeselectAll[]; sob.first.selectObj _ allSelection.lastObject; allSelection.model _ model; allSelection.objects _ sob; allSelection.numObjects _ 1; allSelection.xMin _ sob.first.xMin; allSelection.yMin _ sob.first.yMin; allSelection.xMax _ sob.first.xMax; allSelection.yMax _ sob.first.yMax; }; ENDCASE; }; Deselect: PUBLIC ENTRY PROC [model: SilModel, sob: SilObject] ~ { LocalDeselect[model: model, sob: sob]; }; LocalDeselect: PROC [model: SilModel, sob: SilObject] ~ { IF sob=NIL OR NOT ObjectIsSelected[model, sob] THEN RETURN; allSelection.numObjects _ allSelection.numObjects - 1; IF sob = allSelection.objects THEN allSelection.objects _ sob.first.selectObj ELSE { s, olds: SilObject; FOR s _ allSelection.objects, s.first.selectObj WHILE (s#sob AND s#allSelection.lastObject) DO olds _ s; ENDLOOP; IF s=allSelection.lastObject THEN ERROR; -- should have been guaranteed that sob is on the list, so this "can't happen" IF s=sob THEN olds.first.selectObj _ s.first.selectObj; }; sob.first.selectObj _ NIL; [allSelection.xMin, allSelection.yMin, allSelection.xMax, allSelection.yMax] _ BoundingBoxOfSelection[]; }; DeselectAll: PUBLIC ENTRY PROC [] ~ { LocalDeselectAll[]; }; LocalDeselectAll: PROC [] ~ { olds: SilObject; s: SilObject _ allSelection.objects; -- can't define s in following loop because loop must modify s WHILE s # allSelection.lastObject DO olds _ s; -- remember the s LIST s _ s.first.selectObj; -- make s a new LIST olds.first.selectObj _ NIL; -- NIL out the original s.first.selectLink ENDLOOP; allSelection.objects _ allSelection.lastObject; allSelection.model _ NIL; allSelection.numObjects _ 0; }; DeleteAndCacheSelection: PUBLIC ENTRY PROC [cache: BOOL _ FALSE] ~ { IF allSelection.model#NIL THEN { model: SilModel _ allSelection.model; nextObj: SilObject; IF cache THEN PushDeleteQueue[model]; FOR s: SilObject _ allSelection.objects, nextObj WHILE s # allSelection.lastObject DO nextObj _ s.first.selectObj; -- remember old selectObj DeleteObject[model, s]; s.rest _ NIL; s.first.selectObj _ NIL; IF cache THEN model.deleted[deleteTop] _ JoinObjectLists[model.deleted[deleteTop], s]; ENDLOOP; allSelection.objects _ allSelection.lastObject; allSelection.model _ NIL; allSelection.numObjects _ 0; }; }; DeleteAndCacheObject: PUBLIC PROC [model: SilModel, sob: SilObject] ~ { IF sob # NIL THEN { PushDeleteQueue[model]; model.deleted[deleteTop] _ sob; DeleteObject[model, sob]; model.deleted[deleteTop].rest _ NIL; model.deleted[deleteTop].first.selectObj _ NIL; }; }; DeleteObject: PROC [model: SilModel, sob: SilObject] ~ { IF sob#NIL THEN { oldObj: SilObject; LocalDeselect[model, sob]; SELECT TRUE FROM model.fgnd=sob => model.fgnd _ model.fgnd.rest; model.bkgnd=sob => model.bkgnd _ model.bkgnd.rest; ENDCASE => { oldObj _ model.fgnd; FOR s: SilObject _ model.fgnd, s.rest WHILE s # NIL DO IF s = sob THEN {oldObj.rest _ s.rest; RETURN;} ELSE oldObj _ s; ENDLOOP; oldObj _ model.bkgnd; FOR s: SilObject _ model.bkgnd, s.rest WHILE s # NIL DO IF s = sob THEN {oldObj.rest _ s.rest; RETURN;} ELSE oldObj _ s; ENDLOOP; }; }; }; Undelete: PUBLIC PROC [model: SilModel] RETURNS [sob: SilObject] ~ { nextSob: SilObject; DeselectAll[]; sob _ PopDeleteQueue[model]; FOR s: SilObject _ sob, nextSob WHILE s # NIL DO nextSob _ s.rest; IF s.first.font IN InternalBackgroundBoxFonts THEN {s.rest _ model.bkgnd; model.bkgnd _ s} ELSE {s.rest _ model.fgnd; model.fgnd _ s}; DefineSelectWithObject[model, s]; ENDLOOP; }; AddObjectToMainPicture: PUBLIC PROC [model: SilModel, sobr: SilObjectRec] RETURNS [sob: SilObject _ NIL] ~ { sob _ LIST[sobr]; IF sobr.font IN InternalBackgroundBoxFonts THEN model.bkgnd _ JoinObjectLists[model.bkgnd, sob] ELSE model.fgnd _ JoinObjectLists[model.fgnd, sob]; }; internalMacroOffset: FileMacroFonts ~ 4; InternalFontFromUserFont: PUBLIC PROC [font: UserFonts, bold: BOOL] RETURNS [ifont: InternalFonts] ~ { SELECT font FROM IN PresetFonts => RETURN[IF bold THEN font * 2 + 1 ELSE font * 2]; IN MacroFonts => RETURN[font + internalMacroOffset]; ENDCASE => ERROR; }; UserFontFromInternalFont: PUBLIC PROC [ifont: InternalFonts] RETURNS [font: UserFonts, bold: BOOL] ~ { SELECT ifont FROM IN InternalPresetFonts => RETURN[ifont / 2, FontIsBold[ifont]]; IN InternalMacroFonts => RETURN[ifont - internalMacroOffset, FALSE]; ENDCASE => ERROR; }; FontIsBold: PUBLIC PROC [font: InternalFonts] RETURNS [bold: BOOL] ~ { SELECT font FROM 1,3,5,7 => RETURN [TRUE]; 0,2,4,6,8,9,10,11,12,13,14,15 => RETURN[FALSE]; ENDCASE => RETURN[FALSE]; }; FontNameFromInternalFont: PUBLIC PROC [font: PresetFonts, fontType: FontType _ display] RETURNS [fName: ROPE] ~ { RETURN [IF fontType = display THEN allModelsFontNames[font] ELSE allModelsPFontNames[font]]; }; GetActiveFileName: PUBLIC PROC [model: SilModel] RETURNS [name: ROPE] ~ { IF (model.fgnd#NIL OR model.bkgnd#NIL) THEN RETURN [model.activeName] ELSE RETURN[""]; }; GetLastActiveFileName: PUBLIC PROC [model: SilModel] RETURNS [name: ROPE] ~ { RETURN [model.activeName]; }; MarkFileAsEdited: PUBLIC PROC [model: SilModel] RETURNS [deletedBuildBoxes: BOOL _ FALSE] ~ { IF model.modelIsBuilt THEN { buildMarksDeleted: ARRAY BuildMarkIndex OF BOOL _ ALL[FALSE]; FOR s: SilObject _ model.fgnd, s.rest WHILE s # NIL DO FOR i: BuildMarkIndex IN BuildMarkIndex DO IF NOT buildMarksDeleted[i] AND buildMarks[i].xMin = s.first.xMin AND buildMarks[i].yMin = s.first.yMin AND buildMarks[i].xMax = s.first.xMax AND buildMarks[i].yMax = s.first.yMax THEN { buildMarksDeleted[i] _ TRUE; DeleteObject[model, s]; }; ENDLOOP; ENDLOOP; model.modelIsBuilt _ FALSE; RETURN [TRUE]; }; }; UnbuiltPassByte1: CHAR ~ '9; UnbuiltPassByte2: CHAR ~ 'r; BuiltPassByte1: CHAR ~ '9; BuiltPassByte2: CHAR ~ 's; LargePassByte1: CHAR ~ '9; LargePassByte2: CHAR ~ 'l; -- new password for large format files SilBlockRef: TYPE ~ REF SilBlock; SilBlock: TYPE ~ MACHINE DEPENDENT RECORD [ leftChar, rightChar: CHAR, state: FourBits, xMin: TwelveBits, yMin: SixteenBits, color: FourBits, xMax: TwelveBits, font: FourBits, italic: OneBit, yMax: ElevenBits ]; SevenBits: TYPE ~ [0..177B]; FourBits: TYPE ~ [0..17B]; TwelveBits: TYPE ~ [0..7777B]; SixteenBits: TYPE ~ [0..177777B]; OneBit: TYPE ~ [0..1]; ElevenBits: TYPE ~ [0..3777B]; LargeSilBlockRef: TYPE ~ REF LargeSilBlock; --new format for large format files LargeSilBlock: TYPE ~ MACHINE DEPENDENT RECORD [ leftChar, rightChar: CHAR, xMin: INTEGER, yMin: INTEGER, xMax: INTEGER, yMax: INTEGER, color: FourBits, font: FourBits, italic: OneBit, pad: SevenBits _ 0 ]; mainElementName: CHAR ~ 377C; --this as name of object means it's part of main picture FileToInternalRep: PROC [model: SilModel, name: ROPE, relative: BOOL _ FALSE, macroFile: BOOL _ FALSE, picFont: LibraryFonts _ 5] ~ { sBlock: SilBlockRef _ NEW[SilBlock]; largesBlock: LargeSilBlockRef _ NEW[LargeSilBlock]; passw1, passw2: CHAR; GetNextSilObject: PROC [s: STREAM] RETURNS [name: CHAR, sob: SilObject] ~ { ENABLE IO.EndOfStream => ERROR SilKernel.SilError[BadFile];-- send a civilized message if the file ends up lying to us ReadChar: PROC [] RETURNS [c: CHAR] ~ { RETURN [s.GetChar[] ]; }; len: INTEGER; sobr: SilObjectRec; TRUSTED { bytesGot: INT _ s.UnsafeGetBlock[[base: base, startIndex: 0, count: count]]; IF bytesGot # count THEN ERROR SilKernel.SilError[BadFile]; }; IF large THEN { name _ IF largesBlock.leftChar = mainElementName THEN ' ELSE largesBlock.rightChar; sobr.xMin _ largesBlock.xMin; sobr.yMin _ largesBlock.yMin; sobr.xMax _ largesBlock.xMax; sobr.yMax _ largesBlock.yMax; sobr.color _ largesBlock.color; sobr.font _ largesBlock.font; sobr.italic _ largesBlock.italic = 1; } ELSE { name _ IF sBlock.leftChar = mainElementName THEN ' ELSE sBlock.rightChar; sobr.xMin _ sBlock.xMin; sobr.yMin _ sBlock.yMin; sobr.xMax _ sBlock.xMax; sobr.yMax _ sBlock.yMax; sobr.color _ sBlock.color; sobr.font _ sBlock.font; sobr.italic _ sBlock.italic = 1; }; IF sobr.font IN InternalRopeFonts THEN { -- this is a string, handle its chars len _ s.GetChar[] - 0C; -- first byte is length IF macroFile AND name = ' THEN s.SetIndex[s.GetIndex[] + len] ELSE sobr.value _ Rope.FromProc [len, ReadChar, maxRopeLen]; IF len MOD 2 = 0 THEN --Make sure start next object on a word boundary [] _ s.GetChar[]; --Flush the extra slot for a Char }; sob _ LIST[sobr]; }; s: STREAM _ FS.StreamOpen[name ! FS.Error => ERROR SilKernel.SilError[BadFileName]]; seen: PACKED ARRAY MacroName OF BOOL _ ALL[FALSE]; markX: INTEGER _ SilDisplayCursors.GetMarkX[]; -- for relative input markY: INTEGER _ SilDisplayCursors.GetMarkY[]; large: BOOL _ FALSE; base: LONG POINTER _ NIL; count: INTEGER _ 0; passw1 _ s.GetChar[]; passw2 _ s.GetChar[]; IF macroFile = FALSE THEN { IF (passw1 # UnbuiltPassByte1 AND passw1 # BuiltPassByte1 AND passw1 # LargePassByte1) OR (passw2 # UnbuiltPassByte2 AND passw2 # BuiltPassByte2 AND passw2 # LargePassByte2) THEN { MessageWindow.Append["Illegal Sil File password, proceeding anyway. ", TRUE]; MessageWindow.Blink[]; model.modelIsBuilt _ FALSE; } ELSE model.modelIsBuilt _ (passw1 = BuiltPassByte1 AND passw2 = BuiltPassByte2); }; large _ passw2=LargePassByte2; --reading large format file base _ IF large THEN LOOPHOLE[largesBlock] ELSE LOOPHOLE[sBlock]; -- used by GetNextSilObject count _ IF large THEN SIZE[LargeSilBlock]*Basics.bytesPerWord ELSE SIZE[SilBlock]*Basics.bytesPerWord; -- used by GetNextSilObject WHILE NOT s.EndOf[] DO name: CHAR; sob: SilObject; [name, sob] _ GetNextSilObject[s]; SELECT TRUE FROM NOT macroFile AND name = ' => { --Main file object main picture IF relative THEN { --modify object's position sob.first.xMin _ sob.first.xMin + markX; sob.first.yMin _ sob.first.yMin + markY; sob.first.xMax _ sob.first.xMax + markX; sob.first.yMax _ sob.first.yMax + markY; }; IF sob.first.font IN InternalBackgroundBoxFonts THEN model.bkgnd _ JoinObjectLists[model.bkgnd, sob] ELSE model.fgnd _ JoinObjectLists[model.fgnd, sob]; }; NOT macroFile AND name # ' => { --Main file object macro picture IF NOT seen[name] THEN { -- new macro def model.macros[name].def _ NIL; seen[name] _ TRUE; model.macros[name].xMin _ sob.first.xMin; model.macros[name].yMin _ sob.first.yMin; model.macros[name].xMax _ sob.first.xMax; model.macros[name].yMax _ sob.first.yMax; } ELSE { -- add object to macro IF sob.first.xMin < model.macros[name].xMin THEN model.macros[name].xMin _ sob.first.xMin; IF sob.first.yMin < model.macros[name].yMin THEN model.macros[name].yMin _ sob.first.yMin; IF sob.first.xMax > model.macros[name].xMax THEN model.macros[name].xMax _ sob.first.xMax; IF sob.first.yMax > model.macros[name].yMax THEN model.macros[name].yMax _ sob.first.yMax; }; model.macros[name].def _ JoinObjectLists[model.macros[name].def, sob]; }; macroFile AND name = ' => --Library file main picture object. Ignore. LOOP; macroFile AND name # ' => { --Library file macro picture object. Add to global macros. IF NOT seen[name] THEN { allModelsMacros[picFont][name].def _ NIL; seen[name] _ TRUE; allModelsMacros[picFont][name].xMin _ sob.first.xMin; allModelsMacros[picFont][name].yMin _ sob.first.yMin; allModelsMacros[picFont][name].xMax _ sob.first.xMax; allModelsMacros[picFont][name].yMax _ sob.first.yMax; } ELSE { -- add to library file macro picture object IF sob.first.xMin < allModelsMacros[picFont][name].xMin THEN allModelsMacros[picFont][name].xMin _ sob.first.xMin; IF sob.first.yMin < allModelsMacros[picFont][name].yMin THEN allModelsMacros[picFont][name].yMin _ sob.first.yMin; IF sob.first.xMax > allModelsMacros[picFont][name].xMax THEN allModelsMacros[picFont][name].xMax _ sob.first.xMax; IF sob.first.yMax > allModelsMacros[picFont][name].yMax THEN allModelsMacros[picFont][name].yMax _ sob.first.yMax; }; allModelsMacros[picFont][name].def _ JoinObjectLists[allModelsMacros[picFont][name].def, sob]; }; ENDCASE; ENDLOOP; s.Close[]; }; InternalRepToFile: PROC [model: SilModel, name: ROPE, clipIt: BOOL _ FALSE, large: BOOL _ FALSE, xMin , yMin, xMax, yMax: INTEGER _ 0] RETURNS [wasClipped: BOOL _ FALSE] ~ { base: LONG POINTER _ NIL; count: INTEGER _ 0; sBlock: SilBlockRef _ NEW[SilBlock]; largesBlock: LargeSilBlockRef _ NEW[LargeSilBlock]; WriteNextSilObject: PROC [name: CHAR, sobr: SilObjectRec] ~ { IF large THEN { -- write in new large format IF name = ' THEN largesBlock.leftChar _ largesBlock.rightChar _ mainElementName ELSE { largesBlock.leftChar _ 0C; largesBlock.rightChar _ name; }; largesBlock.xMin _ sobr.xMin; largesBlock.yMin _ sobr.yMin; largesBlock.color _ sobr.color; largesBlock.xMax _ sobr.xMax; largesBlock.font _ sobr.font; largesBlock.italic _ IF sobr.italic THEN 1 ELSE 0; largesBlock.yMax _ sobr.yMax; } ELSE { -- same old format IF name = ' THEN sBlock.leftChar _ sBlock.rightChar _ mainElementName ELSE { sBlock.leftChar _ 0C; sBlock.rightChar _ name; }; sBlock.xMin _ sobr.xMin; sBlock.yMin _ sobr.yMin; sBlock.color _ sobr.color; sBlock.xMax _ sobr.xMax; sBlock.font _ sobr.font; sBlock.italic _ IF sobr.italic THEN 1 ELSE 0; sBlock.yMax _ sobr.yMax; }; TRUSTED { s.UnsafePutBlock[[base: base, startIndex: 0, count: count]];}; IF sobr.font IN InternalRopeFonts THEN { len: NAT _ sobr.value.Length[]; s.PutChar[len + 0C]; s.PutRope[sobr.value]; IF len MOD 2 = 0 THEN s.PutChar[0C]; }; }; s: STREAM _ FS.StreamOpen[fileName: name, accessOptions: $create, createByteCount: BytesInModel[model, large] ! FS.Error => ERROR SilKernel.SilError[BadFileName]; ]; IF large THEN { s.PutChar[LargePassByte1]; s.PutChar[LargePassByte2]; base _ LOOPHOLE[largesBlock]; count _ SIZE[LargeSilBlock]*Basics.bytesPerWord; } ELSE { base _ LOOPHOLE[sBlock]; count _ SIZE[SilBlock]*Basics.bytesPerWord; IF model.modelIsBuilt THEN { s.PutChar[BuiltPassByte1]; s.PutChar[BuiltPassByte2]; } ELSE { s.PutChar[UnbuiltPassByte1]; s.PutChar[UnbuiltPassByte2]; }; }; IF NOT (clipIt OR large) THEN { -- limit the file to the size the old format can handle clipIt _ TRUE; xMin _ yMin _ 0; xMax _ LAST[TwelveBits]; yMax _ LAST[ElevenBits]; }; FOR c: MacroName IN MacroName DO FOR s: SilObject _ model.macros[c].def, s.rest WHILE s # NIL DO WriteNextSilObject[c, s.first]; ENDLOOP; ENDLOOP; FOR s: SilObject _ model.fgnd, s.rest WHILE s # NIL DO SELECT TRUE FROM large AND clipIt => ERROR; --this can happen but shouldn't (clipIt AND s.first.xMin >= xMin AND s.first.yMin >= yMin AND s.first.xMax <= xMax AND s.first.yMax <= yMax) => WriteNextSilObject[' , s.first]; NOT clipIt => WriteNextSilObject[' , s.first]; -- this case applies when large=TRUE ENDCASE => wasClipped _ wasClipped OR TRUE; ENDLOOP; FOR s: SilObject _ model.bkgnd, s.rest WHILE s # NIL DO SELECT TRUE FROM large AND clipIt => ERROR; --this can happen but shouldn't (clipIt AND s.first.xMin >= xMin AND s.first.yMin >= yMin AND s.first.xMax <= xMax AND s.first.yMax <= yMax) => WriteNextSilObject[' , s.first]; NOT clipIt => WriteNextSilObject[' , s.first]; -- this case applies when large=TRUE ENDCASE => wasClipped _ wasClipped OR TRUE; ENDLOOP; s.Close[]; }; BytesInModel: PROC [model: SilModel, large: BOOLEAN] RETURNS [byteLength: INT] ~ { ObjectByteLength: PROC [sobr: SilObjectRec] RETURNS [obLength: INT] ~ { IF sobr.font IN InternalBoxFonts THEN RETURN[objectSize]; obLength _ Rope.Length[sobr.value] + 1 + objectSize; --rope + length byte + all the rest IF obLength MOD 2 # 0 THEN obLength _ obLength + 1; }; objectSize: INT _ IF large THEN SIZE[LargeSilBlock]*Basics.bytesPerWord ELSE SIZE[SilBlock]*Basics.bytesPerWord; byteLength _ 2; --For the password FOR s: SilObject _ model.fgnd, s.rest WHILE s # NIL DO byteLength _ byteLength + ObjectByteLength[s.first]; ENDLOOP; FOR s: SilObject _ model.bkgnd, s.rest WHILE s # NIL DO byteLength _ byteLength + ObjectByteLength[s.first]; ENDLOOP; FOR c: MacroName IN MacroName DO FOR s: SilObject _ model.macros[c].def, s.rest WHILE s # NIL DO byteLength _ byteLength + ObjectByteLength[s.first]; ENDLOOP; ENDLOOP; }; JoinObjectLists: PROC [base, extension: SilObject] RETURNS [SilObject] ~ { FOR s: SilObject _ extension, s.rest WHILE s # NIL DO IF s.rest = NIL THEN { s.rest _ base; RETURN [extension]; }; ENDLOOP; RETURN [base]; }; PushDeleteQueue: PROC [model: SilModel] ~ { FOR i: DeletesToKeep _ deleteBottom, i - 1 WHILE i > deleteTop DO model.deleted[i] _ model.deleted[i - 1]; ENDLOOP; model.deleted[deleteTop] _ NIL; }; PopDeleteQueue: PROC [model: SilModel] RETURNS [sob: SilObject] ~ { sob _ model.deleted[deleteTop]; FOR i: DeletesToKeep _ deleteTop, i + 1 WHILE i < deleteBottom DO model.deleted[i] _ model.deleted[i + 1]; ENDLOOP; model.deleted[deleteBottom] _ NIL; }; PointInBBox: PROC [x, y: INTEGER, sobr: SilObjectRec] RETURNS [in: BOOL] = INLINE { RETURN [NOT (y > sobr.yMax OR x > sobr.xMax OR y < sobr.yMin OR x < sobr.xMin)]; }; BoxPerimeter: PROC [sobr: SilObjectRec] RETURNS [perim: INTEGER] = INLINE { RETURN[ (sobr.xMax - sobr.xMin)*2 + (sobr.yMax - sobr.yMin)*2 ] }; ObjectIsSelected: PUBLIC PROC [model: SilModel, sob: SilObject] RETURNS [selected: BOOL] = { RETURN[model = allSelection.model AND sob.first.selectObj # NIL]; }; BoundingBoxOfSelection: PROC [] RETURNS [xMin, yMin, xMax, yMax: INTEGER] ~ { IF allSelection.numObjects=0 THEN RETURN[0,0,0,0]; xMin _ yMin _ LAST[INTEGER]; xMax _ yMax _ FIRST[INTEGER]; FOR s: SilObject _ allSelection.objects, s.first.selectObj WHILE s # allSelection.lastObject DO { xMin _ MIN[xMin, s.first.xMin]; yMin _ MIN[yMin, s.first.yMin]; xMax _ MAX[xMax, s.first.xMax]; yMax _ MAX[yMax, s.first.yMax]; }; ENDLOOP; }; END. `SilFileImpl.mesa Last Edited by: Larrabee, March 29, 1984 1:03:38 pm PST Last Edited by: Pier, December 18, 1985 5:07:50 pm PST This module is the custodian of the internal data structures built for Sil files. Any creations, deletions, or queries will pass through here. GLOBAL VARIABLES: Data common to all Sil files which are being viewed or edited at any one time: While there may be many sil files open and in use at any given time, they should all be using the same library and fonts (they are set in the user profile). THE Selection. There can only be one Sil selection at any given time. Individual data for each Sil file being edited: This is the concrete representation of an opaque type defined in SilKernel. Create an empty Sil Model. This procedure will initialize all of Sil's internal structure by reading in any library files and retreiving the names of any fonts. ! SilError with explain = BadFile: One of the library files contained some sort sort of lie concerning its length (such as a non-integral number of Sil objects). BadFileName: a Sil file name was given in the user profile which referenced a file which could not be opened. First get the predefined fonts . . . If there is a one element list, that element is used for both display and print font If there is a more than one element list (should be only 2), the first element is the display font and the second element is the print font Now get the Library fonts (if there are any) working . . . Now read in the the Library files specified. put a dummy object on the END of the Select List This should be called after Sil has been initialized, and new model has been created. This proc will add any data in the named file to the main picture data currently in the model provided. If relative, file objects will be origined at (MarkX, MarkY) instead of (0, 0). ! SilError with explain = BadFile: This Sil file contained some sort sort of lie concerning its length (such as a non-integral number of Sil objects). BadFileName: No file could be opened for Read-only given this name. Do not catch SilError; will be caught by caller. This will output all the information in this model to the file name given. If clipIt then only output those objects which lie entirely inside the region given. raises SilKernel.SilError with explain = BadFileName: No file could be opened with this name. Do not catch SilError; will be caught by caller. Don't assign a new model.activeName if writing fails. This will delete the model's main picture as well as any macro definitions. NEVER change allSelection.lastObject This will cause macro definitions in the model to be discarded, along with any macro objects. Note that macros that contain bkgnd boxes are themselves always in the fgnd Check the selection to see if it references macro's that are not defined in model. If disallowName, make sure that the selection does not reference any macro's with name name. Add the macro definition with name name to the model given. Return the pertinent object list (main, or macro) out of the model. If mode is main, ignore fontNumber and c. The InnerProc is necessary in order to catch RuntimeError.BoundsFault. Return the bounding box of the macro given.The InnerProc is needed to catch RuntimeError.BoundsFault. Return a copy of the selection which has all the objects of the real selection, but no mention of their model and not linked by their selectObj fields, and no lastObject record. In model, find the object at with the smallest bounding box and return that object. If there is no object at return NIL. In model, find all the objects inside the box defined by xMin, yMin, xMax, and yMax and add them to the selection. If there are no objects in the box, add no elements to the selection. Return the actual minimum and maximum dimensions of objects inside the box (if there are no objects within the box return the minimum and maximums provided). Sob is added to the previously selected objects. If Sob is from a new model flush the old selection and start a new one. Deselect sob. Deselect all objects. Delete all objects which are selected. IF cache, put deleted objects on delete queue. Delete this Specific object (delink it from the selection list if necessary). Delete this Specific object (delink it from the selection list if necessary). Undelete the most recently deleted objects for this model and make it the selection. Add new object to the main picture of model. Return the internal font corresponding to the user font and boldness given. Return the user font and boldness corresponding to the internal font given. True if this internal font corresponds to a bold user font. Return the correct Font name for the given Sil font. Get the name of the currently active Sil file for this model. Get the name of the currently active Sil file for this model. Mark this file as edited. If this is a "built" file the build marks will be deleted and the display process should know about this. Types for use in Input and Output of Sil Files: Passwords indicating Sil state: Read the entire contents of file with name and arrange the component records in the internal representation. If macroFile is FALSE then this file will contain a main picture, and several macro definitions which will be stored in the model provided (picFont will be ignored). If macroFile is TRUE then the main picture will be ignored, and the macro definitions will be stored as pictures in the global variable allModelMacros (the model provided will be ignored). Any old macro definitions which conflict with old ones will be replaced, but old definitions which do not conflict will remain. If relative is true, objects in the main picture will be placed relative to (MarkX, MarkY) instead of (0, 0). Return the Sil object which appears next in the file being read. Also, return the object's name, which will be ' (space) for main pictures, and a printing character for objects which are part of macro definitions. This procedure exists so it can be passed to Rope.FromProc, below. if you have a string, get its value unless it is a macroFile string, in which case you igonre it by skipping its characters. And now, a little array to tell us which macro definitions are new . . . Right here we are going to read password(s) and do something with it, someday. Create a file with name and place all of the internal data concerning the main picture and the macrodefinitions into it. If clipIt then only output those objects which lie entirely inside the region given. If large, output in the new CedarSil large format. Write a Sil Object to the open file. If it is a main picture object the name will be ' (space), otherwise the name will be the name of the macro definition that the picture belongs to. If large, write in new large format. Now check to see if a rope should be output Output the chars of the rope Make sure start next object on a word boundary Open the file. output the password. First, handle the case where an unclipped old format file is being written Now the macro definitions Now the main picture objects Now close the file Return what the current length of the file to be generated from model (writing out the password, main picture, and macro definitions), and, for rope objects, one byte for each character in the rope, as well as a byte for the length. Every object must generate an even number of bytes. Return the number of bytes necessary to output this object bytes for every object, and, for rope objects, one byte for each character in the rope, as well as a byte for the length. Every object must generate an even number of bytes. The main picture . . . Now the macro definitions this routine is more efficient if extension is shorter than base Could return NIL. Return TRUE if is inside the bounding box of sobr (the border of the box counts as inside the box). Return the perimeter of the bounding box of sobr. TRUE if sob is already selected. Uses the observation that if an object has a non-NIL selectObj link, then it must be selected. There is a dummy object on the end of the selections so that even the last selected object will have a non-NIL select link. Return the bounding box of the currently selected objects Κ#’˜Icodešœ™K™7K™6K™K™‘K™šΟk ˜ Kšœœ˜Kšœ œ˜*Kšœœ˜Kšœœ8œ7˜}Kšœœ˜$Kšœœ-œ˜=Kšœ œ˜!Kšœ˜Kšœ˜K˜ K˜ Kšœ œ˜'Kšœ˜—K˜Kšœ œœœœœTœœœ ˜³K˜KšΟb™KšžN™NK™œK™Kšœœœ ˜/Kšœœ œœΟc˜HKšœœ œœŸ˜FK˜K™EKšœ˜K˜Kšž/™/K™MKšœ œœ ˜!š œ œœ œœ˜-KšœœŸ0˜GKšœœŸ:˜RK˜KšœŸ˜.Kšœ œŸ$˜5KšœŸ*˜˜cšœœ ˜$Jšœ Ÿ˜ JšœŸ˜,JšœœŸ*˜FKšœ˜—Kšœ#œ ˜/Kšœœ˜Kšœ˜Kšœ˜K˜—š ’œœ œœœ˜DK™UK™šœœœ˜ Jšœ%˜%Jšœ˜Kšœœ˜%šœ.œœ ˜UJšœŸ˜6J˜Jšœ œœ˜'JšœœI˜VJšœ˜—Jšœ#œ ˜/Jšœœ˜Jšœ˜J˜—K˜K™—š’œœœ$˜GK™MK™šœœœ˜K˜Kšœ˜K˜Kšœ œ˜$Kšœ+œ˜/K˜—K˜K˜—š’ œœ$˜8K™MK™šœœœ˜Kšœ˜K˜šœœ˜Kšœ/˜/Kšœ2˜2šœ˜ Kšœ˜šœ#œœ˜6Kšœ œœ˜/Kšœ ˜Kšœ˜—Kšœ˜šœ$œœ˜7Kšœ œœ˜/Kšœ ˜Kšœ˜—K˜——K˜—K˜K˜—š’œœœœ˜DK™TK™Kšœ˜Kšœ˜Kšœ˜šœœœœ˜1Kšœ˜Kšœœœ(˜ZKšœ'˜+Kšœ!˜!Kšœ˜—K˜K˜—š ’œœœ'œœ˜lK™,K˜Kšœœ˜Kšœ œœ0˜_Kšœ/˜3˜K˜——šœ(˜(K˜—š ’œœœœœ˜fK™KK™šœ˜Kš œœœœœ ˜BKšœœ˜4Kšœœ˜—K˜K™—š ’œœœœœ˜fK™KK™šœ˜Kšœœ˜?Kšœœœ˜DKšœœ˜—K˜K™—š ’ œœœœœ˜FK™;K™šœ˜Kšœ œœ˜Kšœ!œœ˜/Kšœœœ˜—Kšœ˜—š ’œœœ1œ œ˜qK™4Kšœœœœ˜\K˜K˜—š ’œœœœœ˜IK™=K™Kšœœ œ œœœœœ˜VK˜K˜—š ’œœœœœ˜MK™=K™Kšœ˜K˜K˜—š ’œœœœœœ˜]K™„K™šœœ˜Kš œœœœœœ˜=šœ#œœ˜6šœœ˜*šœœœ$œ#œ#œ#œ˜»Kšœœ˜K˜K˜—Kšœ˜—Kšœ˜—Kšœœ˜Kšœœ˜K˜—K˜—K™K™/K™ Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜KšœœŸ&˜AKšœ œœ ˜!š œ œœ œœ˜,Kšœœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ œ ˜Kšœ œ ˜Kšœ œ˜Kšœ œ˜!Kšœ œ ˜Kšœ œ˜KšœœœŸ#˜Oš œœœ œœ˜1Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ˜Kšœ˜Kšœ˜K˜Kšœ˜—Kšœœ Ÿ8˜WK˜š’œœœ œœ œœ ˜…Kšœ©Οrœcžœ£™ΐK™Kšœœ ˜$Kšœ œ˜3Kšœœ˜K˜š ’œœœœœ˜KKšœΦ™ΦK™KšœœœŸ;˜vK˜š’œœœœ˜'K™BKšœ˜Kšœ˜K˜—Kšœœ˜ K˜šœ˜ Kšœ œ?˜LKšœœœ˜;K˜K˜—šœœ˜Kšœœ(œœ˜TKšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ%˜%K˜—šœ˜Kšœœ#œœ˜JKšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ K˜—K˜K™|šœ œœŸ%˜NKšœŸ˜/Kšœ œ œ œ8˜{šœœœŸ0˜GKšœŸ#˜4—K˜—Kšœœ˜Kšœ˜K˜—š œœœœ œ"˜TKšœH™H—Kš œœœ œœœœ˜2Kšœœ!Ÿ˜DKšœœ ˜.Kšœœœ˜Kšœœœœ˜šœœ˜Kšœ,ž!œ™N—Kšœ+˜+šœ œœ˜šœœœœœœœ˜΅KšœGœ˜MK˜Kšœœ˜K˜—Kšœ/œ˜PK˜K˜—KšœŸ˜:Kš œœœœœœ Ÿ˜]Kš œœœœ$œœ Ÿ˜‚K˜šœœ œ˜Kšœœ˜ Kšœ˜Kšœ"˜"K˜šœœ˜šœ œŸ!˜Cšœ œŸ˜-K˜(Kšœ(˜(Kšœ(˜(Kšœ(˜(K˜—Kšœœœ0˜dKšœ/˜3K˜—šœ œŸ ˜Cšœœ œŸ˜)Kšœœ˜Kšœ œ˜Kšœ)˜)Kšœ)˜)Kšœ)˜)Kšœ)˜)Kšœ˜—šœŸ˜šœ*˜0Kšœ)˜)—šœ*˜0Kšœ)˜)—šœ*˜0Kšœ)˜)—šœ*˜0Kšœ)˜)—K˜—KšœF˜FK˜—šœ œŸ+˜HKšœ˜—šœ œŸ:˜Wšœœ œ˜Kšœ%œ˜)Kšœ œ˜Kšœ5˜5Kšœ5˜5Kšœ5˜5Kšœ5˜5K˜—šœŸ+˜2šœ6˜˜>—šœ˜Kšœ˜Kšœ˜K˜—Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœœ œœ˜3Kšœ˜K˜—šœŸ˜Kšœ œ˜Kšœ4˜4šœ˜K˜K˜K˜—Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœœ œœ˜.Kšœ˜K˜—K˜šœA˜HK˜K™+—šœ œœ˜(Kšœœ˜šœ˜K™—˜KšŸ.™.—Kšœœœ˜$K˜—Kšœ˜—K˜Kšœž™šœœ˜ Kšœcœ œ$˜š—™Kšœ™—šœœ˜Kšœ˜Kšœ˜Kšœœ˜Kšœœ$˜0K˜—šœ˜Kšœœ ˜Kšœœ˜+šœœ˜Kšœ˜Kšœ˜K˜—šœ˜Kšœ˜Kšœ˜K˜—K˜K™J—š œœ œœŸ7˜WKšœ œ˜K˜Kšœœœ ˜2K˜—™K™—šœœ ˜ šœ,œœ˜?Kšœ˜Kšœ˜—Kšœ˜K™—šœ#œœ˜6šœœ˜Kšœœ œŸ˜:Kš œœœœœ:˜‘K•StartOfExpansion[]šœ-Ÿ$˜TKšœœ˜+—Kšœ˜—šœ$œœ˜7šœœ˜Kšœœ œŸ˜:Kš œœœœœ:˜‘K–[]šœ-Ÿ$˜TKšœœ˜+—Kšœ˜—K™K˜ K˜K˜—š ’ œœœœœ˜RK™žK˜š’œœœ œ˜GK™κK˜Kšœ œœœ ˜9Kšœ6Ÿ#˜YKšœ œœ˜3K˜—K˜Kš œ œœœœ$œœ˜pšœŸ˜#K™—šœ#œœ˜6Kšœ4˜4Kšœ˜—šœ$œœ˜7Kšœ4˜4Kšœ˜—K™šœœ ˜ šœ,œœ˜?Kšœ4˜4Kšœ˜—Kšœ˜—K˜K˜—š’œœœ˜JK™@šœ"œœ˜5šœ œœ˜Kšœ˜Kšœ ˜K˜—Kšœ˜—Kšœ˜K˜K˜—š’œœ˜+šœ(œ˜AK˜(Kšœ˜—Kšœœ˜K˜K˜—š’œœœ˜CK™Kšœ˜šœ%œ˜AK˜(Kšœ˜—Kšœœ˜"K˜K˜—š ’ œœœœœœ˜SKšœj™jK™Kš œœœœœ˜QKšœ˜K˜—š ’ œœœ œœ˜KKšœ1™1K™Kšœ:˜@K˜K˜—š ’œœœ#œ œ˜\K™όK™Kšœœœ˜AKšœ˜K˜—š’œœœœ˜MK™9Kšœœœ ˜2Kšœœœ˜Kšœœœ˜šœ8œœ œ˜aKšœœ˜ Kšœœ˜Kšœœ˜ Kšœœ˜K˜Kšœ˜—K˜K˜—Kšœ˜—…—nή²Π