IconRegistryImpl.mesa;
Edited by Teitelman on April 16, 1983 2:13 pm
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
Types
IconList: TYPE = LIST OF IconEntry;
IconEntry: TYPE = REF IconRecord;
IconRecord: TYPE = RECORD[
iconName: Rope.ROPENIL,
fileName: Rope.ROPENIL,
index: CARDINALLAST[CARDINAL],
flavor: Icons.IconFlavor ← unInit,
position: INT ← -1 -- in catalogue
];
Failed: PUBLIC ERROR [why: IconRegistry.Failure, reason: Rope.ROPE] = CODE;
Accessing Icon Catalogue
RegisterIcon: PUBLIC ENTRY PROC [iconName: Rope.ROPE, fileName: Rope.ROPE, index: CARDINAL, saveInCatalogue: BOOLFALSE] = {
Changes RegisteredIcons.Catalogue to reflect the indicated association. This definition will replace any previous definition. comments are for inclusion in the catalogue to help the user figure out what the icon looks like.
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] = {
Obtain an icon flavor for the icon associated with iconName. Create a new icon flavor if one has not previously been created.
If for some reason it could not obtain the icon, and default # unInit, returns the default. Otherwise, raises Error (defined below) with appropriate parameters.
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.ROPENIL, fileName: Rope.ROPENIL, index: CARDINALLAST[CARDINAL]] RETURNS[name: Rope.ROPE, file: Rope.ROPE, i: CARDINAL] = {
for use by iconeditor, so that it can force creation of a new flavor when an icon that has been registered was edited, obtain name of icon when selected, etc. Can be called with either iconName, or fileName and index.
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 use by iconeditor, so that it can force creation of a new flavor when an icon that has been registered was edited.
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] = {
If an iconFlavor was previous created for this icon, discard the flavor, i.e. the next call to GetIcon will create a new flavor. If iconName = NIL, do this for all registered icons. For use by iconeditor.
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;
};
Parsing Icon Catalogue
iconList: IconList ← NIL;
catalogueName: Rope.ROPE ← "RegisteredIcons.Catalogue";
Parse: INTERNAL PROCEDURE = {
OPEN IO;
stream, ris: IO.STREAM;
{
ENABLE {
Failed => REJECT;
IO.EndOfStream => GOTO Out;
ANY => 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.
entry.endPosition ← endPosition;
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];
};
Reporting errors
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.STREAMNIL;
When the Icon Catalogue changes
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[]]};
};
Initialization
[] ← ViewerEvents.RegisterEventProc[proc: WasCatalogueEdited, event: save, before: FALSE];
TRUSTED
{CedarSnapshot.Register[c: NIL, r: NoticeChanges];
};
END.