<> <> 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: BOOLEAN _ FALSE; 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.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[]/(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 { <> <> fetchFile.SetIndex[iconNumber*(Basics.bytesPerWord*SIZE[IconEditorDefs.IconFileFormat])]; <> 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 { <> 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]] _ GetIconBitmap[]; <> 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]]; <> 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 = { <> 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 { <> 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 { <> 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 { <> 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 { <> CleanUp[iconInfo.handle, iconContext]; <> 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 { <> 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]; <> <> 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] = { <> IF LOOPHOLE[handle.currentIC, IconEditorDefs.Nat] < handle.numberOfIcons - 1 THEN { <> 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 { <> handle.icons[LOOPHOLE[handle.currentIC]] _ NIL; <> <> 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] = { <> 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] = { <> 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: 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] = { <> 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] = { <> 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] = { <> 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 <> 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"]; }.