DIRECTORY FS USING [Error, StreamOpen], FileNames USING [ConvertToSlashFormat], Convert USING [RopeFromInt], IO, IOClasses USING [CreateCommentFilterStream], IconRegistry USING [Failure], Icons USING [NewIconFromFile, IconFlavor], Process USING [Detach], Rope USING [Concat, Cat, Equal, IsEmpty, ROPE], SimpleFeedback USING [Append], UserProfile USING [CallWhenProfileChanges, ProfileChangedProc, Token], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, RegisterEventProc], ViewerOps USING [FindViewer, RestoreViewer], VM USING [AddressFault]; IconRegistryImpl: CEDAR MONITOR IMPORTS FileNames, Convert, FS, IO, Icons, IOClasses, Process, Rope, SimpleFeedback, UserProfile, ViewerEvents, ViewerOps, VM 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; 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] THEN { IF l.first.index = index OR NOT saveInCatalogue THEN RETURN[]; -- need way to specify whether or not this definition is to redefine. currently overloading on saveInCatalogue. Idea is that program can register and if user has already registered, then user's takes precedence entry ¬ l.first; EXIT; }; LOOP; }; REPEAT FINISHED => TRUSTED { entry ¬ NEW[IconRecord ¬ []]; iconList ¬ CONS[entry, iconList]; }; ENDLOOP; entry­ ¬ [iconName: iconName, fileName: fileName, index: index, flavor: unInit, position: -1]; IF saveInCatalogue THEN WriteEntry[entry]; }; GetIcon: PUBLIC ENTRY PROC [iconName: Rope.ROPE, default: Icons.IconFlavor ¬ unInit] RETURNS [Icons.IconFlavor] = { ENABLE UNWIND => NULL; 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 ! FS.Error => IF default # unInit THEN GOTO Default ELSE ERROR Failed[fileNotFound, l.first.fileName]; VM.AddressFault => IF default # unInit THEN GOTO Default ELSE ERROR Failed[invalidIndex, Convert.RopeFromInt[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] = { ENABLE UNWIND => NULL; 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]; }; 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"; ParseCatalogue: ENTRY PROCEDURE = { ENABLE UNWIND => NULL; stream: IO.STREAM; stream ¬ IOClasses.CreateCommentFilterStream[FS.StreamOpen[fileName: catalogueName, accessOptions: read ! FS.Error => CONTINUE]]; -- change interface not to explain that it doesn't raise an error if no catalogue. IF stream # NIL THEN { ReadIcons[stream]; stream.Close[]; }; }; ReadIcons: PROCEDURE [stream: IO.STREAM] = { OPEN IO; ris: IO.STREAM ¬ NIL; { ENABLE { Failed => REJECT; IO.EndOfStream => GOTO Out; }; DO tokenProc: IO.BreakProc = { RETURN[SELECT char FROM IO.SP, IO.TAB, ', => sepr, IO.CR => break, ENDCASE => other ]; }; entry: IconEntry; iconName, fileName: Rope.ROPE; index: CARDINAL; position: INT; [] ¬ IO.SkipWhitespace[stream]; position ¬ stream.GetIndex[]; iconName ¬ IO.GetTokenRope[stream].token; IF stream.PeekChar[] # ': THEN { Report1[msg: Rope.Cat["missing : at [", Convert.RopeFromInt[position], "]"]]; [] ¬ IO.GetLineRope[stream]; LOOP; }; [] ¬ stream.GetChar[]; -- the : ris ¬ IO.RIS[IO.GetLineRope[stream], ris]; -- read to CR, make this a stream. Reason is to bound the scope of the GetTokenRope, GetCards below. fileName ¬ FileNames.ConvertToSlashFormat[IO.GetTokenRope[ris, tokenProc].token]; IF Rope.IsEmpty[fileName] THEN { Report1[msg: Rope.Cat["missing fileName at [", Convert.RopeFromInt[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; }; }; WriteEntry: INTERNAL PROC [entry: IconEntry] = { OPEN IO; viewer: ViewerClasses.Viewer; stream: STREAM = FS.StreamOpen[fileName: catalogueName, accessOptions: append]; 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; ENABLE UNWIND => NULL; viewer: ViewerClasses.Viewer; stream: STREAM; lst: IconList ¬ NIL; 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; stream ¬ FS.StreamOpen[fileName: catalogueName, accessOptions: append]; FOR l: IconList ¬ lst, l.rest UNTIL l = NIL DO WriteOne[l.first, stream]; ENDLOOP; stream.Close[]; IF (viewer ¬ ViewerOps.FindViewer[catalogueName]) # NIL THEN ViewerOps.RestoreViewer[viewer]; }; Report1: PROCEDURE [entry: IconEntry ¬ NIL, msg: Rope.ROPE] = { IF entry # NIL THEN msg ¬ msg.Concat[IO.PutFR[", at %g [%d]", [rope[entry.iconName]], [integer[entry.position]]]]; SimpleFeedback.Append[$IconRegistry, begin, $Error, IO.PutFR1["IconRegistry error catalogue/profile at %t", IO.time[]]]; SimpleFeedback.Append[$IconRegistry, end, $Error, msg]; }; WasCatalogueEdited: ViewerEvents.EventProc = { IF Rope.Equal[viewer.name, catalogueName, FALSE] THEN TRUSTED {Process.Detach[FORK ParseCatalogue[]]}; }; IconsFromProfile: UserProfile.ProfileChangedProc = { r: Rope.ROPE ¬ UserProfile.Token["RegisteredIcons"]; IF r # NIL THEN ReadIcons[IO.RIS[r]]; }; [] ¬ ViewerEvents.RegisterEventProc[proc: WasCatalogueEdited, event: save, before: FALSE]; ParseCatalogue[]; UserProfile.CallWhenProfileChanges[IconsFromProfile]; END.  IconRegistryImpl.mesa; Copyright Σ 1985, 1991 by Xerox Corporation. All rights reserved. Edited by Teitelman on May 10, 1983 12:41 pm Converted by: Maxwell, January 6, 1984 9:57 am Rick Beach, April 4, 1985 8:56:14 am PST Pier, March 24, 1987 9:40:58 pm PST Last edited by: Mik Lamming - January 19, 1988 10:26:51 am PST Last tweaked by Mike Spreitzer on October 6, 1989 12:32:09 pm PDT Michael Plass, September 14, 1990 11:26 am PDT Willie-s, November 14, 1991 1:52 pm PST Types Accessing Icon Registry 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. IF iconList = NIL AND saveInCatalogue THEN Initialize[]; -- first time. The saveInCatalogue check is so that if a program registers an icon, and the user has redefined it in his catalogue or profile, then his will take precedence. 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. IF iconList = NIL THEN Parse[! Failed => IF default # unInit THEN GOTO Default]; -- first time IF code # noSuchFile THEN REJECT ELSE 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 use by iconeditor, so that it can force creation of a new flavor when an icon that has been registered was edited. 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. Parsing Icon Catalogue whiteSpace: IO.BreakProc = { RETURN[IF char = CR THEN break ELSE IO.WhiteSpace[char]] }; isACR: IO.BreakProc = { RETURN[IF char = CR THEN break ELSE other] }; -- see if key is followed by a : IO.SkipOver[stream, whiteSpace]; -- skips over spaces, tabs, but stops at CR IF ~FileNames.InASubdirectory["///", fileName] THEN fileName ¬ Rope.Concat[SystemNames.LocalDir["Commands"], FileNames.GetShortName[path: fileName] ]; -- forces current Commands subDir entry.endPosition _ endPosition; Reporting errors Noticing changes PROC [viewer: ViewerClasses.Viewer, event: ViewerEvent] Initialization Change Log: Edited on May 10, 1983 12:41 pm, by Teitelman added facility to get icons from userprofile, and to enable icons registered by programs not to take precedence over those in the user's profile/catalogue, even if registered later, e.g. by a program that is loaded later. does not raise error if no regsiteredicoons.catalogue. deimplemented multiple registration idea. To get same effect, call GetIcon[name, GetIcon[otherName]]; changes to: RegisterIcon (do not parse on first call if saveInCatalogue is FALSE so that if program registers an icon, and there is also one specified by the user, his will take precedence)., GetIcon, IsRegistered, ParseCatalogue, ReadIcons, WriteEntry, Report1, NoticeChanges, WasCatalogueEdited, IconsFromProfile, UserProfile, TRUSTED, DIRECTORY, IMPORTS, UserProfile, RegisterIcon, RegisterIcon Edited on January 6, 1984 9:38 am, by Maxwell changes to: DIRECTORY, IMPORTS, GetIcon, ParseCatalogue, ReadIcons, WriteEntry, WriteCatalogue, Report1, NoticeChanges, TRUSTED Last edited by: Mik Lamming - January 19, 1988 10:21:28 am PST changes to: ReadIcons Bug fix: when reading user profile entry "RegisteredIcons: " only the short name was used to find the icon file, the directory being forced to be ///Commands in all cases. Fixed it so that only short names are forced to the commands directory while long names are left intact Michael Plass, September 14, 1990 11:11:31 am PDT Replaced MessageWindow and log file with calls on SimpleFeedback. Κ Φ–(cedarcode) style•NewlineDelimiter ™codešΟc™Kšœ Οeœ6™BKšœ™,K™.K™(K™#K™>K™AK™.K™'K™—šΟk ˜ KšŸœŸœ˜Kšœ Ÿœ˜'KšœŸœ˜KšŸœ˜Kšœ Ÿœ˜,Kšœ Ÿœ ˜KšœŸœ˜*KšœŸœ ˜KšœŸœŸœ˜/KšœŸœ ˜Kšœ Ÿœ5˜FKšœŸœ ˜Kšœ Ÿœ ˜2Kšœ Ÿœ˜,KšŸœŸœ˜—K˜KšΠblœŸœŸ˜KšŸœŸœŸœYŸœ˜KšŸœ˜KšœŸœ˜headšœ™Kšœ ŸœŸœŸœ ˜#Kšœ ŸœŸœ ˜!šœ ŸœŸœ˜KšœŸœŸœ˜KšœŸœŸœ˜KšœŸœŸœŸœ˜!Kšœ"˜"Kšœ Ÿœ˜#Kšœ˜—Kš œŸœŸœ*ŸœŸœ˜K—™šΟn œŸœŸœŸœŸœŸœ ŸœŸœŸœ˜€K™ΰKšŸ˜K˜KšŸœ ŸœŸœ­™ηšŸœ ŸœŸœŸ˜3šŸœ(ŸœŸœ˜7šŸœ(ŸœŸ˜7Kš ŸœŸœŸœŸœŸœΠbcϝ˜“K˜KšŸœ˜K˜—KšŸ˜KšŸ˜—KšŸ˜šŸœŸœ˜KšœŸœ˜Kšœ Ÿœ˜!K˜—KšŸ˜—Kšœ_˜`KšŸœŸœ˜*K˜—K˜š‘œŸœŸœ$Ÿœ˜sKš}™}K™ KšŸ˜Kš Ÿœ ŸœŸœŸœŸœŸœ  ™_šŸœ ŸœŸœŸ˜3šŸœ(ŸœŸœ˜7šŸœŸœI˜hšŸœ ˜ KšŸœŸœŸ œ™&KšŸœŸœŸœ˜%KšŸœŸœ(˜2—šŸœ˜KšŸœŸœŸœ˜%KšŸœŸœ:˜D—Kšœ1˜4—KšŸœ˜K˜—KšŸ˜—KšŸœŸœŸœ ˜&KšŸœ˜#šŸ˜Kšœ Ÿœ ˜—K˜K˜—š‘ œŸœŸœŸœŸœŸœŸœŸœ ŸœŸœŸœŸœ Ÿœ ŸœŸœ˜²K™ΩKš Ÿœ ŸœŸœŸœ ™BKšŸœŸœŸœ˜šŸœ ŸœŸœŸ˜3šŸ˜Kš œŸœ ŸœŸœ(Ÿœ˜EKš Ÿœ(ŸœŸœŸœŸœ4˜—KšŸ˜—KšŸœŸ œ˜KšŸ˜K˜—š‘œŸœŸœŸœ˜8K™všŸœ ŸœŸœŸ˜3KšŸœ(ŸœŸœŸœ ˜FKšŸ˜—KšŸœŸœ˜ Kšœ˜K˜—š‘œŸ œŸœŸœ˜šœ £ ™Kšœ”™”K™——™1KšœA™A——…—Š9n