IconEditorImplB.mesa
Written by Werner Winiger, 24-Aug-81 13:14:53
Converted to the Viewers world by Rick Beach, April 30, 1982 4:32 pm
Last edit by Mike Spreitzer, November 13, 1982 3:33 pm
Last Edited by: Teitelman, April 23, 1983 1:51 pm
Last Edited by: Doug Wyatt, April 22, 1983 4:09 pm
DIRECTORY
Buttons USING [Button, Create],
ChoiceButtons USING [BuildTextPrompt, PromptDataRef],
Containers USING [Container, Create, ChildXBound, ChildYBound],
Environment USING [bytesPerWord],
FileIO USING [Open],
Graphics,
GraphicsOps USING [DrawBitmap, BitmapRep],
Icons,
IconEditorDefs,
IconEditorIO,
IO,
IconRegistry USING [IsRegistered],
Menus USING [ClickProc, Menu, MenuProc],
MessageWindow USING [Append, Blink, Confirm],
Real USING [FixC],
Rope USING [Cat, ROPE, Size],
Rules USING [Create],
Runtime USING [IsBound],
TIPUser USING [TIPScreenCoords, InstantiateNewTIPTable],
ViewerClasses USING [DestroyProc, NotifyProc, PaintProc, Viewer, ViewerClass,
ViewerClassRec],
ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass, SetNewVersion],
ViewerTools USING [GetContents, SetContents];
IconEditorImplB: CEDAR PROGRAM
IMPORTS Buttons, ChoiceButtons, Containers, Graphics, GraphicsOps, Icons,
IconEditorDefs, IconEditorIO, IconRegistry, IO, MessageWindow, Real, Rope, Rules, Runtime, TIPUser,
ViewerOps, ViewerTools, FileIO
EXPORTS IconEditorDefs = {
OPEN IconEditorDefs;
************************** Declarations used for graphics *****************************
foreground: Graphics.Color ← Graphics.black;
background: Graphics.Color ← Graphics.white;
deskTopGray: CARDINAL = 122645B;
iconEditorIcon: Icons.IconFlavor = Icons.NewIconFromFile["IconEditor.icons", 0];
StartIconEditor: PROC RETURNS [tool: IconHandle] = {
ENABLE UNWIND => NULL;
iconFileInfo, iconNumberInfo: ChoiceButtons.PromptDataRef;
iconFetchButton: Buttons.Button;
iconViewerClass: ViewerClasses.ViewerClass ← NEW[ViewerClasses.ViewerClassRec ← [
paint: IconPainter,
notify: NotifyEvents,
destroy: AboutToDestroy,
coordSys: bottom,
tipTable: TIPUser.InstantiateNewTIPTable["IconEditor.TIP"]
]];
tool ← NEW [IconHandleRec];
tool.iconFileName ← "Sample.icons";
tool.container ← Containers.Create[info: [name: "Icon Editor",
scrollable: FALSE, iconic: TRUE, column: left, icon: iconEditorIcon]];
tool.container.menu ← CreateMenu[tool];
iconFileInfo ← ChoiceButtons.BuildTextPrompt[viewer: tool.container, x: 0, y: 0, title: "File: ",
textViewerWidth: 250, clientdata: tool];
tool.iconFileWindow ← iconFileInfo.textViewer;
ViewerTools.SetContents[tool.iconFileWindow, tool.iconFileName];
iconFileInfo ← ChoiceButtons.BuildTextPrompt[viewer: tool.container, x: iconFileInfo.newx, y: 0, title: " Icon Name: "];
tool.iconNameWindow ← iconFileInfo.textViewer; -- w.t.
iconFetchButton ← Buttons.Create[info: [name: "FetchIcon: ", parent: tool.container, wx: 0,
wy: 20, border: TRUE], proc: IconFetchProc, clientData: tool, fork: TRUE];
iconNumberInfo ← ChoiceButtons.BuildTextPrompt[viewer: tool.container, x: iconFetchButton.ww, y: 20, title: "Number[0-...]: ", textViewerWidth: 30];
tool.iconNumberWindow ← iconNumberInfo.textViewer;
iconFileInfo ← ChoiceButtons.BuildTextPrompt[viewer: tool.container, x: iconNumberInfo.newx, y: 20, title: " from Icon file: "];
tool.iconFetchFileWindow ← iconFileInfo.textViewer;
ViewerTools.SetContents[tool.iconFetchFileWindow, tool.iconFileName];
Containers.ChildXBound[tool.container, Rules.Create[info: [parent: tool.container, wx: 0, wy: 40, ww: 1, wh: 1]]];
ViewerOps.RegisterViewerClass[$IconViewer, iconViewerClass];
tool.viewer ← ViewerOps.CreateViewer[flavor: $IconViewer, info: [parent: tool.container,
scrollable: FALSE, border: FALSE, wx: 0, wy: 40+distX, data: tool]];
Containers.ChildXBound[tool.container, tool.viewer];
Containers.ChildYBound[tool.container, tool.viewer];
[tool.numberReadIn, tool.iconFile] ← IconEditorDefs.LoadIcons[tool, tool.iconFileName !
IconEditorDefs.CouldntLoadIcons => GOTO BadFile];
tool.numberOfIcons ← tool.numberReadIn;
EXITS BadFile =>
MessageWindow.Append[Rope.Cat["Icon file ",tool.iconFileName," could not be loaded"],TRUE];
};
IconFetchProc: Menus.ClickProc = {
OPEN IconEditorDefs;
handle: IconHandle ← NARROW[clientData];
badNews: BOOLEANFALSE;
iconInfo: iconInfoRef ← NEW[iconInfoRec ← [handle, icon, handle.currentIC]];
iconNumberRope: Rope.ROPE ← ViewerTools.GetContents[handle.iconNumberWindow];
iconNumber: INTEGER;
iconFetchFileName: Rope.ROPE ← ViewerTools.GetContents[handle.iconFetchFileWindow];
h: IO.Handle ← IO.CreateInputStreamFromRope[iconNumberRope];
{ENABLE IO.Error => GOTO BadFile;
badNews ← FALSE;
iconNumber ← IO.GetInt[h ! IO.Error => { OPEN MessageWindow;
Append["Type a valid number in the Number window first!", TRUE];
Blink[]; badNews ← TRUE; CONTINUE }];
IF badNews THEN RETURN;
IF Rope.Size[iconNumberRope]=0 OR iconNumber<0 THEN {
MessageWindow.Append[message: "Could not fetch icon due to invalid icon number . . .",
clearFirst: TRUE];
RETURN};
TRUSTED {-- fetch the icon from the named file
fetchFile: IO.Handle ← FileIO.Open[fileName: iconFetchFileName];
numIconsInFile: INTEGER ← fetchFile.GetLength[]/(Environment.bytesPerWord*SIZE[iconFileFormat]);
IF iconNumber >= numIconsInFile THEN {
MessageWindow.Append[message: "Could not fetch icon due to invalid icon number . . .",
clearFirst: TRUE];
RETURN}
ELSE {
Set the file index to point at the icon we wish to fetch. Since each icon takes up
Environment.bytesPerWord*SIZE[iconFileFormat] bytes, we index into the file by multiples of this.
IO.SetIndex[fetchFile, iconNumber*(Environment.bytesPerWord*SIZE[iconFileFormat])];
Now read in the block
[] ← IO.UnsafeGetBlock[fetchFile,[LOOPHOLE[handle.icons[LOOPHOLE[handle.currentIC]]],
0, SIZE[iconFileFormat]*2]];
IO.Close[fetchFile];
handle.currentIconRep ← handle.icons[LOOPHOLE[handle.currentIC]];
};
};
ViewerOps.SetNewVersion[handle.container];
ViewerOps.PaintViewer[viewer: handle.viewer, hint: all, whatChanged: iconInfo, clearClient: FALSE];
EXITS
BadFile => MessageWindow.Append[message: Rope.Cat["Icon could not be fetched from ", iconFetchFileName], clearFirst: TRUE];
}};
BitMapToCurrentIcon: PUBLIC PROC [bitmap: REF ANY] = TRUSTED {
allows any bit map to be converted to icon format and allows the user to edit it
iconInfo: iconInfoRef;
handle: IconHandle ← NIL;
handle.currentIC ← IconEditorDefs.GetNewFlavor[handle];
handle.currentIconRep ← handle.icons[LOOPHOLE[handle.currentIC]] ← NEW[IconEditorDefs.iconFileFormat];
iconInfo ← NEW[iconInfoRec ← [handle, newIcon, handle.currentIC]];
handle.numberOfIcons ← handle.numberOfIcons + 1;
ViewerOps.SetNewVersion[handle.container];
make sure the new icon contains the bit map
handle.icons[LOOPHOLE[handle.currentIC]] ← LOOPHOLE[bitmap];
before we paint the screen, make sure new icon is visible
IF LOOPHOLE[handle.currentIC, CARDINAL] >= maxIconsOnDisplay THEN
handle.startDisplay ← (handle.startDisplay + 1) MOD handle.numberOfIcons;
ViewerOps.PaintViewer[viewer: handle.viewer, hint: client,whatChanged: iconInfo,
clearClient: FALSE];
};
UndoProc: PUBLIC Menus.MenuProc = {
OPEN IconEditorDefs;  
handle: IconHandle ← NARROW[clientData];
iconInfo: iconInfoRef ← NEW[iconInfoRec ← [handle, icon, handle.currentIC]];
ViewerOps.SetNewVersion[handle.container];
Undo[handle, handle.currentIC];
ViewerOps.PaintViewer[viewer: handle.viewer, hint: all, whatChanged: iconInfo,
clearClient: FALSE];
};
DeleteIconProc: PUBLIC Menus.MenuProc = {
handle: IconHandle ← NARROW[clientData];
iconInfo: iconInfoRef ← NEW[iconInfoRec ← [handle, screen, handle.currentIC]];
make sure we have at least one icon left after we delete
IF handle.numberOfIcons < 2 THEN {
MessageWindow.Append[message: "Sorry, can't delete the last icon.", clearFirst: TRUE];
MessageWindow.Blink[];
RETURN };
IF ~MessageWindow.Confirm["Confirm deletion of current icon..."] THEN RETURN;
DeleteIcon[handle];
ViewerOps.SetNewVersion[handle.container];
ViewerOps.PaintViewer[viewer: handle.viewer, hint: all, whatChanged: iconInfo, clearClient: FALSE];
};
CleanUp: PROCEDURE [handle: IconHandle, context: Graphics.Context] = {
IF handle.functionNotApplied THEN
IF handle.drewRectangle THEN {DrawRectangle[handle, context, handle.currentRectangle];
handle.drewRectangle ← FALSE};
IF handle.drewLine THEN {SketchLine[handle, context]; handle.drewLine ← FALSE};
};
IconPainter: PUBLIC ViewerClasses.PaintProc = {
ENABLE UNWIND => NULL;
iconInfo: iconInfoRef;
iconContext: Graphics.Context ← context;
bounds: Graphics.Box ← Graphics.GetBounds[iconContext];
MessageWindow.Append[clearFirst: TRUE, message: ""];
IF whatChanged=NIL THEN {
we better find out what viewer we want to be dealing with
handle: IconHandle ← NARROW[self.data];
LayoutBoard[handle, bounds ! ViewerNoGood => GOTO Done];
IF ~clear THEN ClearScreen[bounds, iconContext];
DrawIcons[handle, iconContext];
[] ← SetCurrentIcon[handle, XfromIC[handle, handle.currentIC]+10,
YfromIC[handle, handle.currentIC]+10];
DrawBoard[handle, iconContext]
}
ELSE {
iconInfo ← NARROW[whatChanged];
IconEditorDefs.LayoutBoard[iconInfo.handle, bounds ! ViewerNoGood => GOTO Done];
SELECT iconInfo.update FROM
screen => {
ClearScreen[bounds, iconContext];
iconInfo.handle.currentIC ← iconInfo.currentIcon;
DrawIcons[iconInfo.handle, iconContext];
[] ← SetCurrentIcon[iconInfo.handle,
XfromIC[iconInfo.handle, iconInfo.handle.currentIC]+10,
YfromIC[iconInfo.handle, iconInfo.handle.currentIC]+10];
DrawBoard[iconInfo.handle, iconContext];
};
board => DrawBoard[iconInfo.handle, iconContext];
icon => {
DrawIcon[iconInfo.handle, iconContext, LOOPHOLE[iconInfo.handle.currentIC ← iconInfo.currentIcon]];
DrawBoard[iconInfo.handle, iconContext]
};
bit => {
IF iconInfo.clearBit THEN RemoveBit[iconInfo.handle, iconContext, iconInfo.x, iconInfo.y]
ELSE AddBit[iconInfo.handle, iconContext, iconInfo.x, iconInfo.y];
};
newIcon => {
ClearScreen[bounds, iconContext];
DrawIcons[iconInfo.handle, iconContext];
DrawBoard[iconInfo.handle, iconContext];
DrawBorder[iconInfo.handle, iconContext];
};
mark => { 
IF iconInfo.handle.firstMark THEN {
Erase the previous rectangle or line if necessary
CleanUp[iconInfo.handle, iconContext];
iconInfo.handle.functionNotApplied ← TRUE;
[iconInfo.handle.mark1.x, iconInfo.handle.mark1.y] ← BoardCoords[
iconInfo.handle, iconInfo.x, iconInfo.y];
iconInfo.handle.currentRectangle.x ← iconInfo.handle.mark1.x;
iconInfo.handle.currentRectangle.y ← iconInfo.handle.mark1.y;
iconInfo.handle.currentRectangle.w ← 0;
iconInfo.handle.currentRectangle.h ← 0;
}
ELSE {
Erase the old rectangle
DrawRectangle[iconInfo.handle, iconContext, iconInfo.handle.currentRectangle];
[iconInfo.handle.mark2.x, iconInfo.handle.mark2.y] ←
BoardCoords[iconInfo.handle, iconInfo.x, iconInfo.y];
iconInfo.handle.currentRectangle ← IconEditorDefs.NormalizeRectangle[
iconInfo.handle.mark1, iconInfo.handle.mark2];
DrawRectangle[iconInfo.handle, iconContext, iconInfo.handle.currentRectangle];
}
};
line => {
IF iconInfo.handle.firstMark THEN {
Erase the previous line or rectangle if necessary
CleanUp[iconInfo.handle, iconContext];
Clear up the rectangle marking just in case
iconInfo.handle.currentRectangle.x ← 0;
iconInfo.handle.currentRectangle.y ← 0;
iconInfo.handle.currentRectangle.w ← 0;
iconInfo.handle.currentRectangle.h ← 0;
iconInfo.handle.functionNotApplied ← TRUE;
[iconInfo.handle.mark1.x, iconInfo.handle.mark1.y] ←
BoardCoords[iconInfo.handle, iconInfo.x, iconInfo.y];
iconInfo.handle.mark2.x ← iconInfo.handle.mark1.x;
iconInfo.handle.mark2.y ← iconInfo.handle.mark1.y;-- Initialize the line to be a point
iconInfo.handle.currentLine ←
IconEditorDefs.TransferEndPoints[iconInfo.handle.mark1, iconInfo.handle.mark2];
}
ELSE {
Erase the old line
SketchLine[iconInfo.handle, iconContext];
[iconInfo.handle.mark2.x, iconInfo.handle.mark2.y] ←
BoardCoords[iconInfo.handle, iconInfo.x, iconInfo.y];
iconInfo.handle.currentLine ←
IconEditorDefs.TransferEndPoints[iconInfo.handle.mark1, iconInfo.handle.mark2];
SketchLine[iconInfo.handle, iconContext];
};
};
rect => {
DrawRectangle[iconInfo.handle, iconContext, iconInfo.handle.labelRect];
};
ENDCASE;
};
iconContext ← NIL; -- just to be safe
EXITS Done => NULL;
};
AboutToDestroy: PUBLIC ViewerClasses.DestroyProc = {
ENABLE UNWIND => NULL;
handle: IconHandle ← NARROW[self.data];
Just to be safe, close the most recent icon file we were working with
We must find some way of getting the handle from the viewer
IO.Close[handle.iconFile];
};
XfromIC: PROC[handle: IconHandle, ic: IconEditorDefs.iconFlavor] RETURNS [CARDINAL] = {
i: CARDINALLOOPHOLE[ic];
realFlavor: CARDINAL ← (i - handle.startDisplay + handle.numberOfIcons) MOD
handle.numberOfIcons;
RETURN [handle.icX+(distX/2) + (realFlavor MOD handle.numIconsPerRow) *
(IconEditorDefs.iconW+distX)];
};
YfromIC: PROC[handle: IconHandle, ic: IconEditorDefs.iconFlavor] RETURNS [CARDINAL] = {
i: CARDINALLOOPHOLE[ic];
realFlavor: CARDINAL ← (i - handle.startDisplay + handle.numberOfIcons) MOD
handle.numberOfIcons;
RETURN [handle.icY + (distY/2) + (handle.numIconsPerCol-1-realFlavor /
handle.numIconsPerRow) * (IconEditorDefs.iconH+distY)];
};
ICfromXY: PROC[handle: IconHandle, x, y: REAL]
RETURNS [ignore: BOOLEAN, ic: IconEditorDefs.iconFlavor] = {
xC, yC: CARDINAL;
possibleIC, theIC: CARDINAL;
iconsOnDisplay: CARDINALMIN[handle.numberOfIcons, maxIconsOnDisplay];
ignore ← FALSE;
xC ← Real.FixC[x];
IF xC > handle.icX + (distX/2) THEN xC ← (xC-handle.icX-distX/2)/(IconEditorDefs.iconW+distX)
ELSE { ignore ← TRUE; xC ← 0};
yC ← Real.FixC[y];
IF yC > handle.icY + (distY/2) THEN yC ← (yC-handle.icY-distY/2)/(IconEditorDefs.iconH+distY)
ELSE { ignore ← TRUE; yC ← 0};
IF xC >= handle.numIconsPerRow THEN xC ← handle.numIconsPerRow-1;
IF yC >= handle.numIconsPerCol THEN yC ← handle.numIconsPerCol-1;
yC ← handle.numIconsPerCol-1-yC;
possibleIC ← handle.numIconsPerRow*yC+xC; -- the icon we might have hit
theIC ← (possibleIC + handle.startDisplay) MOD handle.numberOfIcons;
ic ← LOOPHOLE[theIC, IconEditorDefs.iconFlavor];
};
BoardCoords: PROC [handle: IconHandle, x, y: REAL] RETURNS [i, j: CARDINAL] = {
i ← IF x > handle.boardSize THEN IconEditorDefs.intHBound
ELSE Real.FixC[x/handle.unit];
j ← LOOPHOLE[MAX[LOOPHOLE[IconEditorDefs.intHBound-Real.FixC[y/handle.unit],INTEGER], 0]];
};
DeleteIcon: PROCEDURE [handle: IconHandle] = {
deletes the current icon and then compacts the array of icons.
IF LOOPHOLE[handle.currentIC, CARDINAL] < handle.numberOfIcons - 1 THEN {
the icon we wish to delete is in the middle of the array of icons; Compact.
FOR i: CARDINAL IN [LOOPHOLE[handle.currentIC, CARDINAL] .. handle.numberOfIcons-1) DO
handle.icons[LOOPHOLE[i]] ← handle.icons[LOOPHOLE[i+1]];
ENDLOOP;
}
ELSE {
we are deleting the last icon in the array.
handle.icons[LOOPHOLE[handle.currentIC]] ← NIL;
now let currentIC be the previous flavor. Since we always must have at least one
icon in existance, we will always have a previous flavor
handle.currentIC ← PRED[handle.currentIC];
};
handle.numberOfIcons ← handle.numberOfIcons - 1;
handle.nextFlavor ← PRED[handle.nextFlavor];
handle.currentIconRep ← handle.icons[LOOPHOLE[handle.currentIC]];
};
Undo: PROC [handle: IconHandle, flavor: IconEditorDefs.iconFlavor] = {
handle.icons[LOOPHOLE[flavor]].bits ← handle.savedBitMap;
};
DrawIcons: PROC [handle: IconHandle, context: Graphics.Context] = {
FOR i: CARDINAL IN [0..MIN[handle.numberOfIcons, maxIconsOnDisplay]) DO
DrawIcon[handle, context, (handle.startDisplay + i) MOD handle.numberOfIcons];
ENDLOOP;
};
DrawBit: PROC [handle: IconHandle, context: Graphics.Context, x, y: CARDINAL] = {
OPEN IconEditorDefs;  -- for intHBound and unit.
lx: REAL ← x*handle.unit;
by: REAL ← (intHBound-y)*handle.unit;
[] ← Graphics.SetPaintMode[context, invert];
Graphics.DrawBox[context, [xmin: lx+1, ymin: by, xmax: lx+handle.unit, ymax: by+handle.unit-1]];
[] ← Graphics.SetPaintMode[context, opaque];
};
ClearScreen: PROC [bounds: Graphics.Box, context: Graphics.Context] = {
p: Graphics.Path ← Graphics.NewPath[5];
Graphics.SetColor[context, background];
Graphics.Rectangle[p, 0.0, 0.0, bounds.xmax, bounds.ymax];
Graphics.DrawArea[context, p];
Graphics.SetColor[context, foreground];
};
DrawBox: PROCEDURE [context: Graphics.Context, x0, y0, x1, y1: INTEGER, thickness: REAL] = {
Draws a box on the screen with bottom left corner (x0, y0) and top right corner (x1, y1)
p: Graphics.Path ← Graphics.NewPath[5];
[] ← Graphics.SetPaintMode[context, invert];
Graphics.MoveTo[p, x0, y0];
Graphics.LineTo[p, x0, y1];
Graphics.DrawStroke[context, p, normalThickness];
Graphics.FlushPath[p];
Graphics.MoveTo[p, x0 + 1, y1];
Graphics.LineTo[p, x1, y1];
Graphics.DrawStroke[context, p, normalThickness];
Graphics.FlushPath[p];
Graphics.MoveTo[p, x1, y1 - 1];
Graphics.LineTo[p, x1, y0];
Graphics.DrawStroke[context, p, normalThickness];
Graphics.FlushPath[p];
Graphics.MoveTo[p, x1 -1, y0];
Graphics.LineTo[p, x0, y0];
Graphics.DrawStroke[context, p, thickness];
[] ← Graphics.SetPaintMode[context, opaque];
};
DrawIcon: PROC [handle: IconHandle, context: Graphics.Context, index: CARDINAL] = {
IF handle.icons[LOOPHOLE[index]] = NIL THEN RETURN-- Just to be safe
ELSE {
iconBitmap: REF GraphicsOps.BitmapRep ← NEW[GraphicsOps.BitmapRep ←
[NIL, IconEditorDefs.iconW/bitsPerWord, IconEditorDefs.iconW, IconEditorDefs.iconH]];
iconBitmap.base ← handle.icons[LOOPHOLE[index]];
Graphics.SetCP[context, XfromIC[handle, LOOPHOLE[index]],
YfromIC[handle, LOOPHOLE[index]] + IconEditorDefs.iconH];
GraphicsOps.DrawBitmap[context, iconBitmap, IconEditorDefs.iconW, 
IconEditorDefs.iconH];
};
};
DrawBoard: PROC [handle: IconHandle, context: Graphics.Context] = {
OPEN IconEditorDefs; -- intHBound (to make code less cumbersome)
x,y: REAL;
[x,y] ← Graphics.GetCP[context];
Graphics.SetColor[context, background];
Graphics.DrawBox[context, [0.0, 0.0, handle.boardSize, handle.boardSize]];
Graphics.SetColor[context, foreground];
FOR i: CARDINAL IN [0..intHBound+1] DO
temp: INT;
Graphics.SetCP[context, temp ← i*handle.unit, 0.0];
Graphics.DrawTo[context, temp, handle.boardSize];
Graphics.SetCP[context, 0.0, temp ← i*handle.unit];
Graphics.DrawTo[context, handle.boardSize, temp];
ENDLOOP;
Graphics.SetCP[context, x, y];
DrawCurrentIcon[handle, context];
handle.drewRectangle ← handle.drewLine ← FALSE;
handle.functionNotApplied ← FALSE;
};
DrawCurrentIcon: PROC [handle: IconHandle, context: Graphics.Context] = {
[] ← Graphics.SetPaintMode[context, invert];
FOR i: CARDINAL IN IconEditorDefs.intH DO
FOR j: CARDINAL IN IconEditorDefs.intW DO
IF handle.currentIconRep.bits[i].b[j] THEN {
lx: REAL ← j*handle.unit;
by: REAL ← (IconEditorDefs.intHBound-i)*handle.unit;
Graphics.DrawBox[context, [xmin: lx+1, ymin: by, xmax: lx+handle.unit, ymax: by+handle.unit-1]];
};
ENDLOOP;
ENDLOOP;
[] ← Graphics.SetPaintMode[context, opaque];
};
DrawRectangle: PROC [handle: IconHandle, context: Graphics.Context,
rect: IconEditorDefs.rectangleRec] = {
Draws a rectangle on the drawing board
OPEN IconEditorDefs;  -- for unit, intHBound
rX0, rY0, rX1, rY1: INTEGER;
rX0 ← rect.x*handle.unit - 1;       -- top right corner
rY0 ← (intHBound-rect.y + 1)*handle.unit + 1;
rX1 ← (rect.x + rect.w)*handle.unit + 1;    -- bottom left corner
rY1 ← (intHBound-rect.y - rect.h + 1)*handle.unit - 1;
DrawBox[context, rX0, rY0, rX1, rY1, normalThickness];
handle.drewRectangle ← TRUE;
};
DrawBorder: PROC [handle: IconHandle, context: Graphics.Context] = {
draws a thin border around newly created icons
x0, y0, x1, y1: INTEGER;
x0 ← XfromIC[handle, handle.currentIC];
y0 ← YfromIC[handle, handle.currentIC]+1;
x1 ← x0 + IconEditorDefs.iconW - 1;
y1 ← y0 + IconEditorDefs.iconH - 1;
DrawBox[context, x0, y0, x1, y1, normalThickness];
};
SketchLine: PROC [handle: IconHandle, context: Graphics.Context] = {
Draws a thin line from one endpoint to another
line: IconEditorDefs.lineRec ← IconEditorDefs.ComputeEndPoints[handle, handle.currentLine];
p: Graphics.Path ← Graphics.NewPath[];
[] ← Graphics. SetPaintMode[context, invert];
Graphics.MoveTo[p, line.x1, line.y1];
Graphics.LineTo[p, line.x2, line.y2];
Graphics.DrawStroke[context, p, normalThickness];
[] ← Graphics.SetPaintMode[context, opaque];
handle.drewLine ← TRUE;
};
SetCurrentIcon: PROC [handle: IconHandle, x, y: REAL] RETURNS [ignored: BOOLEAN] = {
ic: IconEditorDefs.iconFlavor;
[ignored, ic] ← ICfromXY[handle, x, y];
IF ~ignored THEN {
handle.currentIC ← ic;
handle.currentIconRep ← handle.icons[LOOPHOLE[handle.currentIC]];
}
};
BitSet: PROC [handle: IconHandle, x, y: REAL] RETURNS [BOOLEAN] = {
xC, yC: CARDINAL;
[xC, yC] ← BoardCoords[handle, x, y];
RETURN [handle.currentIconRep.bits[yC].b[xC]];
};
AddBit: PROC [handle: IconHandle, context: Graphics.Context, x, y: REAL] = {
xC, yC: CARDINAL;
[xC, yC] ← BoardCoords[handle, x, y];
handle.currentIconRep.bits[yC].b[xC] ← TRUE;
DrawBit[handle, context, xC, yC];
DrawIcon[handle, context, LOOPHOLE[handle.currentIC]];
};
RemoveBit: PROC [handle: IconHandle, context: Graphics.Context, x, y: REAL] = {
xC, yC: CARDINAL;
[xC, yC] ← BoardCoords[handle, x, y];
handle.currentIconRep.bits[yC].b[xC] ← FALSE;
DrawBit[handle, context, xC, yC];
DrawIcon[handle, context, LOOPHOLE[handle.currentIC]];
};
NotifyEvents: ViewerClasses.NotifyProc = { -- react on user input
ENABLE UNWIND => NULL; -- release lock
iMouse, jMouse: REAL;
erase: BOOLEAN;
handle: IconHandle ← NARROW[self.data];
InterpretAtom: PROC [atom: ATOM] = {
SELECT atom FROM
$Erase => erase ← TRUE; 
$Select => {
IF iMouse > handle.boardSize OR jMouse > handle.boardSize THEN {
ignored: BOOLEAN ← SetCurrentIcon[handle, iMouse, jMouse];
IF ~ignored THEN TRUSTED {
iconInfo: iconInfoRef ← NEW[iconInfoRec ← [handle, icon, handle.currentIC]];
IF Runtime.IsBound[IconRegistry.IsRegistered] THEN
ViewerTools.SetContents[handle.iconNameWindow,
IconRegistry.IsRegistered[
fileName: ViewerTools.GetContents[handle.iconFileWindow],
index: LOOPHOLE[handle.currentIC, CARDINAL]].name]; -- w.t.
ViewerOps.PaintViewer[viewer: handle.viewer, hint: client,
whatChanged: iconInfo, clearClient: FALSE];
};
};
};
$Draw => { -- SetBit / RemoveBit
IF iMouse < handle.boardSize AND jMouse < handle.boardSize THEN {
ViewerOps.SetNewVersion[handle.container];
IF erase = BitSet[handle, iMouse, jMouse] THEN {
iconInfo: iconInfoRef ← NEW[iconInfoRec←[handle, bit, handle.currentIC, erase,
iMouse, jMouse]];
ViewerOps.PaintViewer[viewer: handle.viewer, hint: client, whatChanged:
iconInfo, clearClient: FALSE];
};
};
};
$Mark1 => {
IF iMouse < handle.boardSize AND jMouse < handle.boardSize THEN {
iconInfo: iconInfoRef ← NEW[iconInfoRec ←
[handle, mark, handle.currentIC, erase, iMouse, jMouse]];
handle.firstMark ← TRUE;
ViewerOps.SetNewVersion[handle.container];
ViewerOps.PaintViewer[viewer: handle.viewer, hint: client, whatChanged: iconInfo,
clearClient: FALSE];
};
};
$Mark2 => {
IF iMouse < handle.boardSize AND jMouse < handle.boardSize THEN {
iconInfo: iconInfoRef ← NEW[iconInfoRec ←
[handle, mark, handle.currentIC, erase, iMouse, jMouse]];
handle.firstMark ← FALSE;
ViewerOps.SetNewVersion[handle.container];
ViewerOps.PaintViewer[viewer: handle.viewer, hint: client, whatChanged: iconInfo,
clearClient: FALSE]
};
};
$EndPoint1 => {
IF iMouse < handle.boardSize AND jMouse < handle.boardSize THEN {
iconInfo: iconInfoRef ← NEW[iconInfoRec ←
[handle, line, handle.currentIC, erase, iMouse, jMouse]];
handle.firstMark ← TRUE;
ViewerOps.SetNewVersion[handle.container];
ViewerOps.PaintViewer[viewer: handle.viewer, hint: client,whatChanged: iconInfo,
clearClient: FALSE]
};
};
$EndPoint2 => { 
IF iMouse < handle.boardSize AND jMouse < handle.boardSize THEN {
iconInfo: iconInfoRef ← NEW[iconInfoRec ← [handle, line, handle.currentIC, erase,
iMouse, jMouse]];
handle.firstMark ← FALSE;
ViewerOps.SetNewVersion[handle.container];
ViewerOps.PaintViewer[viewer: handle.viewer, hint: client, whatChanged: iconInfo,
clearClient: FALSE]
};
};
ENDCASE;
}; -- InterpretAtom
FOR params: LIST OF REF ANY ← input, params.rest UNTIL params=NIL DO
WITH params.first SELECT FROM
x: TIPUser.TIPScreenCoords => {
iMouse ← x.mouseX; jMouse ← x.mouseY; erase ← FALSE;
};
x: ATOM => InterpretAtom[x];
ENDCASE;
ENDLOOP;
};
[] ← StartIconEditor[];
}.
Edited on April 11, 1983 4:13 pm, by Teitelman
added code to create an icon name field for use in conjunction with register button, and to fill Name field when icon selected,
changes to: DIRECTORY, IconEditorImpl, StartIconEditor, IconFetchProc, InterpretAtom (local of NotifyEvents), DIRECTORY, IconEditorImpl, StartIconEditor, IconFetchProc, InterpretAtom (local of NotifyEvents), }
Edited on April 12, 1983 5:03 pm, by Teitelman
changes to: InterpretAtom (local of NotifyEvents), DIRECTORY, IconEditorImpl
Edited on April 23, 1983 1:51 pm, by Teitelman
InterpretAtom was getting name of file from fetchIconFileWindow rather than fetchFileWIndow
changes to: InterpretAtom (local of NotifyEvents)