<> <> DIRECTORY AMEvents USING [Debugging, Debugged], CIFS USING [Error], CedarSnapshot USING [CheckpointProc, Register, RollbackProc], Convert USING [Value, ValueToRope], IO USING [GetCard, EndOfStream, Close, CR, CreateFilterCommentsStream, GetChar, GetIndex, int, PeekChar, PutF, RIS, rope, STREAM, time, TAB, SP, GetToken, SkipOver, TokenProc, BreakProc, WhiteSpace], IOExtras USING [GetLine], IconRegistry USING [Failure], Icons USING [NewIconFromFile, IconFlavor], FileIO USING [Open, OpenFailed], MessageWindow USING [Append, Blink], PageFault USING [AddressFault], Process USING [Detach], Rope USING [Cat, Equal, IsEmpty, ROPE], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, RegisterEventProc], ViewerOps USING [FindViewer, RestoreViewer] ; IconRegistryImpl: CEDAR MONITOR IMPORTS AMEvents, CedarSnapshot, CIFS, Convert, FileIO, IO, IOExtras, MessageWindow, PageFault, Process, Rope, Icons, ViewerEvents, ViewerOps EXPORTS IconRegistry = BEGIN <> IconList: TYPE = LIST OF IconEntry; IconEntry: TYPE = REF IconRecord; IconRecord: TYPE = RECORD[ iconName: Rope.ROPE _ NIL, fileName: Rope.ROPE _ NIL, index: CARDINAL _ LAST[CARDINAL], flavor: Icons.IconFlavor _ unInit, position: INT _ -1 -- in catalogue ]; Failed: PUBLIC ERROR [why: IconRegistry.Failure, reason: Rope.ROPE] = CODE; <> RegisterIcon: PUBLIC ENTRY PROC [iconName: Rope.ROPE, fileName: Rope.ROPE, index: CARDINAL, saveInCatalogue: BOOL _ FALSE] = { <> ENABLE UNWIND => NULL; entry: IconEntry; IF iconList = NIL THEN Parse[]; -- first time FOR l: IconList _ iconList, l.rest UNTIL l = NIL DO IF Rope.Equal[l.first.iconName, iconName, FALSE] THEN { IF Rope.Equal[l.first.fileName, fileName, FALSE] AND l.first.index = index THEN RETURN[]; -- multiple registrations are nops. LOOP; }; REPEAT FINISHED => {entry _ NEW[IconRecord _ []]; iconList _ CONS[entry, iconList];} ENDLOOP; entry^ _ [iconName: iconName, fileName: fileName, index: index, flavor: unInit, position: -1]; -- multiple entries permitted, guys registered later, i.e. appear earlier on list, take precedence. IF saveInCatalogue THEN WriteEntry[entry]; }; GetIcon: PUBLIC ENTRY PROC [iconName: Rope.ROPE, default: Icons.IconFlavor _ unInit] RETURNS [Icons.IconFlavor] = { <> <> ENABLE UNWIND => NULL; IF iconList = NIL THEN Parse[! Failed => IF default # unInit THEN GOTO Default]; -- first time FOR l: IconList _ iconList, l.rest UNTIL l = NIL DO IF Rope.Equal[l.first.iconName, iconName, FALSE] THEN { IF l.first.flavor = unInit THEN l.first.flavor _ Icons.NewIconFromFile[l.first.fileName, l.first.index ! CIFS.Error => IF code # noSuchFile THEN REJECT ELSE IF default # unInit THEN GOTO Default ELSE ERROR Failed[fileNotFound, l.first.fileName]; PageFault.AddressFault => IF default # unInit THEN GOTO Default ELSE ERROR Failed[invalidIndex, Convert.ValueToRope[[signed[l.first.index]]]]; ]; -- NewIconFromFile raises this if given badindex. RETURN[l.first.flavor]; }; ENDLOOP; IF default # unInit THEN GOTO Default; ERROR Failed[noSuchIcon, iconName]; EXITS Default => RETURN[default]; }; IsRegistered: PUBLIC ENTRY PROC[iconName: Rope.ROPE _ NIL, fileName: Rope.ROPE _ NIL, index: CARDINAL _ LAST[CARDINAL]] RETURNS[name: Rope.ROPE, file: Rope.ROPE, i: CARDINAL] = { <> IF iconList = NIL THEN Parse[! Failed => GOTO Out]; -- first time FOR l: IconList _ iconList, l.rest UNTIL l = NIL DO IF (IF iconName # NIL THEN Rope.Equal[l.first.iconName, iconName, FALSE] ELSE Rope.Equal[l.first.fileName, fileName, FALSE] AND l.first.index = index) THEN RETURN[l.first.iconName, l.first.fileName, l.first.index]; ENDLOOP; RETURN[NIL, NIL, 0]; EXITS Out => RETURN[NIL, NIL, 0]; }; Lookup: PROC[iconName: Rope.ROPE] RETURNS[IconEntry] = { <> FOR l: IconList _ iconList, l.rest UNTIL l = NIL DO IF Rope.Equal[l.first.iconName, iconName, FALSE] THEN RETURN[l.first]; ENDLOOP; RETURN[NIL]; }; InvalidateCache: PUBLIC PROC [iconName: Rope.ROPE _ NIL] = { <> FOR l: IconList _ iconList, l.rest UNTIL l = NIL DO IF iconName = NIL OR Rope.Equal[l.first.iconName, iconName, FALSE] THEN l.first.flavor _ unInit; ENDLOOP; }; <> iconList: IconList _ NIL; catalogueName: Rope.ROPE _ "RegisteredIcons.Catalogue"; Parse: INTERNAL PROCEDURE = { OPEN IO; stream, ris: IO.STREAM; { ENABLE { Failed => REJECT; IO.EndOfStream => GOTO Out; < GOTO Out;>> }; stream _ CreateFilterCommentsStream[FileIO.Open[fileName: catalogueName, accessOptions: read, createOptions: oldOnly ! FileIO.OpenFailed => ERROR Failed[noCatalogue, catalogueName]]]; DO tokenProc: IO.BreakProc = { RETURN[SELECT char FROM IO.SP, IO.TAB, ', => sepr, IO.CR => break, ENDCASE => other ]; }; whiteSpace: IO.BreakProc = { RETURN[IF char = CR THEN break ELSE IO.WhiteSpace[char]] }; isACR: IO.BreakProc = { RETURN[IF char = CR THEN break ELSE other] }; entry: IconEntry; iconName, fileName: Rope.ROPE; index: CARDINAL; position: INT; IO.SkipOver[stream, IO.WhiteSpace]; position _ stream.GetIndex[]; iconName _ IO.GetToken[stream]; <<-- see if key is followed by a :>> IO.SkipOver[stream, whiteSpace]; -- skips over spaces, tabs, but stops at CR IF stream.PeekChar[] # ': THEN { IO.SkipOver[stream, isACR]; Report1[msg: Rope.Cat["missing : at [", Convert.ValueToRope[[signed[position]]], "]"]]; LOOP; }; [] _ stream.GetChar[]; -- the : ris _ IO.RIS[IOExtras.GetLine[stream], ris]; -- read to CR, make this a stream. Reason is to bound the scope of the GetToken, GetCards below. fileName _ IO.GetToken[ris, tokenProc]; IF Rope.IsEmpty[fileName] THEN { Report1[msg: Rope.Cat["missing fileName at [", Convert.ValueToRope[[signed[position]]], "]"]]; LOOP; }; index _ IO.GetCard[ris]; entry _ Lookup[iconName]; IF entry # NIL THEN { entry.position _ position; -- comments may have changed. <> IF NOT (Rope.Equal[entry.fileName, fileName, FALSE] AND index = entry.index) THEN { entry.fileName _ fileName; entry.index _ index; entry.flavor _ unInit; }; } ELSE iconList _ CONS[NEW[IconRecord _ [iconName: iconName, fileName: fileName, index: index, position: position]], iconList]; ENDLOOP; EXITS Out => NULL; }; IF stream # NIL THEN stream.Close[]; IF errorLog # NIL THEN { MessageWindow.Append["problems encountered, see IconRegistry.log."]; MessageWindow.Blink[]; errorLog.Close[]; errorLog _ NIL; }; }; WriteEntry: INTERNAL PROC [entry: IconEntry] = { OPEN IO; viewer: ViewerClasses.Viewer; stream: STREAM = FileIO.Open[fileName: catalogueName, accessOptions: append, createOptions: oldOnly]; WriteOne[entry, stream]; stream.Close[]; IF (viewer _ ViewerOps.FindViewer[catalogueName]) # NIL THEN ViewerOps.RestoreViewer[viewer]; }; WriteOne: INTERNAL PROC [entry: IconEntry, stream: IO.STREAM] = { OPEN IO; entry.position _ stream.GetIndex[]; stream.PutF["\n%g: %g %d", rope[entry.iconName], rope[entry.fileName], int[entry.index]]; }; WriteCatalogue: PUBLIC ENTRY PROC [] = { OPEN IO; viewer: ViewerClasses.Viewer; stream: STREAM; lst: IconList; FOR l: IconList _ iconList, l.rest UNTIL l = NIL DO IF l.first.position = -1 THEN lst _ CONS[l.first, lst]; -- need to write them out in reverse order seen on lst, i.e. first one seen gets written out last ENDLOOP; IF lst = NIL THEN RETURN; FOR l: IconList _ lst, l.rest UNTIL l = NIL DO WriteOne[l.first, stream]; ENDLOOP; stream _ FileIO.Open[fileName: catalogueName, accessOptions: append, createOptions: oldOnly]; stream.Close[]; IF (viewer _ ViewerOps.FindViewer[catalogueName]) # NIL THEN ViewerOps.RestoreViewer[viewer]; }; <> Report: ENTRY PROCEDURE [entry: IconEntry _ NIL, msg: Rope.ROPE] = { Report1[entry, msg]; }; Report1: INTERNAL PROCEDURE [entry: IconEntry _ NIL, msg: Rope.ROPE] = { OPEN IO; ENABLE { UNWIND, AMEvents.Debugging, AMEvents.Debugged => NULL; ANY => CONTINUE; }; IF errorLog = NIL THEN { errorLog _ FileIO.Open[fileName: "IconRegistry.log", accessOptions: overwrite]; errorLog.PutF["Processing %g at %t", rope[catalogueName], time[]]; }; errorLog.PutF["\n\n%g", rope[msg]]; IF entry # NIL THEN errorLog.PutF[", at %g [%d]", rope[entry.iconName], int[entry.position]]; }; errorLog: IO.STREAM _ NIL; <> NoticeChanges: CedarSnapshot.RollbackProc = {IF iconList # NIL THEN ParseCatalogue[]}; ParseCatalogue: ENTRY PROC = {Parse[]}; WasCatalogueEdited: ViewerEvents.EventProc -- [viewer: ViewerClasses.Viewer, event: ViewerEvent] -- = { IF Rope.Equal[viewer.name, catalogueName, FALSE] THEN TRUSTED {Process.Detach[FORK ParseCatalogue[]]}; }; <> [] _ ViewerEvents.RegisterEventProc[proc: WasCatalogueEdited, event: save, before: FALSE]; TRUSTED {CedarSnapshot.Register[c: NIL, r: NoticeChanges]; }; END. <<>>