<> <> <> <<>> <> <<>> 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] ~ { < with the smallest bounding box and return that object. If there is no object at return 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]; }; }; <<>> <> << Passwords indicating Sil state:>> 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 { < is inside the bounding box of sobr (the border of the box counts as inside the box).>> <<>> 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.