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: BOOL ← FALSE];
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"];
}.