<> <<>> <> <> <> DIRECTORY Environment USING [bytesPerWord], FileIO USING [Open], Graphics USING [Box], IconEditorDefs, IconRegistry USING [IsRegistered, InvalidateCache], IO, MessageWindow USING [Append, Blink], Real USING [FixC, FixI], Rope USING [ROPE], Runtime USING [IsBound]; IconEditorIO: CEDAR PROGRAM IMPORTS FileIO, IO, IconRegistry, MessageWindow, Real, Runtime EXPORTS IconEditorDefs = BEGIN OPEN IconEditorDefs; CouldntLoadIcons: PUBLIC SIGNAL = CODE; CouldntSaveIcons: PUBLIC SIGNAL = CODE; ViewerNoGood: PUBLIC SIGNAL = CODE; PointsMarked: PUBLIC PROC [handle: IconHandle,representation: MarkType] RETURNS [BOOLEAN] = { OPEN MessageWindow; -- for Append and Blink IF (handle.mark1.x = 0) AND (handle.mark1.y = 0) AND (handle.mark2.x = 0) AND (handle.mark2.y = 0) THEN { SELECT representation FROM rect=>Append["Hold down yellow mouse button and mark a rectangular area first...",TRUE]; line=>Append["Hold down yellow mouse button and control key and mark a line first...", TRUE]; ENDCASE => ERROR; Blink[]; RETURN[FALSE] } ELSE RETURN[TRUE]; }; SaveBitMap: PUBLIC PROCEDURE [handle: IconHandle] = { handle.savedBitMap _ handle.icons[LOOPHOLE[handle.currentIC]].bits; }; LayoutBoard: PUBLIC PROC [handle: IconHandle, bounds: Graphics.Box] = { iconsOnDisplay: CARDINAL _ MIN[handle.numberOfIcons, maxIconsOnDisplay]; handle.numIconsPerRow _ Real.FixC[bounds.xmax/(iconW+distX)]; handle.numIconsPerCol _ Real.FixC[bounds.ymax/(iconH+distY)]; IF handle.numIconsPerRow*handle.numIconsPerCol < iconsOnDisplay THEN { MessageWindow.Append["Icon Editor viewer is too small to display current Icon menu.", TRUE]; SIGNAL ViewerNoGood}; handle.possibleX _ Real.FixC[bounds.xmax - ((iconsOnDisplay + handle.numIconsPerCol - 1) / handle.numIconsPerCol) * (iconW + distX)]; handle.possibleY _ Real.FixC[bounds.ymax - ((iconsOnDisplay + handle.numIconsPerRow-1) / handle.numIconsPerRow) * (iconH+distY)]; IF handle.possibleX < minimumUnit * iconW AND handle.possibleY < minimumUnit * iconH THEN { MessageWindow.Append["Icon Editor viewer is too small for a decent drawing board.", TRUE]; SIGNAL ViewerNoGood}; IF handle.possibleX <= handle.possibleY THEN { -- use a vertical layout, preferred if square handle.boardSize _ MIN[Real.FixC[bounds.xmax], handle.possibleY]; handle.numIconsPerCol _ (iconsOnDisplay + handle.numIconsPerRow - 1) / handle.numIconsPerRow; handle.icX _ 0; handle.icY _ Real.FixC[handle.boardSize] + 1} ELSE { -- use a horizontal layout handle.boardSize _ MIN[Real.FixC[bounds.ymax], handle.possibleX]; handle.numIconsPerRow _ (iconsOnDisplay + handle.numIconsPerCol - 1) / handle.numIconsPerCol; handle.icX _ Real.FixC[handle.boardSize] + 1; handle.icY _ 0}; handle.unit _ Real.FixI[handle.boardSize / (intHBound + 1)]; handle.boardSize _ handle.unit * (intHBound + 1); }; GetNewFlavor: PUBLIC PROC [handle: IconHandle] RETURNS [newFlavor: iconFlavor] = BEGIN newFlavor _ handle.nextFlavor; handle.nextFlavor _ SUCC[newFlavor]; IF LOOPHOLE[handle.nextFlavor, CARDINAL] >= maxStorage THEN { MessageWindow.Append[message: "Sorry but you have exceeded the current limit on icons.", clearFirst: TRUE]; MessageWindow.Blink[]; ERROR}; END; LoadIcons: PUBLIC PROCEDURE[handle: IconHandle, filename: Rope.ROPE] RETURNS [nRead: INTEGER, iconFile: IO.Handle] = TRUSTED { ENABLE IO.Error => GOTO Punt; newFlavor: iconFlavor; handle.nextFlavor _ FIRST[iconFlavor]; -- Initialize this since our icons start w/ the 1st flavor iconFile _ FileIO.Open[fileName: filename]; nRead _ iconFile.GetLength[] / (Environment.bytesPerWord*SIZE[iconFileFormat]); FOR n: INTEGER IN [0..nRead) DO newFlavor _ GetNewFlavor[handle]; IF handle.icons[LOOPHOLE[newFlavor]] = NIL THEN handle.icons[LOOPHOLE[newFlavor]] _ NEW[iconFileFormat]; [] _ IO.UnsafeGetBlock[iconFile, [LOOPHOLE[handle.icons[LOOPHOLE[newFlavor]]], 0, SIZE[iconFileFormat]*2]]; ENDLOOP; IO.Close[iconFile]; -- now close things up EXITS Punt => SIGNAL CouldntLoadIcons; }; SaveIcons: PUBLIC PROC [handle: IconHandle, filename: Rope.ROPE, numberOfIcons: CARDINAL] RETURNS [iconFile: IO.Handle] = { iconFile _ FileIO.Open[fileName: filename, accessOptions: overwrite]; FOR j: CARDINAL IN [0..numberOfIcons) DO iconName: Rope.ROPE; IO.UnsafePutBlock[iconFile, [LOOPHOLE[handle.icons[LOOPHOLE[j]]], 0, SIZE[iconFileFormat]*2]]; TRUSTED {IF Runtime.IsBound[IconRegistry.IsRegistered] AND (iconName _ IconRegistry.IsRegistered[fileName: filename, index: j].name) # NIL THEN IconRegistry.InvalidateCache[iconName]}; -- w.t. ENDLOOP; IO.Flush[iconFile]; -- make sure we got everything IO.Close[iconFile]; -- now close things up }; NormalizeRectangle: PUBLIC PROC [corner1, corner2: markPoint] RETURNS [rect: rectangleRec] = { << takes two diagonal points and computes the bottom left corner of the rectangle and its width and height>> rect.x _ MIN[corner1.x, corner2.x]; rect.y _ MIN[corner1.y, corner2.y]; rect.w _ ABS[corner2.x-corner1.x] + 1; rect.h _ ABS[corner2.y-corner1.y] + 1; }; TransferEndPoints: PUBLIC PROC [endpoint1, endpoint2: markPoint] RETURNS [line: lineRec] = { line.x1 _ endpoint1.x; line.y1 _ endpoint1.y; line.x2 _ endpoint2.x; line.y2 _ endpoint2.y; }; ComputeEndPoints: PUBLIC PROC [handle: IconHandle, oldLine: lineRec] RETURNS [newLine: lineRec] = { center: INTEGER; center _ Real.FixI[handle.unit/2]; -- used to center endpoints of line in grid square newLine.x1 _ oldLine.x1*handle.unit; newLine.y1 _ (intHBound- oldLine.y1)*handle.unit; newLine.x2 _ oldLine.x2*handle.unit; newLine.y2 _ (intHBound- oldLine.y2)*handle.unit; << Now add one unit to the rightmost x and to the upper y so that an accurate representation of the line is drawn.>> IF newLine.x1 > newLine.x2 THEN { newLine.x1 _ newLine.x1 + handle.unit - center; newLine.x2 _ newLine.x2 + center}; IF newLine.x2 > newLine.x1 THEN { newLine.x2 _ newLine.x2 + handle.unit - center; newLine.x1 _ newLine.x1 + center}; IF newLine.x1 = newLine.x2 THEN { newLine.x1 _ newLine.x2 _ newLine.x2 + center}; IF newLine.y1 > newLine.y2 THEN { newLine.y1 _ newLine.y1 + handle.unit - center; newLine.y2 _ newLine.y2 + center}; IF newLine.y2 > newLine.y1 THEN { newLine.y2 _ newLine.y2 + handle.unit - center; newLine.y1 _ newLine.y1 + center}; IF newLine.y1 = newLine.y2 THEN { newLine.y1 _ newLine.y2 _ newLine.y2 + center}; }; ClearIcon: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor] = { i,j: CARDINAL; iconToClear: iconRef _ handle.icons[LOOPHOLE[flavor]]; FOR i IN intW DO FOR j IN intH DO iconToClear.bits[j].b[i] _ FALSE; ENDLOOP; ENDLOOP; }; MirrorIcon: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor, rect: rectangleRec] = { << Reflects icon accross imaginary x-axis. Achieves the effect of holding the icon in a mirror>> i,j: INTEGER; temp: BOOLEAN; farRight: CARDINAL _ rect.x+rect.w-1; FOR j IN [rect.y..rect.y+rect.h-1] DO FOR i IN [0..rect.w/2) DO temp _ handle.icons[LOOPHOLE[flavor]].bits[j].b[rect.x + i]; handle.icons[LOOPHOLE[flavor]].bits[j].b[rect.x + i] _ handle.icons[LOOPHOLE[flavor]].bits[j].b[farRight - i]; handle.icons[LOOPHOLE[flavor]].bits[j].b[farRight - i] _ temp; ENDLOOP; ENDLOOP; }; DrawLine: PUBLIC PROC [handle: IconHandle, line: lineRec, flavor: iconFlavor]= { deltaX, deltaY, i, e, x, y, factor: INTEGER; deltaX _ ABS[line.x2 - line.x1]; deltaY _ ABS[line.y2 - line.y1]; IF ((line.x1= deltaY THEN { e _ 2*deltaY - deltaX; FOR i IN [0..deltaX] DO handle.icons[LOOPHOLE[flavor]].bits[y].b[x] _ TRUE; IF e > 0 THEN { IF (y >= 0) AND (y < intHBound) THEN y _ y + factor; -- Just to be safe e _ e + (2*deltaY - 2*deltaX) } ELSE e _ e + 2*deltaY; IF x < intWBound THEN x _ x + 1; -- Just to be safe ENDLOOP; } ELSE { e _ 2*deltaX - deltaY; FOR i IN [0..deltaY] DO handle.icons[LOOPHOLE[flavor]].bits[y].b[x] _ TRUE; IF e > 0 THEN { IF x < intWBound THEN x _ x + 1; -- Just to be safe e _ e + (2*deltaX - 2*deltaY) } ELSE e _ e + 2*deltaX; IF (y >= 0) AND (y < intHBound) THEN y _ y + factor; -- Just to be safe ENDLOOP; }; FreshMarks[handle]; }; FreshMarks: PUBLIC PROC [handle: IconHandle] = { handle.mark1.x _ handle.mark1.y _ handle.mark2.x _ handle.mark2.y _ 0; handle.functionNotApplied _ FALSE; }; WhiteLabel: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor] = { handle.icons[LOOPHOLE[flavor]].invertLabel _ TRUE; }; BlackLabel: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor] = { handle.icons[LOOPHOLE[flavor]].invertLabel _ FALSE; }; SetLabel: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor, rect: rectangleRec]= { <> OPEN i: handle.icons[LOOPHOLE[flavor]]; i.label _ TRUE; handle.labelRect.x _ i.lx _ rect.x; i.ly _ iconH - 1 - (rect.y + rect.h); handle.labelRect.y _ rect.y; handle.labelRect.w _ i.lw _ rect.w; handle.labelRect.h _ i.lh _ rect.h; FreshMarks[handle]; }; UnSetLabel: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor]= { <> OPEN i: handle.icons[LOOPHOLE[flavor]]; i.label _ FALSE; i.lx _ 0; i.ly _ 0; i.lw _ 0; i.lh _ 0; }; SetWhite: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor, rect: rectangleRec] = { i, j: INTEGER; iconToClear: iconRef _ handle.icons[LOOPHOLE[flavor]]; FOR i IN [rect.x..rect.x+rect.w-1] DO FOR j IN [rect.y..rect.y+rect.h-1] DO iconToClear.bits[j].b[i] _ FALSE; ENDLOOP; ENDLOOP; FreshMarks[handle]; }; SetDeskTopGray: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor, rect: rectangleRec] = { <> i, j, k: INTEGER; indent: INTEGER _ 0; FOR j _ 0, j+2 WHILE j < LOOPHOLE[IconEditorDefs.intHBound] DO -- each gray line is two lines high FOR k IN [0..1] DO -- paint gray FOR i _ indent, i+4 WHILE i <= LOOPHOLE[IconEditorDefs.intWBound] DO IF j IN [rect.y .. rect.y+rect.h) AND i IN [rect.x ..rect.x+rect.w) THEN handle.icons[LOOPHOLE[flavor]].bits[j+k].b[i] _ TRUE; ENDLOOP; ENDLOOP; -- next time do a differnt indentation indent _ IF indent=0 THEN 2 ELSE 0; ENDLOOP; FreshMarks[handle]; }; SetDarkGray: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor, rect: rectangleRec] = { << set dark gray pattern in specified icon>> i, j: INTEGER; indent: INTEGER _ 0; FOR j IN [rect.y..rect.y+rect.h-1) DO -- paint gray FOR i _ (rect.x+indent), i+2 WHILE i < (rect.x+rect.w-1) DO handle.icons[LOOPHOLE[flavor]].bits[j].b[i] _ TRUE; ENDLOOP; -- next time do a differnt indentation indent _ IF indent=0 THEN 1 ELSE 0; ENDLOOP; FreshMarks[handle]; }; SetBlack: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor, rect: rectangleRec] = { << fills in a rectangle with black>> i, j: INTEGER; FOR i IN [rect.x..rect.x+rect.w-1] DO FOR j IN [rect.y..rect.y+rect.h-1] DO handle.icons[LOOPHOLE[flavor]].bits[j].b[i] _ TRUE; ENDLOOP; ENDLOOP; FreshMarks[handle]; }; Invert: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor, rect: rectangleRec] = { i,j: INTEGER; FOR i IN [rect.x..rect.x+rect.w-1] DO FOR j IN [rect.y..rect.y+rect.h-1] DO handle.icons[LOOPHOLE[flavor]].bits[j].b[i] _ IF handle.icons[LOOPHOLE[flavor]].bits[j].b[i] THEN FALSE ELSE TRUE; ENDLOOP; ENDLOOP; FreshMarks[handle]; }; Rectangle: TYPE = RECORD[xmin, ymin, xmax, ymax: NAT]; GetRectangle: PROC[handle: IconHandle] RETURNS[Rectangle] = { x1, y1, x2, y2: NAT; x1 _ handle.mark1.x; y1 _ handle.mark1.y; x2 _ handle.mark2.x; y2 _ handle.mark2.y; IF x1=0 AND y1=0 AND x2=0 AND y2=0 THEN { x2 _ intWBound; y2 _ intHBound }; RETURN[[xmin: MIN[x1, x2], ymin: MIN[y1, y2], xmax: MAX[x1, x2], ymax: MAX[y1, y2]]]; }; MoveRight: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor, slowShift: BOOLEAN] = { numberToMove: CARDINAL _ IF slowShift THEN 1 ELSE 4; r: Rectangle = GetRectangle[handle]; FOR k: CARDINAL IN [1..numberToMove] DO oldBits: bitArray _ handle.icons[LOOPHOLE[flavor]].bits; FOR i: NAT IN [r.xmin..r.xmax] DO FOR j: NAT IN [r.ymin..r.ymax] DO handle.icons[LOOPHOLE[flavor]].bits[j].b[i] _ oldBits[j].b[IF i=r.xmin THEN r.xmax ELSE i-1]; ENDLOOP; ENDLOOP; ENDLOOP; }; MoveLeft: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor, slowShift: BOOLEAN] = { numberToMove: CARDINAL _ IF slowShift THEN 1 ELSE 4; r: Rectangle = GetRectangle[handle]; FOR k: CARDINAL IN [1..numberToMove] DO oldBits: bitArray _ handle.icons[LOOPHOLE[flavor]].bits; FOR i: NAT IN [r.xmin..r.xmax] DO FOR j: NAT IN [r.ymin..r.ymax] DO handle.icons[LOOPHOLE[flavor]].bits[j].b[i] _ oldBits[j].b[IF i=r.xmax THEN r.xmin ELSE i+1]; ENDLOOP; ENDLOOP; ENDLOOP; }; MoveUp: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor, slowShift: BOOLEAN] = { numberToMove: CARDINAL _ IF slowShift THEN 1 ELSE 4; r: Rectangle = GetRectangle[handle]; FOR k: CARDINAL IN [1..numberToMove] DO oldBits: bitArray _ handle.icons[LOOPHOLE[flavor]].bits; FOR i: NAT IN [r.xmin..r.xmax] DO FOR j: NAT IN [r.ymin..r.ymax] DO handle.icons[LOOPHOLE[flavor]].bits[j].b[i] _ oldBits[IF j=r.ymax THEN r.ymin ELSE j+1].b[i]; ENDLOOP; ENDLOOP; ENDLOOP; }; MoveDown: PUBLIC PROC [handle: IconHandle, flavor: iconFlavor, slowShift: BOOLEAN] = { numberToMove: CARDINAL _ IF slowShift THEN 1 ELSE 4; r: Rectangle = GetRectangle[handle]; FOR k: CARDINAL IN [1..numberToMove] DO oldBits: bitArray _ handle.icons[LOOPHOLE[flavor]].bits; FOR i: NAT IN [r.xmin..r.xmax] DO FOR j: NAT IN [r.ymin..r.ymax] DO handle.icons[LOOPHOLE[flavor]].bits[j].b[i] _ oldBits[IF j=r.ymin THEN r.ymax ELSE j-1].b[i]; ENDLOOP; ENDLOOP; ENDLOOP; }; END. <<>> <> <> <> <<>> <> <> <<>> <> <> <<>> <<>>