IconsImpl.mesa
Copyright © 1982, 1983, 1984 Xerox Corporation. All rights reserved.
McGregor on October 20, 1982 4:28 pm
Maxwell, January 3, 1983 12:53 pm
Plass, April 20, 1983 10:00 am
Paul Rovner, August 10, 1983 3:59 pm
Russ Atkinson, November 16, 1983 7:19 pm
(changed opening of Standard.icons to only use local name, also changed the global variable icons to be a REF to an array, instead of taking 128 words in the global frame!)
Doug Wyatt, January 12, 1984 4:17 pm
(rewrote Decompose)
Doug Wyatt, July 30, 1984 11:23:15 am PDT
(added Viewers.SuppressIconPrefix feature)
Doug Wyatt, September 4, 1984 3:22:14 pm PDT
(converted to Imager)
DIRECTORY
Font,
FS USING [Close, Error, Open, GetInfo, OpenFile, Read],
Imager,
ImagerOps,
Icons,
Rope USING [Fetch, Length, ROPE, Run],
UserProfile USING [CallWhenProfileChanges, ProfileChangedProc, Token],
VFonts USING [EstablishFont, Font, FontAscent],
ViewerExtras USING [ImagerFont],
VM USING [Interval, Allocate, AddressForPageNumber, Free, wordsPerPage];
IconsImpl: CEDAR MONITOR
IMPORTS Font, FS, Imager, ImagerOps, Rope, UserProfile, VFonts, VM
EXPORTS Icons
= BEGIN OPEN Icons;
ROPE: TYPE = Rope.ROPE;
DrawIcon: PUBLIC ENTRY DrawIconProc = {
PROC[flavor: IconFlavor, context: Graphics.Context, x, y: INTEGER ← 0, label: ROPENIL]
imager: Imager.Context ~ ImagerOps.ImagerFromGraphics[context];
leading: INTEGER = 3;
iconInfo: IconRef ← icons[LOOPHOLE[flavor]];
prefix: ROPENIL;
IF iconInfo.proc=NIL THEN {
iconBitmap.base ← iconInfo; -- bits guaranteed first in record!!!
ImagerOps.DrawBitmap[context, iconBitmap, x, y];
IF iconInfo.label THEN {
iconFont: Font.FONT;
fontHeight: INTEGER;
pos: Imager.VEC ← [x+iconInfo.lx, y+iconInfo.ly+iconInfo.lh];
indent: REAL ← 0;
map: MapType ~ {
pos.y ← pos.y-fontHeight;
Imager.SetXY[imager, pos];
pos.y ← pos.y-leading;
Imager.ShowRope[imager, rope, start, len];
pos.x ← pos.x+indent; indent ← 0;
};
IF Font.RopeWidth[bigIconFont, label].x <= iconInfo.lw THEN {
iconFont ← bigIconFont;
fontHeight ← bigFontHeight;
}
ELSE {
iconFont ← smallIconFont;
fontHeight ← smallFontHeight;
indent ← Font.CharWidth[iconFont, ' ].x;
};
Imager.ClipRectangle[imager, pos.x, pos.y, iconInfo.lw, -iconInfo.lh];
Imager.SetFont[imager, iconFont];
IF iconInfo.invertLabel THEN Imager.SetColor[imager, Imager.white];
IF iconFont=bigIconFont THEN map[label, 0, INT.LAST]
ELSE Decompose[label, iconInfo.lw, indent, iconFont, map];
};
}
ELSE iconInfo.proc[flavor, context, x, y, label];
};
GetFonts: ENTRY PROC = {
bigFont: VFonts.Font ← VFonts.EstablishFont["Helvetica", 8];
smallFont: VFonts.Font ← VFonts.EstablishFont["Helvetica", 7];
bigFontHeight ← VFonts.FontAscent[bigFont];
smallFontHeight ← VFonts.FontAscent[smallFont];
bigIconFont ← ViewerExtras.ImagerFont[bigFont];
smallIconFont ← ViewerExtras.ImagerFont[smallFont];
};
bigIconFont: Font.FONT;
bigFontHeight: INTEGER;
smallIconFont: Font.FONT;
smallFontHeight: INTEGER;
suppressIconPrefix: ROPENIL;
ChangePrefix: UserProfile.ProfileChangedProc ~ {
suppressIconPrefix ← UserProfile.Token[key: "Viewers.SuppressIconPrefix", default: NIL];
};
MapType: TYPE ~ PROC[rope: ROPE, start, len: INT];
Decompose: PROC[name: ROPE, width, indent: REAL, font: Font.FONT, map: MapType] = {
start: INT ← 0;
stop: INT ← name.Length[];
extensionStart, extensionLen: INT ← 0;
Build: PROC[start: INT] ~ {
maxw: REAL ← (IF start=0 THEN width ELSE width-indent);
sw: REAL ← 0; -- width of string so far
next: INT ← -1;
FOR i: INT IN[start..stop) DO
c: CHAR ~ name.Fetch[i];
sw ← sw+Font.CharWidth[font, c].x;
IF sw>maxw THEN { IF next<0 THEN next ← MAX[i, start+1]; EXIT }; -- stop here
SELECT c FROM
'], '>, '/, ' => next ← i+1; -- could end a line
IN['A..'Z], IN['0..'9] => IF i>start THEN next ← i; -- next line could start here
ENDCASE;
REPEAT FINISHED => next ← stop;
ENDLOOP;
IF next>start THEN map[name, start, next-start];
IF next<stop THEN Build[next]
ELSE IF extensionLen>0 THEN map[name, extensionStart, extensionLen];
};
IF suppressIconPrefix#NIL THEN {
prefixLen: INT ~ suppressIconPrefix.Length[];
IF Rope.Run[s1: name, s2: suppressIconPrefix, case: FALSE]=prefixLen THEN start ← prefixLen;
};
FOR i: INT DECREASING IN[start..stop) DO
IF name.Fetch[i]='. THEN { extensionStart ← i; extensionLen ← stop-i; stop ← i; EXIT };
ENDLOOP;
Build[start];
};
wSlop: INTEGER = 1;
OldDecompose: PROC [name: ROPE, width: REAL, font: Graphics.FontRef]
RETURNS [segments: LIST OF ROPENIL] =
{
end: INT ← 0;
len: INT ← Rope.Length[name];
haveExtension, inWord: BOOLFALSE;
SkipToNextWord: PROC [c: CHAR] RETURNS [BOOL] = CHECKED {
IF c IN ['A..'Z] OR c = '> OR c IN ['0..'9] THEN { IF inWord THEN RETURN[TRUE] }
ELSE inWord ← TRUE;
IF c='. THEN RETURN[haveExtension ← TRUE];
end ← end+1; RETURN[FALSE]; };
Build: PROC[start: INT] RETURNS [LIST OF ROPE] = {
IF start>=len THEN RETURN[NIL];
end ← start+1;
inWord ← FALSE;
IF haveExtension THEN end ← len
ELSE [] ← Rope.Map[base: name, start: end, action: SkipToNextWord];
RETURN[CONS[name.Substr[start, end-start], Build[end]]]; };
Coalesce: PROC[segs: LIST OF ROPE] RETURNS [LIST OF ROPE] = {
c: ROPE;
IF segs=NIL OR segs.rest=NIL THEN RETURN[segs];
IF segs.rest.first.Fetch[0]#'. AND
Graphics.RopeWidth[font, c ← segs.first.Concat[segs.rest.first]].xw<=width
THEN RETURN[Coalesce[CONS[c, segs.rest.rest]]];
RETURN[CONS[segs.first, Coalesce[segs.rest]]]; };
width ← width+wSlop; -- allow small amount of clipping.
RETURN[Coalesce[Build[0]]];
};
iconBitmap: REF;
NEW[GraphicsOps.BitmapRep ← [NIL, iconW/16, iconW, iconH]];
maxIcons: CARDINAL = 64;
IconArray: TYPE = ARRAY [0..maxIcons) OF IconRef;
icons: REF IconArray ← NEW[IconArray ← ALL[NIL]];
nextFlavor: IconFlavor ← document;
NewFlavor: ENTRY PROC RETURNS [newFlavor: IconFlavor] = {
newFlavor ← nextFlavor;
nextFlavor ← SUCC[newFlavor];
IF LOOPHOLE[nextFlavor, CARDINAL] >= maxIcons THEN ERROR;
};
NewIcon: PUBLIC PROC [info: IconRef] RETURNS [newFlavor: IconFlavor] = {
newFlavor ← NewFlavor[];
icons[LOOPHOLE[newFlavor]] ← info;
};
IconsFromFile: PROC [file: ROPE] RETURNS [first: IconFlavor, nRead: INTEGER] =
TRUSTED {
fh: FS.OpenFile ← FS.Open[file];
pages: INT ← FS.GetInfo[fh].pages;
space: VM.Interval ← VM.Allocate[count: pages];
iconBase: LONG POINTER TO IconFileFormat ← VM.AddressForPageNumber[space.page];
FS.Read[file: fh, from: 0, nPages: pages, to: iconBase];
first ← nextFlavor;
nRead ← pages/(SIZE[IconFileFormat]/VM.wordsPerPage);
FOR n: INTEGER IN [0..nRead) DO
[] ← NewIcon[NEW[IconRep ← [iconBase.bits, iconBase.label, iconBase.invertLabel,
0--filler--, iconBase.lx, iconBase.ly, iconBase.lw, iconBase.lh]]];
iconBase ← iconBase+SIZE[IconFileFormat];
ENDLOOP;
VM.Free[space];
FS.Close[fh];
};
NewIconFromFile: PUBLIC PROC [file: ROPE, n: CARDINAL]
RETURNS [newFlavor: IconFlavor] =
TRUSTED {
fh: FS.OpenFile ← FS.Open[file];
pages: INT ← FS.GetInfo[fh].pages;
space: VM.Interval ← VM.Allocate[count: pages];
iconBase: LONG POINTER TO IconFileFormat ← VM.AddressForPageNumber[space.page];
FS.Read[file: fh, from: 0, nPages: pages, to: iconBase];
iconBase ← iconBase + (n*SIZE[IconFileFormat]);
newFlavor ← NewIcon[NEW[IconRep ← [iconBase.bits, iconBase.label, iconBase.invertLabel,
0--filler--, iconBase.lx, iconBase.ly, iconBase.lw, iconBase.lh]]];
VM.Free[space];
FS.Close[fh];
};
{-- Font and Icon initialization takes place here.
ok: BOOLTRUE;
GetFonts[];
[] ← IconsFromFile["Standard.icons"
! FS.Error => IF error.group # bug THEN {ok ← FALSE; CONTINUE}];
IF NOT ok THEN [] ← IconsFromFile["[]<>Standard.icons"];
We do not handle FS.Error here because BootEssentials.df is supposed to have attached the local name to the correct place.
};
UserProfile.CallWhenProfileChanges[ChangePrefix];
END.