IconEditorIO.mesa
Last edit by Michael Plass, January 31, 1983 2:51 pm
Last Edited by: Teitelman, April 12, 1983 5:01 pm
Last Edited by: Doug Wyatt, April 22, 1983 4:37 pm
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: CARDINALMIN[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<line.x2) AND (line.y2<line.y1)) OR ((line.x2<line.x1) AND (line.y1<line.y2)) THEN {
factor ← -1;
y ← MAX[line.y1, line.y2] }
ELSE {
factor ← 1;
y ← MIN[line.y1, line.y2] };
x ← MIN[line.x1, line.x2];
IF deltaX >= 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]= {
sets the proper parameters in the file representation
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]= {
undoes the proper parameters in the file representation
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] = {
set gray pattern in specified icon
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: CARDINALIF 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: CARDINALIF 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: CARDINALIF 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: CARDINALIF 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.
Edited on April 11, 1983 3:20 pm, by Teitelman
fixed SaveIcons to invalidate cache of IconRegistry for any icons in the file that is being saved.
changes to: SaveIcons, DIRECTORY, IMPORTS
Edited on April 12, 1983 4:59 pm, by Teitelman
changes to: SaveIcons, DIRECTORY, IMPORTS
Edited on April 22, 1983 4:35 pm, by Doug Wyatt
Added GetRectangle; the shifting operations now use GetRectangle, so they are insensitive to which corners were used to select the rectangle.