IconEditorImplB.mesa
Copyright Ó 1987, 1991, 1992 by Xerox Corporation. All rights reserved.
Ken Pier, June 26, 1991 11:23 am PDT
Russ Atkinson (RRA) March 9, 1987 5:34:20 pm PST
Bloomenthal, March 6, 1989 9:05:35 am PST
Willie-s, June 5, 1992 12:48 pm PDT
DIRECTORY Atom, Basics, Buttons, ChoiceButtons, Commander, Containers, FileNames, FS, IconEditorDefs, IconRegistry, Icons, Imager, ImagerBackdoor, ImagerColor, ImagerPath, ImagerPixel, ImagerSample, IO, ScreenBoundingBox, MBQueue, Menus, MessageWindow, ProcessProps, Real, Rope, Rules, RuntimeError, TIPUser, ViewerClasses, ViewerOps, ViewerPrivate, ViewerTools;
IconEditorImplB: CEDAR PROGRAM
IMPORTS Atom, Basics, ChoiceButtons, Commander, Containers, FileNames, FS, IconEditorDefs, IconRegistry, Icons, Imager, ImagerBackdoor, ImagerSample, IO, ScreenBoundingBox, MBQueue, MessageWindow, ProcessProps, Real, Rope, Rules, RuntimeError, TIPUser, ViewerOps, ViewerPrivate, ViewerTools
EXPORTS IconEditorDefs = {
foreground: ImagerColor.ConstantColor ¬ Imager.black;
background: ImagerColor.ConstantColor ¬ Imager.white;
iconEditorIcon: Icons.IconFlavor = Icons.NewIconFromFile["IconEditor.icons", 0];
iconEditorTipTable: TIPUser.TIPTable = TIPUser.InstantiateNewTIPTable["IconEditor.tip"];
installationDirectory: Rope.ROPE = FileNames.CurrentWorkingDirectory[];
IconHandle: TYPE ~ IconEditorDefs.IconHandle;
StartIconEditor: Commander.CommandProc = {
ENABLE UNWIND => NULL;
tool: 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.Concat["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;
tool.workingDir ¬ FileNames.CurrentWorkingDirectory[];
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];
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];
};
};
GetFileName: PUBLIC PROC [h: IconHandle, v: ViewerClasses.Viewer] RETURNS [n: Rope.ROPE]~{
Inner: PROC ~ {n ¬ FileNames.ResolveRelativePath[ViewerTools.GetContents[v]]};
ProcessProps.AddPropList[Atom.PutPropOnList[NIL, $WorkingDirectory, h.workingDir], Inner];
};
IconFetchProc: Menus.ClickProc = {
handle: IconHandle ¬ NARROW[clientData];
fetchFile: IO.STREAM;
numIconsInFile: INT16;
badNews: BOOLEAN ¬ FALSE;
iconInfo: IconEditorDefs.IconInfoRef ¬ NEW[IconEditorDefs.IconInfoRec ¬ [handle, icon, handle.currentIC]];
iconNumberRope: Rope.ROPE ¬ ViewerTools.GetContents[handle.iconNumberWindow];
iconNumber: INT16;
iconFetchFileName: Rope.ROPE ¬ GetFileName[handle, handle.iconFetchFileWindow];
h: IO.STREAM ¬ IO.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[]/BYTES[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*BYTES[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.Concat["Icon could not be fetched from ", iconFetchFileName], clearFirst: TRUE];
}
};
FillIcon: PUBLIC PROC [handle: IconHandle] = TRUSTED {
allows any screen bit map to be converted to icon format
r: IconEditorDefs.RectangleRec ¬ handle.currentRectangle;
ir: IconEditorDefs.IconRef ¬ GetIconBitmap[];
IF r.w # 0
THEN { -- overwrite the selected rectangle of the current icon with screen bit map:
iconInfo: IconEditorDefs.IconInfoRef ¬ NEW[IconEditorDefs.IconInfoRec ¬ [handle, icon, handle.currentIC]];
IconEditorDefs.SaveBitMap[handle];
ViewerOps.SetNewVersion[handle.container];
FOR y: NAT IN [0..r.h) DO
FOR x: NAT IN [0..r.w) DO
handle.currentIconRep.bits[r.y+y][r.x+x] ¬ ir.bits[y][x];
ENDLOOP;
ENDLOOP;
handle.currentRectangle.w ¬ handle.currentRectangle.h ¬ 0;
IconEditorDefs.FreshMarks[handle];
ViewerOps.PaintViewer[viewer: handle.viewer, hint: client, whatChanged: iconInfo,
clearClient: FALSE];
}
ELSE { -- create a completely new icon containing the selected screen bit map:
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];
handle.icons[LOOPHOLE[handle.currentIC]] ¬ ir;
before we paint the screen, make sure new icon is visible (unless a rectangle is given)
IF LOOPHOLE[handle.currentIC, INT16] >= 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 {
Inner: PROC [pixelMap: Imager.PixelMap] = TRUSTED {
transfer screen pixelMap into icon formatted storage
bitmap: ImagerSample.SampleMap ~ ImagerSample.NewSampleMap[[max:[IconEditorDefs.iconH, IconEditorDefs.iconW]]];
bitmapContext: Imager.Context ~ ImagerBackdoor.BitmapContext[bitmap];
Imager.DrawPixels[context: bitmapContext, pixelMap: pixelMap, colorOperator: ImagerBackdoor.GetBufferColorOperator[ctx], referencePoint: [pixelMap.box.min.s, pixelMap.box.min.f], scanMode: [down,right], position: [0.0, IconEditorDefs.iconH] ];
FOR i: INT16 IN [0..IconEditorDefs.iconH) DO
FOR j: INT16 IN [0..IconEditorDefs.iconW) DO
ir.bits[i].b[j] ¬ IF bitmap.Get[[i,j]]=0 THEN FALSE ELSE TRUE;
ENDLOOP;
ENDLOOP;
};
x,y,w,h: NAT;
ctx: Imager.Context ¬ ViewerPrivate.CreateContext[main];
ir ¬ NEW[IconEditorDefs.IconFileFormat]; --get new storage for bit array, etc.
[x,y,w,h] ¬ ScreenBoundingBox.GetArea[];
TRUSTED {ImagerBackdoor.AccessBufferRectangle[ctx, Inner, [x,y,w,h] ]};
};
UndoProc: PUBLIC Menus.MenuProc = {
handle: 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];
};
KillAllIconsProc: PUBLIC Menus.MenuProc = {
handle: IconHandle ¬ NARROW[clientData];
iconInfo: IconEditorDefs.IconInfoRef ¬ NEW[IconEditorDefs.IconInfoRec ¬ [handle, screen, handle.currentIC]];
IF ~MessageWindow.Confirm["Confirm deletion of ALL icons..."] THEN RETURN;
WHILE handle.numberOfIcons > 1 DO
DeleteIcon[handle];
ENDLOOP;
ViewerOps.SetNewVersion[handle.container];
ViewerOps.PaintViewer[viewer: handle.viewer, hint: all, whatChanged: iconInfo, clearClient: FALSE];
};
DeleteIconProc: PUBLIC Menus.MenuProc = {
handle: 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: 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;
bounds: Imager.Box;
boundRect: Imager.Rectangle;
iconInfo: IconEditorDefs.IconInfoRef;
iconContext: Imager.Context ¬ context;
boundRect ¬ ImagerBackdoor.GetBounds[iconContext
! Imager.Error => {
boundRect ¬ [0.0, 0.0, 72*8.5, 72*11.0]; -- krock
CONTINUE;
};
];
bounds ¬ [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: 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: 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: IconHandle, ic: IconEditorDefs.IconFlavor]
RETURNS [INT16] = {
i: INT16 ¬ LOOPHOLE[ic];
realFlavor: INT16 ¬ (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: IconHandle, ic: IconEditorDefs.IconFlavor]
RETURNS [INT16] = {
i: INT16 ¬ LOOPHOLE[ic];
realFlavor: INT16 ¬ (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: IconHandle, x, y: REAL]
RETURNS [ignore: BOOLEAN, ic: IconEditorDefs.IconFlavor] = {
xC, yC: INT16;
possibleIC, theIC: INT16;
iconsOnDisplay: INT16 ¬ MIN[handle.numberOfIcons, IconEditorDefs.maxIconsOnDisplay];
ignore ¬ FALSE;
xC ¬ Real.Fix[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.Fix[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: IconHandle, x, y: REAL]
RETURNS [i, j: INT16] = {
IF x > handle.boardSize
THEN i ¬ IconEditorDefs.intWBound
ELSE i ¬ Real.Fix[x/handle.unit];
j ¬ MAX[IconEditorDefs.intHBound-INTEGER[Real.Fix[y/handle.unit]], 0];
};
DeleteIcon: PROC [handle: IconHandle] = {
deletes the current icon and then compacts the array of icons.
IF LOOPHOLE[handle.currentIC, INT16] < handle.numberOfIcons - 1 THEN {
the icon we wish to delete is in the middle of the array of icons; Compact.
FOR i: INT16 IN [LOOPHOLE[handle.currentIC, INT16] .. 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: IconHandle, flavor: IconEditorDefs.IconFlavor] = {
handle.icons[LOOPHOLE[flavor]].bits ¬ handle.savedBitMap;
};
DrawIcons: PROC [handle: IconHandle, context: Imager.Context] = {
FOR i: INT16 IN [0..MIN[handle.numberOfIcons, IconEditorDefs.maxIconsOnDisplay]) DO
DrawIcon[handle, context, (handle.startDisplay + i) MOD handle.numberOfIcons];
ENDLOOP;
};
DrawBit: PROC [handle: IconHandle, context: Imager.Context, x, y: INT16] = {
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: INT16] = {
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: IconHandle, context: Imager.Context, index: INT16] = {
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 POINTER ¬ LOOPHOLE[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: IconHandle, context: Imager.Context] = {
DrawBoardProc: PROC = {
bs: INTEGER ¬ Real.Fix[handle.boardSize];
Imager.SetColor[context, background];
Imager.MaskRectangleI[context: context, x: 0, y: 0, w: bs, h: bs];
Imager.SetColor[context, foreground];
FOR i: INT16 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: IconHandle, context: Imager.Context] = {
DrawCIProc: PROC = {
Imager.SetColor[context, ImagerBackdoor.invert];
FOR i: INT16 IN IconEditorDefs.IntH DO
FOR j: INT16 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: IconHandle, context: Imager.Context,
rect: IconEditorDefs.RectangleRec] = {
Draws a rectangle on the drawing board
rX0, rY0, rX1, rY1: INT16;
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: IconHandle, context: Imager.Context] = {
draws a thin border around newly created icons
x0, y0, x1, y1: INT16;
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: 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: 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: INT16;
[xC, yC] ¬ BoardCoords[handle, x, y];
RETURN [handle.currentIconRep.bits[yC].b[xC]];
};
AddBit: PROC [handle: IconHandle, context: Imager.Context, x, y: REAL] = {
xC, yC: INT16;
[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: Imager.Context, x, y: REAL] = {
xC, yC: INT16;
[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: 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 Basics.IsBound[LOOPHOLE[IconRegistry.IsRegistered]] THEN
ViewerTools.SetContents[handle.iconNameWindow,
IconRegistry.IsRegistered[
fileName: GetFileName[handle, handle.iconFileWindow],
index: LOOPHOLE[handle.currentIC, INT16]].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;
};
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"];
}.