IconEditorImplB.mesa
Last Edited by: Ken Pier, May 31, 1985 10:28:06 am PDT
DIRECTORY
Basics USING [bitsPerWord, bytesPerWord],
Buttons USING [Button],
ChoiceButtons USING [BuildTextPrompt, PromptDataRef],
Commander USING [CommandProc, Register],
CommandTool USING [CurrentWorkingDirectory],
Containers USING [Container, Create, ChildXBound, ChildYBound],
FS USING [StreamOpen, Error],
IconEditorDefs,
IconRegistry USING [IsRegistered],
Icons USING [IconFlavor, NewIconFromFile],
Imager USING [black, Box, Context, DoSave, DoSaveAll, MaskBox, MaskRectangleI, MaskStroke, Rectangle, SetColor, SetStrokeWidth, white],
ImagerBackdoor USING [GetBounds, invert, DrawBits],
ImagerColorDefs USING [ConstantColor],
ImagerPath USING [PathProc],
IO USING [Close, EndOfStream, Error, GetInt, GetLength, RIS, SetIndex, STREAM, UnsafeGetBlock],
LFBoundingBox USING [GetArea],
MBQueue USING [Queue, Create, CreateButton],
Menus USING [ClickProc, Menu, MenuProc],
MessageWindow USING [Append, Blink, Confirm],
PrincOps USING [BBptr, BBTableSpace],
PrincOpsUtils USING [AlignedBBTable, BITBLT, IsBound],
Real USING [FixC, FixI],
Rope USING [Cat, ROPE, Size],
Rules USING [Create],
RuntimeError USING [BoundsFault],
Terminal USING [Current, FrameBuffer, GetBWFrameBuffer, Virtual],
TIPUser USING [TIPScreenCoords, TIPTable, InstantiateNewTIPTable],
ViewerClasses USING [DestroyProc, NotifyProc, PaintProc, Viewer, ViewerClass,
ViewerClassRec],
ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass, SetNewVersion],
ViewerTools USING [GetContents, SetContents]
;
IconEditorImplB: CEDAR PROGRAM
IMPORTS ChoiceButtons, Commander, CommandTool, Containers, Icons, IconEditorDefs, IconRegistry, Imager, ImagerBackdoor, IO, MBQueue, MessageWindow, Real, Rope, Rules, RuntimeError, PrincOpsUtils, TIPUser, ViewerOps, ViewerTools, FS, Terminal, LFBoundingBox
EXPORTS IconEditorDefs = {
foreground: ImagerColorDefs.ConstantColor ← Imager.black;
background: ImagerColorDefs.ConstantColor ← Imager.white;
iconEditorIcon: Icons.IconFlavor = Icons.NewIconFromFile["IconEditor.icons", 0];
iconEditorTipTable: TIPUser.TIPTable = TIPUser.InstantiateNewTIPTable["IconEditor.TIP"];
installationDirectory: Rope.ROPE = CommandTool.CurrentWorkingDirectory[];
StartIconEditor: Commander.CommandProc = {
ENABLE UNWIND => NULL;
tool: IconEditorDefs.IconHandle ← NEW [IconEditorDefs.IconHandleRec];
{
iconFileInfo, iconNumberInfo: ChoiceButtons.PromptDataRef;
iconFetchButton: Buttons.Button;
iconViewerClass: ViewerClasses.ViewerClass ← NEW[ViewerClasses.ViewerClassRec ← [
paint: IconPainter,
notify: NotifyEvents,
destroy: AboutToDestroy,
tipTable: iconEditorTipTable
]];
tool.queue ← MBQueue.Create[]; --create an automaticallly managed queue
tool.iconFileName ← installationDirectory.Cat["Sample.icons"];
tool.container ← Containers.Create[info: [name: "Icon Editor",
scrollable: FALSE, iconic: TRUE, column: left, icon: iconEditorIcon]];
tool.container.menu ← IconEditorDefs.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 ← MBQueue.CreateButton[q: tool.queue, 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+IconEditorDefs.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 = {
handle: IconEditorDefs.IconHandle ← NARROW[clientData];
fetchFile: IO.STREAM;
numIconsInFile: IconEditorDefs.Nat;
badNews: BOOLEANFALSE;
iconInfo: IconEditorDefs.IconInfoRef ← NEW[IconEditorDefs.IconInfoRec ← [handle, icon, handle.currentIC]];
iconNumberRope: Rope.ROPE ← ViewerTools.GetContents[handle.iconNumberWindow];
iconNumber: IconEditorDefs.Nat;
iconFetchFileName: Rope.ROPE ← ViewerTools.GetContents[handle.iconFetchFileWindow];
h: IO.STREAMIO.RIS[iconNumberRope];
{ENABLE IO.Error => GOTO BadFile;
badNews ← FALSE;
iconNumber ← h.GetInt[ ! IO.Error, IO.EndOfStream => {
MessageWindow.Append["Type a valid number in the Number window first!", TRUE];
MessageWindow.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
};
fetchFile ← FS.StreamOpen[fileName: iconFetchFileName ! FS.Error => {
MessageWindow.Append[iconFetchFileName, TRUE];
MessageWindow.Append[": ", FALSE];
MessageWindow.Append[error.explanation, FALSE];
MessageWindow.Blink[]; badNews ← TRUE; CONTINUE
}];
IF badNews THEN RETURN;
numIconsInFile ← fetchFile.GetLength[]/(Basics.bytesPerWord*SIZE[IconEditorDefs.IconFileFormat]);
IF iconNumber >= numIconsInFile THEN {
MessageWindow.Append[message: "Could not fetch icon due to invalid icon number",
clearFirst: TRUE];
RETURN
}
ELSE TRUSTED {
Set the file index to point at the icon we wish to fetch. Since each icon takes up
Basics.bytesPerWord*SIZE[IconFileFormat] bytes, we index into the file by multiples of this.
fetchFile.SetIndex[iconNumber*(Basics.bytesPerWord*SIZE[IconEditorDefs.IconFileFormat])];
Now read in the block
IF shift THEN { --shift means OR new icon bitmap into current bitmap
block: REF IconEditorDefs.BitArray ← NEW[IconEditorDefs.BitArray];
[] ← fetchFile.UnsafeGetBlock[block: [base: LOOPHOLE[block], startIndex: 0, count: SIZE[IconEditorDefs.BitArray]*Basics.bytesPerWord] ! RuntimeError.BoundsFault => GOTO BadFile];
FOR h: IconEditorDefs.IntH IN IconEditorDefs.IntH DO
FOR w: IconEditorDefs.IntW IN IconEditorDefs.IntW DO
handle.icons[LOOPHOLE[handle.currentIC]].bits[h].b[w] ← handle.icons[LOOPHOLE[handle.currentIC]].bits[h].b[w] OR block[h].b[w];
ENDLOOP;
ENDLOOP;
}
ELSE [] ← fetchFile.UnsafeGetBlock[block: [base: LOOPHOLE[handle.icons[LOOPHOLE[handle.currentIC]]], startIndex: 0, count: SIZE[IconEditorDefs.IconFileFormat]*Basics.bytesPerWord] ! RuntimeError.BoundsFault => GOTO BadFile];
fetchFile.Close[];
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];
}
};
FillIcon: PUBLIC PROC [handle: IconEditorDefs.IconHandle] = TRUSTED {
allows any screen bit map to be converted to icon format
iconInfo: IconEditorDefs.IconInfoRef;
handle.currentIC ← IconEditorDefs.GetNewFlavor[handle];
handle.currentIconRep ← handle.icons[LOOPHOLE[handle.currentIC]] ← NEW[IconEditorDefs.IconFileFormat];
iconInfo ← NEW[IconEditorDefs.IconInfoRec ← [handle, newIcon, handle.currentIC]];
handle.numberOfIcons ← handle.numberOfIcons + 1;
ViewerOps.SetNewVersion[handle.container];
make the new icon contain the screen bit map
handle.icons[LOOPHOLE[handle.currentIC]] ← GetIconBitmap[];
before we paint the screen, make sure new icon is visible
IF LOOPHOLE[handle.currentIC, IconEditorDefs.Nat] >= IconEditorDefs.maxIconsOnDisplay THEN
handle.startDisplay ← (handle.startDisplay + 1) MOD handle.numberOfIcons;
ViewerOps.PaintViewer[viewer: handle.viewer, hint: client, whatChanged: NIL,
clearClient: FALSE];
};
GetIconBitmap: PROC [] RETURNS [ir: IconEditorDefs.IconRef] ~ TRUSTED {
x,y,w,h: NAT;
bbtSpace: PrincOps.BBTableSpace;
bbt: PrincOps.BBptr ← PrincOpsUtils.AlignedBBTable[@bbtSpace];
vt: Terminal.Virtual = Terminal.Current[];
frameBuf: Terminal.FrameBuffer ← Terminal.GetBWFrameBuffer[vt];
[x,y,w,h] ← LFBoundingBox.GetArea[];
y ← MAX[vt.bwHeight-y-h, 0]; --LFBB uses lower left, vt uses top and upper left
ir ← NEW[IconEditorDefs.IconFileFormat]; --get new storage for bit array, etc.
bbt.src ← [word: frameBuf.base+(LONG[y]*frameBuf.wordsPerLine)+(x/16), bit: x MOD 16];
bbt.srcDesc ← [srcBpl[srcBpl: frameBuf.wordsPerLine*Basics.bitsPerWord]];
bbt.width ← IconEditorDefs.iconW;
bbt.height ← IconEditorDefs.iconH;
bbt.dst ← [word: @ir.bits, bit: 0];
bbt.dstBpl ← IconEditorDefs.iconW;
bbt.flags ← [disjoint: TRUE, gray: FALSE];
PrincOpsUtils.BITBLT[bbt]; --copy screen bitmap into IconFileFormat
};
UndoProc: PUBLIC Menus.MenuProc = {
handle: IconEditorDefs.IconHandle ← NARROW[clientData];
iconInfo: IconEditorDefs.IconInfoRef ← NEW[IconEditorDefs.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: IconEditorDefs.IconHandle ← NARROW[clientData];
iconInfo: IconEditorDefs.IconInfoRef ← NEW[IconEditorDefs.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: PROC [handle: IconEditorDefs.IconHandle, context: Imager.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 = {
PaintProc: TYPE = PROC [self: Viewer, context: Imager.Context, whatChanged: REF, clear: BOOL] RETURNS [quit: BOOLFALSE];
ENABLE UNWIND => NULL;
iconInfo: IconEditorDefs.IconInfoRef;
iconContext: Imager.Context ← context;
boundRect: Imager.Rectangle ← ImagerBackdoor.GetBounds[iconContext];
bounds: Imager.Box ← [xmin: boundRect.x, ymin: boundRect.y, xmax: boundRect.x+boundRect.w, ymax: boundRect.y+boundRect.h];
MessageWindow.Append[clearFirst: TRUE, message: ""];
IF whatChanged=NIL THEN {
we better find out what viewer we want to be dealing with
handle: IconEditorDefs.IconHandle ← NARROW[self.data];
IconEditorDefs.LayoutBoard[handle, bounds ! IconEditorDefs.ViewerNoGood => GOTO Done];
IF ~clear THEN ClearScreen[iconContext, bounds];
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 ! IconEditorDefs.ViewerNoGood => GOTO Done];
SELECT iconInfo.update FROM
screen => {
ClearScreen[iconContext, bounds];
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[iconContext, bounds];
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: IconEditorDefs.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
handle.iconFile.Close[];
};
XfromIC: PROC[handle: IconEditorDefs.IconHandle, ic: IconEditorDefs.IconFlavor] RETURNS [IconEditorDefs.Nat] = {
i: IconEditorDefs.Nat ← LOOPHOLE[ic];
realFlavor: IconEditorDefs.Nat ← (i - handle.startDisplay + handle.numberOfIcons) MOD
handle.numberOfIcons;
RETURN [handle.icX+(IconEditorDefs.distX/2) + (realFlavor MOD handle.numIconsPerRow) *
(IconEditorDefs.iconW+IconEditorDefs.distX)];
};
YfromIC: PROC[handle: IconEditorDefs.IconHandle, ic: IconEditorDefs.IconFlavor] RETURNS [IconEditorDefs.Nat] = {
i: IconEditorDefs.Nat ← LOOPHOLE[ic];
realFlavor: IconEditorDefs.Nat ← (i - handle.startDisplay + handle.numberOfIcons) MOD
handle.numberOfIcons;
RETURN [handle.icY + (IconEditorDefs.distY/2) + (handle.numIconsPerCol-1-realFlavor /
handle.numIconsPerRow) * (IconEditorDefs.iconH+IconEditorDefs.distY)];
};
ICfromXY: PROC[handle: IconEditorDefs.IconHandle, x, y: REAL]
RETURNS [ignore: BOOLEAN, ic: IconEditorDefs.IconFlavor] = {
xC, yC: IconEditorDefs.Nat;
possibleIC, theIC: IconEditorDefs.Nat;
iconsOnDisplay: IconEditorDefs.Nat ← MIN[handle.numberOfIcons, IconEditorDefs.maxIconsOnDisplay];
ignore ← FALSE;
xC ← Real.FixC[x];
IF xC > handle.icX + (IconEditorDefs.distX/2) THEN xC ← (xC-handle.icX-IconEditorDefs.distX/2)/(IconEditorDefs.iconW+IconEditorDefs.distX)
ELSE { ignore ← TRUE; xC ← 0};
yC ← Real.FixC[y];
IF yC > handle.icY + (IconEditorDefs.distY/2) THEN yC ← (yC-handle.icY-IconEditorDefs.distY/2)/(IconEditorDefs.iconH+IconEditorDefs.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: IconEditorDefs.IconHandle, x, y: REAL] RETURNS [i, j: IconEditorDefs.Nat] = {
i ← IF x > handle.boardSize THEN IconEditorDefs.intWBound ELSE Real.FixI[x/handle.unit];
j ← MAX[IconEditorDefs.intHBound-Real.FixI[y/handle.unit], 0];
};
DeleteIcon: PROC [handle: IconEditorDefs.IconHandle] = {
deletes the current icon and then compacts the array of icons.
IF LOOPHOLE[handle.currentIC, IconEditorDefs.Nat] < handle.numberOfIcons - 1 THEN {
the icon we wish to delete is in the middle of the array of icons; Compact.
FOR i: IconEditorDefs.Nat IN [LOOPHOLE[handle.currentIC, IconEditorDefs.Nat] .. handle.numberOfIcons-1) DO
handle.icons[LOOPHOLE[i]] ← handle.icons[LOOPHOLE[i+1]];
ENDLOOP;
handle.icons[handle.numberOfIcons - 1] ← NIL; --forget prior last icon
}
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: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor] = {
handle.icons[LOOPHOLE[flavor]].bits ← handle.savedBitMap;
};
DrawIcons: PROC [handle: IconEditorDefs.IconHandle, context: Imager.Context] = {
FOR i: IconEditorDefs.Nat IN [0..MIN[handle.numberOfIcons, IconEditorDefs.maxIconsOnDisplay]) DO
DrawIcon[handle, context, (handle.startDisplay + i) MOD handle.numberOfIcons];
ENDLOOP;
};
DrawBit: PROC [handle: IconEditorDefs.IconHandle, context: Imager.Context, x, y: IconEditorDefs.Nat] = {
DrawBitProc: PROC = {
Imager.SetColor[context, ImagerBackdoor.invert];
Imager.MaskBox[context, [xmin: lx+1.0, ymin: by+1.0, xmax: lx+handle.unit, ymax: by+handle.unit]];
};
lx: REAL ← x*handle.unit;
by: REAL ← (IconEditorDefs.intHBound-y)*handle.unit;
Imager.DoSaveAll[context: context, action: DrawBitProc];
};
ClearScreen: PROC [context: Imager.Context, bounds: Imager.Box] = {
Imager.SetColor[context, background];
Imager.MaskBox[context: context, box: bounds];
Imager.SetColor[context, foreground];
};
DrawBox: PROC [context: Imager.Context, x0, y0, x1, y1: IconEditorDefs.Nat] = {
Draws a box on the screen with bottom left corner (x0, y0) and top right corner (x1, y1)
DrawBoxProc: PROC = {
DrawBoxPathProc: ImagerPath.PathProc = {
moveTo[[x0, y0]];
lineTo[[x0, y1]];
lineTo[[x1, y1]];
lineTo[[x1, y0]];
lineTo[[x0, y0]];
};
Imager.SetColor[context, ImagerBackdoor.invert];
Imager.SetStrokeWidth[context: context, strokeWidth: IconEditorDefs.normalThickness];
Imager.MaskStroke[context: context, path: DrawBoxPathProc];
};
Imager.DoSaveAll[context: context, action: DrawBoxProc];
};
DrawIcon: PROC [handle: IconEditorDefs.IconHandle, context: Imager.Context, index: IconEditorDefs.Nat] = {
THIS PROC WILL PROBABLY NOT DO THE RIGHT THING!! CHECK IT !!
IF handle.icons[LOOPHOLE[index]] = NIL THEN RETURN -- Just to be safe
ELSE {
base: LONG POINTERLOOPHOLE[handle.icons[LOOPHOLE[index]]];
wordsPerLine: NAT ← IconEditorDefs.iconW/Basics.bitsPerWord;
ImagerBackdoor.DrawBits[context: context, base: base, wordsPerLine: wordsPerLine,
sMin: 0, fMin: 0, sSize: IconEditorDefs.iconW, fSize: IconEditorDefs.iconH, tx: XfromIC[handle: handle, ic: LOOPHOLE[index]], ty: YfromIC[handle: handle, ic: LOOPHOLE[index]] + IconEditorDefs.iconH];
};
};
DrawBoard: PROC [handle: IconEditorDefs.IconHandle, context: Imager.Context] = {
DrawBoardProc: PROC = {
bs: INTEGER ← Real.FixI[handle.boardSize];
Imager.SetColor[context, background];
Imager.MaskRectangleI[context: context, x: 0, y: 0, w: bs, h: bs];
Imager.SetColor[context, foreground];
FOR i: IconEditorDefs.Nat IN [0..IconEditorDefs.intHBound+1] DO
Imager.MaskRectangleI[context: context, x: i*handle.unit, y: 0, w: 1, h: bs];
Imager.MaskRectangleI[context: context, x: 0, y: i*handle.unit, w: bs, h: 1];
ENDLOOP;
};
Imager.DoSaveAll[context: context, action: DrawBoardProc];
DrawCurrentIcon[handle, context];
handle.drewRectangle ← handle.drewLine ← FALSE;
handle.functionNotApplied ← FALSE;
};
DrawCurrentIcon: PROC [handle: IconEditorDefs.IconHandle, context: Imager.Context] = {
DrawCIProc: PROC = {
Imager.SetColor[context, ImagerBackdoor.invert];
FOR i: IconEditorDefs.Nat IN IconEditorDefs.IntH DO
FOR j: IconEditorDefs.Nat IN IconEditorDefs.IntW DO
IF handle.currentIconRep.bits[i].b[j] THEN {
lx: REAL ← j*handle.unit;
by: REAL ← (IconEditorDefs.intHBound-i)*handle.unit;
Imager.MaskBox[context, [xmin: lx+1.0, ymin: by+1.0, xmax: lx+handle.unit, ymax: by+handle.unit]];
};
ENDLOOP;
ENDLOOP;
};
Imager.DoSave[context: context, action: DrawCIProc];
};
DrawRectangle: PROC [handle: IconEditorDefs.IconHandle, context: Imager.Context,
rect: IconEditorDefs.RectangleRec] = {
Draws a rectangle on the drawing board
rX0, rY0, rX1, rY1: IconEditorDefs.Nat;
rX0 ← rect.x*handle.unit - 1; -- top right corner
rY0 ← (IconEditorDefs.intHBound-rect.y + 1)*handle.unit + 1;
rX1 ← (rect.x + rect.w)*handle.unit + 1; -- bottom left corner
rY1 ← (IconEditorDefs.intHBound-rect.y - rect.h + 1)*handle.unit - 1;
DrawBox[context, rX0, rY0, rX1, rY1];
handle.drewRectangle ← TRUE;
};
DrawBorder: PROC [handle: IconEditorDefs.IconHandle, context: Imager.Context] = {
draws a thin border around newly created icons
x0, y0, x1, y1: IconEditorDefs.Nat;
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];
};
SketchLine: PROC [handle: IconEditorDefs.IconHandle, context: Imager.Context] = {
Draws a thin line from one endpoint to another
SLProc: PROC = {
SLPathProc: ImagerPath.PathProc = {
moveTo[[line.x1, line.y1]];
lineTo[[line.x2, line.y2]];
};
Imager.SetColor[context, ImagerBackdoor.invert];
Imager.SetStrokeWidth[context: context, strokeWidth: IconEditorDefs.normalThickness];
Imager.MaskStroke[context: context, path: SLPathProc];
};
line: IconEditorDefs.LineRec ← IconEditorDefs.ComputeEndPoints[handle, handle.currentLine];
Imager.DoSaveAll[context: context, action: SLProc];
handle.drewLine ← TRUE;
};
SetCurrentIcon: PROC [handle: IconEditorDefs.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: IconEditorDefs.IconHandle, x, y: REAL] RETURNS [BOOLEAN] = {
xC, yC: IconEditorDefs.Nat;
[xC, yC] ← BoardCoords[handle, x, y];
RETURN [handle.currentIconRep.bits[yC].b[xC]];
};
AddBit: PROC [handle: IconEditorDefs.IconHandle, context: Imager.Context, x, y: REAL] = {
xC, yC: IconEditorDefs.Nat;
[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: IconEditorDefs.IconHandle, context: Imager.Context, x, y: REAL] = {
xC, yC: IconEditorDefs.Nat;
[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
NotifyProc: TYPE = PROC [self: Viewer, input: LIST OF REF ANY];
ENABLE UNWIND => NULL; -- release lock
iMouse, jMouse: REAL;
erase: BOOLEAN;
handle: IconEditorDefs.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: IconEditorDefs.IconInfoRef ← NEW[IconEditorDefs.IconInfoRec ← [handle, icon, handle.currentIC]];
IF PrincOpsUtils.IsBound[LOOPHOLE[IconRegistry.IsRegistered]] THEN
ViewerTools.SetContents[handle.iconNameWindow,
IconRegistry.IsRegistered[
fileName: ViewerTools.GetContents[handle.iconFileWindow],
index: LOOPHOLE[handle.currentIC, IconEditorDefs.Nat]].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: IconEditorDefs.IconInfoRef ← NEW[IconEditorDefs.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: IconEditorDefs.IconInfoRef ← NEW[IconEditorDefs.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: IconEditorDefs.IconInfoRef ← NEW[IconEditorDefs.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: IconEditorDefs.IconInfoRef ← NEW[IconEditorDefs.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: IconEditorDefs.IconInfoRef ← NEW[IconEditorDefs.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;
};
Commander.Register["IconEditor", StartIconEditor, "create a tool to create and edit icon files"];
}.