IconsImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Plass, April 20, 1983 10:00 am
Doug Wyatt, April 15, 1985 5:45:51 pm PST
Russ Atkinson (RRA) May 6, 1986 11:12:57 pm PDT
DIRECTORY
Basics USING [bytesPerWord],
Cursors USING [SetCursor],
FS USING [Error, StreamOpen],
Icons USING [DrawIconProc, IconFileFormat, IconFlavor, iconH, IconRef, IconRep, iconW],
Imager USING [ClipRectangleI, Font, SetColor, SetFont, SetXYI, ShowRope, white],
ImagerBackdoor USING [DrawBits],
InputFocus USING [GetInputFocus, SetInputFocus],
IO USING [EndOfStream, SetIndex, STREAM, UnsafeGetBlock],
Menus USING [FindEntry, MenuEntry],
MessageWindow USING [Append, Blink, Confirm],
Process USING [Detach],
Rope USING [Concat, Fetch, Length, ROPE, Run, Substr],
TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords, TIPTable],
UserProfile USING [CallWhenProfileChanges, ProfileChangedProc, Token],
VFonts USING [CharWidth, EstablishFont, FontAscent, StringWidth],
ViewerClasses USING [ModifyProc, NotifyProc, Viewer],
ViewerOps USING [DestroyViewer, OpenIcon, PaintViewer, ChangeColumn, SaveViewer],
ViewerPrivate USING [],
ViewerTools USING [SelPosRec, SetSelection],
WindowManager USING [colorDisplayOn];
IconsImpl: CEDAR MONITOR
IMPORTS Cursors, FS, Imager, ImagerBackdoor, InputFocus, IO, Menus, MessageWindow, Process, Rope, TIPUser, UserProfile, VFonts, ViewerOps, ViewerTools, WindowManager
EXPORTS Icons, ViewerPrivate
SHARES Menus
= BEGIN OPEN Icons;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Viewer: TYPE = ViewerClasses.Viewer;
Global Icon variables
bigFont: Imager.Font ← NIL;
bigFontHeight: INTEGER ← 0;
smallFont: Imager.Font ← NIL;
smallFontHeight: INTEGER ← 0;
suppressIconPrefix: ROPENIL;
maxIcons: CARDINAL = 64;
IconArray: TYPE = ARRAY [0..maxIcons) OF IconRef;
icons: REF IconArray ← NEW[IconArray ← ALL[NIL]];
nextFlavor: IconFlavor ← document;
Icon procedures
DrawIcon: PUBLIC ENTRY DrawIconProc = {
leading: INTEGER = 3;
iconInfo: IconRef ~ icons[ORD[flavor]];
prefix: ROPENIL;
IF iconInfo.proc=NIL THEN {
ImagerBackdoor.DrawBits[context: context, base: LOOPHOLE[iconInfo], wordsPerLine: iconW/16,
sMin: 0, fMin: 0, sSize: iconH, fSize: iconW, tx: x, ty: y+iconH];
IF iconInfo.label THEN {
iconFont: Imager.Font;
fontHeight: INTEGER;
ix: INTEGER ← x+iconInfo.lx;
iy: INTEGER ← y+iconInfo.ly+iconInfo.lh;
indent: INTEGER ← 0;
segments: LIST OF ROPENIL;
IF VFonts.StringWidth[label, bigFont]<=iconInfo.lw THEN {
iconFont ← bigFont;
fontHeight ← bigFontHeight;
segments ← LIST[label];
}
ELSE {
iconFont ← smallFont;
fontHeight ← smallFontHeight;
indent ← VFonts.CharWidth[' , iconFont];
segments ← Decompose[label, iconInfo.lw, indent, iconFont];
};
Imager.ClipRectangleI[context, ix, iy, iconInfo.lw, -iconInfo.lh];
Imager.SetFont[context, iconFont];
IF iconInfo.invertLabel THEN Imager.SetColor[context, Imager.white];
FOR list: LIST OF ROPE ← segments, list.rest UNTIL list=NIL DO
rope: ROPE ~ list.first;
iy ← iy-fontHeight;
Imager.SetXYI[context, ix, iy];
iy ← iy-leading;
Imager.ShowRope[context, rope];
IF list=segments THEN ix ← ix+indent;
ENDLOOP;
};
}
ELSE iconInfo.proc[flavor, context, x, y, label];
};
GetFonts: ENTRY PROC = {
bigFont ← VFonts.EstablishFont["Helvetica", 8];
bigFontHeight ← VFonts.FontAscent[bigFont];
smallFont ← VFonts.EstablishFont["Helvetica", 7];
smallFontHeight ← VFonts.FontAscent[smallFont];
};
ChangePrefix: UserProfile.ProfileChangedProc ~ {
suppressIconPrefix ← UserProfile.Token[key: "Viewers.SuppressIconPrefix", default: NIL];
};
Decompose: PROC [name: ROPE, width, indent: INTEGER, font: Imager.Font]
RETURNS [segments: LIST OF ROPENIL] = {
start: INT ← 0;
stop: INT ← name.Length[];
extension: ROPENIL;
Build: PROC[start: INT] RETURNS[LIST OF ROPE] ~ {
maxw: INTEGER ← (IF start=0 THEN width ELSE width-indent);
sw: INTEGER ← 0; -- width of string so far
next: INT ← -1;
IF start>=stop THEN RETURN[IF extension=NIL THEN NIL ELSE LIST[extension]];
FOR i: INT IN[start..stop) DO
c: CHAR ~ name.Fetch[i];
sw ← sw+VFonts.CharWidth[c, font];
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;
RETURN[CONS[name.Substr[start: start, len: next-start], Build[next]]];
};
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 { extension ← name.Substr[start: i]; stop ← i; EXIT };
ENDLOOP;
RETURN[Build[start]];
};
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[ORD[newFlavor]] ← info;
};
IconRefFromStream: PROC[stream: STREAM, n: CARDINAL] RETURNS [IconRef] ~ {
format: IconFileFormat; -- 512 words!
bytesPerIcon: INT ~ SIZE[IconFileFormat]*Basics.bytesPerWord;
IO.SetIndex[stream, bytesPerIcon*n];
TRUSTED {
base: LONG POINTER TO IconFileFormat ~ @format;
bytesRead: INT ~ IO.UnsafeGetBlock[stream, [base: LOOPHOLE[base], count: bytesPerIcon]];
IF bytesRead#bytesPerIcon THEN ERROR IO.EndOfStream[stream];
};
RETURN[NEW[IconRep ← [bits: format.bits,
label: format.label, invertLabel: format.invertLabel,
lx: format.lx, ly: format.ly, lw: format.lw, lh: format.lh]]];
};
IconsFromFile: ENTRY PROC [file: ROPE] RETURNS [first: IconFlavor, nRead: INTEGER] ~ {
ENABLE UNWIND => NULL;
stream: STREAM ~ FS.StreamOpen[file];
first ← nextFlavor;
FOR nRead ← 0, nRead+1 DO
info: IconRef ~ IconRefFromStream[stream, nRead ! IO.EndOfStream => EXIT];
icons[ORD[nextFlavor]] ← info;
nextFlavor ← nextFlavor.SUCC;
ENDLOOP;
};
NewIconFromFile: PUBLIC PROC [file: ROPE, n: CARDINAL]
RETURNS
[newFlavor: IconFlavor] ~ {
stream: STREAM ~ FS.StreamOpen[file];
info: IconRef ~ IconRefFromStream[stream, n ! IO.EndOfStream => GOTO Fail];
RETURN[NewIcon[info]];
EXITS Fail => RETURN[unInit];
};
Icon Manager (formerly IconManagerImpl)
IconNotify: PUBLIC ViewerClasses.NotifyProc = {
mx, my: INTEGER;
closeOthers, switchDisplays: BOOLFALSE;
FOR l: LIST OF REF ANY ← input, l.rest UNTIL l = NIL DO WITH l.first SELECT FROM
z: TIPUser.TIPScreenCoords => [mouseX: mx, mouseY: my] ← z^;
z: ATOM => SELECT z FROM
$OpenDesktop, $ResetDesktop => {
IF self.class.flavor = $DeskTop THEN self.class.notify[self, input]; RETURN};
$SetInputFocus => SELECT self.class.flavor FROM
$Text => ViewerTools.SetSelection[self, NEW[ViewerTools.SelPosRec←[]]];
$Typescript, $Chat => ViewerTools.SetSelection[self];
ENDCASE;
$CloseOthers => closeOthers ← TRUE;
$Color => IF WindowManager.colorDisplayOn THEN
ViewerOps.ChangeColumn[self, color];
$Delete => DestroyIcon[self];
$Save => {
entry: Menus.MenuEntry = Menus.FindEntry[self.menu, "Save"];
MessageWindow.Append["Saving icon . . . ", TRUE];
IF entry = NIL THEN ViewerOps.SaveViewer[self] ELSE entry.proc[self];
MessageWindow.Append["done."]};
$Left => ViewerOps.ChangeColumn[self, left];
$MouseMove => Cursors.SetCursor[textPointer];
$Open => ViewerOps.OpenIcon[self, closeOthers];
$Right => ViewerOps.ChangeColumn[self, right];
$Select => SelectIcon[self];
$TogglePos => ViewerOps.ChangeColumn[self, SELECT self.column FROM
left => right,
right => left,
color => left,
ENDCASE => ERROR];
ENDCASE => NULL;
ENDCASE => NULL;
ENDLOOP;
};
IconModify: PUBLIC ViewerClasses.ModifyProc = {
SELECT change FROM
set => ViewerOps.PaintViewer[self, all];
kill => {selectedIcon ← NIL; ViewerOps.PaintViewer[self, all]};
ENDCASE;
};
selectedIcon: PUBLIC ViewerClasses.Viewer ← NIL;
DestroyIcon: PROC [icon: Viewer] = {
IF icon.inhibitDestroy THEN {
MessageWindow.Append["That icon cannot be destroyed.", TRUE];
MessageWindow.Blink[];
RETURN};
IF icon.link = NIL AND (icon.newVersion OR icon.newFile)
THEN TRUSTED {Process.Detach[FORK ConfirmDestroyIcon[icon]]} -- fork for confirmation
ELSE ViewerOps.DestroyViewer[icon];
};
ConfirmDestroyIcon: PROC [icon: Viewer] = {
IF MessageWindow.Confirm[Rope.Concat["Confirm delete (and loss of edits) for icon: ", icon.name]]
THEN ViewerOps.DestroyViewer[icon];
};
SelectIcon: PROC [viewer: Viewer] = {
IF InputFocus.GetInputFocus[].owner#viewer THEN {
InputFocus.SetInputFocus[NIL]; -- kill old so we don't smash selectedIcon
selectedIcon ← viewer;
InputFocus.SetInputFocus[viewer];
};
};
Initialization
iconTIP: PUBLIC TIPUser.TIPTable ← TIPUser.InstantiateNewTIPTable["Icons.tip"];
{-- 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.