-- ALELinesB.mesa
-- Edited by Sweet, 20-Jan-81 10:36:11
DIRECTORY
ALEOps,
Inline,
Storage,
Table,
UserTerminal,
Window;
ALELinesB: PROGRAM
IMPORTS ALEOps, Inline, Storage, Table, UserTerminal, Window
EXPORTS ALEOps =
BEGIN OPEN ALEOps;
ptb, ltb, lbb: Table.Base;
ALELinesBNotify: PUBLIC Table.Notifier =
BEGIN
ptb ← base[ptType];
ltb ← base[ltType];
lbb ← base[lbType];
END;
AddHoriz: PROCEDURE [l: LTIndex] =
BEGIN
x1: ADistance = ptb[ltb[l].p1].pos.x;
x2: ADistance = ptb[ltb[l].p2].pos.x;
y: ADistance = ptb[ltb[l].p1].pos.y;
hi: INTEGER = FindHList[y, TRUE];
prev: LTIndex ← LTNull;
m: LTIndex ← hArray[hi].lines;
WHILE m # LTNull AND ptb[ltb[m].p1].pos.x < x1 DO
prev ← m; m ← ltb[m].thread;
ENDLOOP;
IF prev = LTNull THEN {hArray[hi].lines ← l; hArray[hi].l.min ← x1}
ELSE ltb[prev].thread ← l;
ltb[l].thread ← m;
hArray[hi].l.max ← MAX[hArray[hi].l.max, x2];
hArray[hi].maxW ← MAX[hArray[hi].maxW, ltb[l].width];
END;
AddVert: PROCEDURE [l: LTIndex] =
BEGIN
y1: ADistance = ptb[ltb[l].p1].pos.y;
y2: ADistance = ptb[ltb[l].p2].pos.y;
x: ADistance = ptb[ltb[l].p1].pos.x;
vi: INTEGER = FindVList[x, TRUE];
prev: LTIndex ← LTNull;
m: LTIndex ← vArray[vi].lines;
WHILE m # LTNull AND ptb[ltb[m].p1].pos.y < y1 DO
prev ← m; m ← ltb[m].thread;
ENDLOOP;
IF prev = LTNull THEN {vArray[vi].lines ← l; vArray[vi].l.min ← y1}
ELSE ltb[prev].thread ← l;
ltb[l].thread ← m;
vArray[vi].l.max ← MAX[vArray[vi].l.max, y2];
vArray[vi].maxW ← MAX[vArray[vi].maxW, ltb[l].width];
END;
ShouldLengthen: PUBLIC PROC [l: LTIndex] RETURNS [extra: [0..4]] =
BEGIN
CheckThis: LineScan =
BEGIN
IF lth.class = vert AND lth.p2 = endP THEN
extra ← MAX[extra, lth.width];
RETURN[FALSE];
END;
endP: PTIndex = ltb[l].p2;
extra ← 0;
IF ltb[l].class = horiz THEN [] ← LinesThru[endP, CheckThis];
END;
MaybeDisplayLine: PUBLIC PROC [l: LTIndex] =
BEGIN
lBox: Window.Box ← BoxForLine[l];
vBox: Window.Box ← [
[x: -pictureWindow.box.place.x, y: -pictureWindow.box.place.y],
FrameBox.dims];
IF ~Disjoint[@lBox, @vBox] THEN DisplayLine[l, lBox]; -- conservative
END;
RedrawSelections: PUBLIC PROC =
BEGIN
CheckLine: LineScan =
BEGIN
IF lth.width # state.currentWidth OR lth.texture # state.currentTexture THEN
BEGIN
Window.InvalidateBox[pictureWindow, BoxForLine[l]];
lth.width ← state.currentWidth;
lth.texture ← state.currentTexture;
END;
RETURN[FALSE];
END;
CheckLabel: LabelScan =
BEGIN
IF lbh.font # state.currentFont THEN
BEGIN
Window.InvalidateBox[pictureWindow, BoxForLabel[lb]];
lbh.font ← state.currentFont;
END;
RETURN[FALSE];
END;
[] ← SelectedLines[CheckLine];
[] ← SelectedLabels[CheckLabel];
END;
DrawLine: PUBLIC PROC [pos1, pos2: APosition, selected: BOOLEAN ← TRUE] =
BEGIN
l: LTIndex;
IF pos1 = pos2 THEN RETURN;
IF selected THEN ClearSelections[];
l ← InsertLine[
InsertPoint[pos1],
InsertPoint[pos2],
state.currentWidth,
state.currentTexture];
ltb[l].selected ← selected;
IF selected THEN header.selectedLines ← l;
MaybeDisplayLine[l];
END;
InsertLine: PROC [p1, p2: PTIndex, width: LineWidth, texture: LineTexture]
RETURNS [newL: LTIndex] =
BEGIN
pos1: APosition;
pos2: APosition;
pTemp: PTIndex;
FindP2: LineScan =
BEGIN
p: PTIndex = IF ltb[l].p1 = p1 THEN ltb[l].p2 ELSE ltb[l].p1;
RETURN [p = p2];
END;
newL ← LinesThru[p1, FindP2];
IF newL # LTNull THEN
BEGIN
Window.InvalidateBox[pictureWindow, BoxForLine[newL]];
ltb[newL].width ← width;
ltb[newL].texture ← texture;
ltb[newL].selected ← TRUE;
RETURN
END;
pos1 ← ptb[p1].pos;
pos2 ← ptb[p2].pos;
SELECT pos1.x FROM
< pos2.x => NULL;
> pos2.x => {pTemp ← p1; p1 ← p2; p2 ← pTemp};
ENDCASE => IF pos1.y > pos2.y THEN
{pTemp ← p1; p1 ← p2; p2 ← pTemp};
newL ← AllocateLine[];
ltb[newL] ← [
p1: p1, p1Chain: ptb[p1].lines,
p2: p2, p2Chain: ptb[p2].lines,
width: width, texture: texture, class: NULL];
ptb[p1].lines ← newL;
ptb[p2].lines ← newL;
ltb[newL].class ← SELECT TRUE FROM
pos1.x = pos2.x => vert,
pos1.y = pos2.y => horiz,
ABS[pos1.x-pos2.x] <= ABS[pos1.y-pos2.y] => steep,
ENDCASE => shallow;
SELECT ltb[newL].class FROM
horiz => AddHoriz[newL];
vert => AddVert[newL];
ENDCASE =>
{ltb[newL].thread ← header.diagLines; header.diagLines ← newL};
END;
AllocateLine: PROC RETURNS [l: LTIndex] =
BEGIN
IF (l ← header.freeLine) # LTNull THEN
BEGIN
header.freeLine ← ltb[LOOPHOLE[header.freeLine, FNIndex]].next;
RETURN
END;
l ← Table.Allocate[ltType, SIZE[Line]];
END;
InsertPoint: PROC [pos: APosition] RETURNS [newP: PTIndex] =
BEGIN
hi: INTEGER = FindHList[pos.y, TRUE];
follows: PTIndex ← PTNull;
next: PTIndex;
newP ← hArray[hi].points;
WHILE newP # PTNull DO
SELECT ptb[newP].pos.x FROM
< pos.x => NULL;
> pos.x => EXIT;
ENDCASE => RETURN;
follows ← newP;
newP ← ptb[follows].thread;
ENDLOOP;
next ← IF follows = PTNull THEN hArray[hi].points
ELSE ptb[follows].thread;
newP ← AllocatePoint[];
ptb[newP] ← [pos: pos, thread: next];
IF follows = PTNull THEN hArray[hi].points ← newP
ELSE ptb[follows].thread ← newP;
hArray[hi].p.max ← MAX[hArray[hi].p.max, pos.x];
hArray[hi].p.min ← MIN[hArray[hi].p.min, pos.x];
END;
AllocatePoint: PROC RETURNS [p: PTIndex] =
BEGIN
IF (p ← header.freePoint) # PTNull THEN
BEGIN
header.freePoint ← ptb[LOOPHOLE[header.freePoint, FNIndex]].next;
RETURN
END;
p ← Table.Allocate[ptType, SIZE[Point]];
END;
DrawnWidth: ARRAY [-3..4] OF CARDINAL = [1, 1, 1, 1, 1, 2, 4, 4];
BoxForLine: PUBLIC PROC [l: LTIndex] RETURNS [box: Window.Box] =
BEGIN
pos1: APosition = ptb[ltb[l].p1].pos;
pos2: APosition = ptb[ltb[l].p2].pos;
x1: ADistance = MIN[pos1.x, pos2.x];
y1: ADistance = MIN[pos1.y, pos2.y];
width: CARDINAL = ltb[l].width * DrawnWidth[state.magnify];
w: CARDINAL ← MAX[DotsForADistance[ABS[pos1.x-pos2.x]], 1];
h: CARDINAL ← MAX[DotsForADistance[ABS[pos1.y-pos2.y]], 1];
IF ltb[l].class >= steep THEN w ← w + width
ELSE h ← h + width;
IF ltb[l].class = horiz THEN
w ← w + ShouldLengthen[l] * DrawnWidth[state.magnify];
RETURN [[PicturePlace[[x: x1, y: y1]], [w: w, h: h]]];
END;
DisplayLine: PUBLIC PROC [l: LTIndex, box: Window.Box] =
BEGIN
aBox: ABox ← ABoxForBox[box];
ChopUpLine[l, @aBox, DisplaySolidLine];
END;
Hypot: PUBLIC PROC [a,b: LONG INTEGER] RETURNS [LONG INTEGER] =
BEGIN
factor: LONG INTEGER ← 1;
c: LONG INTEGER;
a ← ABS[a]; b ← ABS[b];
WHILE a > LAST[INTEGER]/2 OR b > LAST[INTEGER]/2 DO
a ← a/2; b ← b/2; factor ← factor * 2;
ENDLOOP;
c ← Sqrt[a*a+b*b];
RETURN [c*factor];
END;
ChopUpLine: PUBLIC PROC [l: LTIndex, box: POINTER TO ABox, pproc: PROC [
pos1, pos2: APosition,
class: LineClass,
color: LineColor,
lWidth: LineWidth,
lengthen: [0..4],
box: POINTER TO ABox]] =
BEGIN
marksRemoved: BOOLEAN ← FALSE;
inch: ADistance = 16;
tex: LineTexture = ltb[l].texture;
dash: ADistance ← SELECT tex FROM
d2 => 2*inch,
d4 => 4*inch,
ENDCASE => 6*inch; -- solid will ignore this value
gap: ADistance ← inch;
class: LineClass = ltb[l].class;
width: LineWidth = ltb[l].width;
pos1: APosition ← ptb[ltb[l].p1].pos;
pos2: APosition ← ptb[ltb[l].p2].pos;
segstart: APosition;
solidColor: LineColor = IF ltb[l].selected THEN grey ELSE black;
length, ends: ADistance;
lengthen: [0..4] ← 0;
SELECT class FROM
horiz => {length ← pos2.x - pos1.x; lengthen ← ShouldLengthen[l]};
vert => length ← pos2.y - pos1.y;
ENDCASE =>
BEGIN
MarksOut[]; marksRemoved ← TRUE;
length ← Hypot[pos2.x - pos1.x, pos2.y - pos1.y];
END;
IF tex = solid OR length < dash+2*gap THEN
{pproc[pos1, pos2, class, solidColor, width, lengthen, box];
IF marksRemoved THEN MarksIn[];
RETURN};
SELECT class FROM
horiz =>
BEGIN
DoSeg: PROC [sl: ADistance, sc: LineColor, lengthen: [0..4] ← 0] =
BEGIN
segstop: APosition ← [x: segstart.x + sl, y: segstart.y];
pproc[
pos1: segstart,
pos2: segstop,
class: class,
color: sc,
lWidth: width,
lengthen: lengthen,
box: box];
segstart ← segstop;
END;
ends ← (dash + (length - dash - gap) MOD (dash+gap))/2;
segstart ← pos1;
DoSeg[ends, solidColor];
DoSeg[gap, white];
WHILE segstart.x + dash + gap < pos2.x DO
DoSeg[dash, solidColor];
DoSeg[gap, white];
ENDLOOP;
DoSeg[pos2.x - segstart.x, solidColor, lengthen];
END;
vert =>
BEGIN
DoSeg: PROC [sl: ADistance, sc: LineColor] =
BEGIN
segstop: APosition ← [x: segstart.x, y: segstart.y + sl];
pproc[
pos1: segstart,
pos2: segstop,
class: class,
color: sc,
lWidth: width,
lengthen: 0,
box: box];
segstart ← segstop;
END;
ends ← (dash + (length - dash - gap) MOD (dash+gap))/2;
segstart ← pos1;
DoSeg[ends, solidColor];
DoSeg[gap, white];
WHILE segstart.y + dash + gap < pos2.y DO
DoSeg[dash, solidColor];
DoSeg[gap, white];
ENDLOOP;
DoSeg[pos2.y - segstart.y, solidColor];
END;
steep =>
BEGIN
deltaY: ADistance = pos2.y - pos1.y;
deltaX: ADistance = pos2.x - pos1.x;
dir: INTEGER = IF deltaY < 0 THEN -1 ELSE 1;
G: PROC [y: ADistance] RETURNS [x: ADistance] =
BEGIN
x ← pos1.x + MulDiv[(y - pos1.y), deltaX, deltaY];
END;
DoSeg: PROC [sl: ADistance, sc: LineColor] =
BEGIN
segstop: APosition;
segstop.y ← segstart.y + sl*dir;
segstop.x ← G[segstop.y];
pproc[
pos1: segstart,
pos2: segstop,
class: class,
color: sc,
lWidth: width,
lengthen: 0,
box: box];
segstart ← segstop;
END;
dash ← MulDiv[dash, ABS[deltaY], length];
gap ← MulDiv[gap, ABS[deltaY], length];
gap ← MAX [gap, ADistanceForDots[1]];
ends ← (dash + (ABS[pos1.y-pos2.y] - dash - gap) MOD (dash+gap))/2;
segstart ← pos1;
DoSeg[ends, solidColor];
DoSeg[gap, white];
WHILE (segstart.y + dir*(dash + gap) - pos2.y)*dir < 0 DO
DoSeg[dash, solidColor];
DoSeg[gap, white];
ENDLOOP;
DoSeg[ABS[pos2.y - segstart.y], solidColor];
END;
ENDCASE => -- shallow
BEGIN
deltaY: ADistance = pos2.y - pos1.y;
deltaX: ADistance = pos2.x - pos1.x;
F: PROC [x: ADistance] RETURNS [y: ADistance] =
BEGIN
y ← pos1.y + MulDiv[(x - pos1.x), deltaY, deltaX];
END;
DoSeg: PROC [sl: ADistance, sc: LineColor] =
BEGIN
segstop: APosition;
segstop.x ← segstart.x + sl;
segstop.y ← F[segstop.x];
pproc[
pos1: segstart,
pos2: segstop,
class: class,
color: sc,
lWidth: width,
lengthen: 0,
box: box];
segstart ← segstop;
END;
dash ← MulDiv[dash, deltaX, length];
gap ← MulDiv[gap, deltaX, length];
gap ← MAX [gap, ADistanceForDots[1]];
ends ← (dash + (pos2.x - pos1.x - dash - gap) MOD (dash+gap))/2;
segstart ← pos1;
DoSeg[ends, solidColor];
DoSeg[gap, white];
WHILE segstart.x + dash + gap < pos2.x DO
DoSeg[dash, solidColor];
DoSeg[gap, white];
ENDLOOP;
DoSeg[pos2.x - segstart.x, solidColor];
END;
IF marksRemoved THEN MarksIn[];
END;
DisplaySolidLine: PROC [pos1, pos2: APosition, class: LineClass, color: LineColor, lWidth: LineWidth, lengthen: [0..4], box: POINTER TO ABox] =
BEGIN
deltaY, deltaX: LONG INTEGER;
start, stop, current, leftCorner, rightCorner: Window.Place;
width: CARDINAL = lWidth * DrawnWidth[state.magnify];
drawn: BOOLEAN ← FALSE;
chunkBox: Window.Box;
F: PROC [x: INTEGER] RETURNS [y: INTEGER] =
BEGIN
y ← start.y + Inline.LowHalf[ MulDiv[(x - start.x), deltaY, deltaX]]
END;
G: PROC [y: INTEGER] RETURNS [x: INTEGER] =
BEGIN
x ← start.x + Inline.LowHalf[ MulDiv[(y - start.y), deltaX, deltaY]]
END;
IF color = white THEN RETURN; -- try not painting white part
leftCorner ← PicturePlace[[box.x1, box.y1]];
rightCorner ← PicturePlace[[box.x2, box.y2]];
start ← PicturePlace[pos1];
stop ← PicturePlace[pos2];
SELECT class FROM
horiz =>
BEGIN
start.x ← MAX[start.x, leftCorner.x];
stop.x ← MIN[stop.x, rightCorner.x];
IF start.x < stop.x THEN Window.DisplayShade[
pictureWindow,
[start,
[w: stop.x - start.x + lengthen*DrawnWidth[state.magnify], h: width]],
GreyColor[color]];
RETURN
END;
vert =>
BEGIN
start.y ← MAX[start.y, leftCorner.y];
stop.y ← MIN[stop.y, rightCorner.y];
IF start.y < stop.y THEN Window.DisplayShade[
pictureWindow,
[start, [w: width, h: stop.y - start.y]],
GreyColor[color]];
RETURN
END;
ENDCASE;
deltaY ← stop.y - start.y;
deltaX ← stop.x - start.x;
IF class = steep THEN
BEGIN
negSlope: BOOLEAN ← FALSE;
limitY: INTEGER;
PaintChunk: PROC [Window.Handle] RETURNS [Window.Box, INTEGER] =
BEGIN
h: INTEGER;
DO
IF negSlope THEN {IF current.y <= limitY THEN EXIT}
ELSE IF current.y >= limitY THEN EXIT;
h ← MAX [ABS[current.y - F[current.x+1]], 1];
h ← MIN [ABS[limitY - current.y], h];
IF negSlope THEN
chunkBox ← [[current.x, current.y-h], [w: width, h: h]]
ELSE chunkBox ← [current, [w: width, h: h]];
current.x ← current.x + 1;
current.y ← current.y + (IF negSlope THEN -h ELSE h);
RETURN [chunkBox, 0];
ENDLOOP;
RETURN [Window.NullBox, 0];
END;
IF deltaY < 0 THEN
{limitY ← MAX[stop.y, leftCorner.y];
current.y ← MIN[start.y, rightCorner.y];
negSlope ← TRUE}
ELSE
{limitY ← MIN[stop.y, rightCorner.y];
current.y ← MAX[start.y, leftCorner.y]};
current.x ← G[current.y];
Window.Trajectory[
window: pictureWindow,
box: WindowBox[box],
proc: PaintChunk,
bbop: replace,
bbsource: gray, -- why can't they agree on spelling?
grey: GreyColor[color]];
END
ELSE -- shallow
BEGIN
negSlope: BOOLEAN ← FALSE;
dy: INTEGER;
limitX: INTEGER = MIN[stop.x, rightCorner.x];
PaintChunk: PROC [Window.Handle] RETURNS [Window.Box, INTEGER] =
BEGIN
w: INTEGER;
DO
IF current.x >= limitX THEN EXIT;
w ← MAX[G[current.y+dy] - current.x, 1];
w ← MIN[stop.x - current.x, w];
IF negSlope THEN
chunkBox ← [[current.x, current.y-1], [w: w, h: width]]
ELSE chunkBox ← [current, [w: w, h: width]];
current.x ← current.x + w;
current.y ← current.y + dy;
RETURN [chunkBox, 0];
ENDLOOP;
RETURN [Window.NullBox, 0];
END;
IF deltaY < 0 THEN {dy ← -1; negSlope ← TRUE} ELSE dy ← 1;
current.x ← MAX[start.x, leftCorner.x];
current.y ← F[current.x];
Window.Trajectory[
window: pictureWindow,
box: WindowBox[box],
proc: PaintChunk,
bbop: replace,
bbsource: gray,
grey: GreyColor[color]];
END;
END;
WindowBox: PROC [box: POINTER TO ABox] RETURNS [Window.Box] =
BEGIN
place: Window.Place ← PicturePlace[[box.x1, box.y1]];
RETURN [[place,
[w: MAX[DotsForADistance[box.x2-box.x1], 1],
h: MAX[DotsForADistance[box.y2 - box.y1], 1]]]];
END;
pending: Redraw ← NIL;
CopySelections: PUBLIC PROC [delta: APosition] =
BEGIN
originalSource: APosition = Absolute[GetSourcePos[FALSE]];
CopyLine: LineScan =
BEGIN
IF lth.selected THEN
BEGIN
new: Redraw = Storage.Node[SIZE[line RedrawObject]];
pos1: APosition = ptb[lth.p1].pos;
pos2: APosition = ptb[lth.p2].pos;
new↑ ← [next: pending, var: line[
pos1: [pos1.x + delta.x, pos1.y + delta.y],
pos2: [pos2.x + delta.x, pos2.y + delta.y],
width: lth.width,
texture: lth.texture]];
lth.selected ← FALSE;
MaybeDisplayLine[l];
pending ← new;
END;
RETURN[FALSE];
END;
CopyLabel: LabelScan =
BEGIN
IF lbh.selected THEN
BEGIN
new: Redraw = Storage.Node[SIZE[label RedrawObject]];
pos: APosition = lbh.pos;
new↑ ← [next: pending, var: label[
font: lbh.font, mode: lbh.mode,
pos: [pos.x + delta.x, pos.y + delta.y],
hti: lbh.hti]];
pending ← new;
lbh.selected ← FALSE;
PaintLabel[lb];
END;
RETURN[FALSE];
END;
IF BadMove[delta] THEN RETURN;
[] ← SelectedLines[CopyLine];
[] ← SelectedLabels[CopyLabel];
header.selectedLines ← LTNull;
header.selectedLabels ← LBNull;
RedrawItems[pending];
pending ← NIL;
ASetSourcePos[[x: originalSource.x + delta.x,
y: originalSource.y + delta.y]];
ASetDestPos[[x: originalSource.x + 2*delta.x,
y: originalSource.y + 2*delta.y]];
END;
BadMove: PROC [delta: APosition] RETURNS [BOOLEAN] =
BEGIN
BadPoint: PointScan =
BEGIN
RETURN [pth.selected AND
(pth.pos.x + delta.x < 0 OR pth.pos.y + delta.y < 0)];
END;
BadLine: LineScan =
BEGIN
p1: PTIndex = lth.p1;
p2: PTIndex = lth.p2;
RETURN [lth.selected AND
(ptb[p1].pos.x + delta.x < 0 OR ptb[p1].pos.y + delta.y < 0 OR
ptb[p2].pos.x + delta.x < 0 OR ptb[p2].pos.y + delta.y < 0)]
END;
BadLabel: LabelScan =
BEGIN
RETURN [lbh.selected AND
(lbh.pos.x + delta.x < 0 OR lbh.pos.y + delta.y < 0)];
END;
IF delta.x > 0 AND delta.y > 0 THEN RETURN[FALSE];
IF AllPoints[BadPoint] # PTNull THEN GO TO bad;
IF AllLines[BadLine] # LTNull THEN GO TO bad;
IF AllLabels[BadLabel] # LBNull THEN GO TO bad;
RETURN[FALSE];
EXITS
bad => {UserTerminal.BlinkDisplay[]; RETURN[TRUE]};
END;
MoveSelections: PUBLIC PROC [delta: APosition] =
BEGIN
originalSource: APosition = Absolute[GetSourcePos[FALSE]];
MoveLine: LineScan =
BEGIN
IF lth.selected THEN
BEGIN
new: Redraw = Storage.Node[SIZE[line RedrawObject]];
pos1: APosition = ptb[lth.p1].pos;
pos2: APosition = ptb[lth.p2].pos;
new↑ ← [next: pending, var: line[
pos1: [pos1.x + delta.x, pos1.y + delta.y],
pos2: [pos2.x + delta.x, pos2.y + delta.y],
width: lth.width,
texture: lth.texture]];
pending ← new;
DeleteLine[l];
END;
RETURN[FALSE];
END;
MoveLabel: LabelScan =
BEGIN
IF lbh.selected THEN
BEGIN
new: Redraw = Storage.Node[SIZE[label RedrawObject]];
pos: APosition = lbh.pos;
new↑ ← [next: pending, var: label[
font: lbh.font, mode: lbh.mode,
pos: [pos.x + delta.x, pos.y + delta.y],
hti: lbh.hti]];
pending ← new;
DeleteLabel[lb];
END;
RETURN[FALSE];
END;
MovePoint: PointScan =
BEGIN
IF pth.selected THEN
BEGIN
pos1: APosition = [x: pth.pos.x + delta.x, y: pth.pos.y + delta.y];
pBox: Window.Box = BoxForPoint[p];
StretchLine: LineScan =
BEGIN
other: PTIndex = IF lth.p1 = p THEN lth.p2 ELSE lth.p1;
pos2: APosition ← ptb[other].pos;
new: Redraw = Storage.Node[SIZE[line RedrawObject]];
IF ptb[other].selected THEN
{pos2.x ← pos2.x + delta.x; pos2.y ← pos2.y + delta.y};
new↑ ← [next: pending, var: line[
pos1: pos1, pos2: pos2, width: lth.width, texture: lth.texture]];
pending ← new;
DeleteLine[l];
RETURN[FALSE];
END;
Window.InvalidateBox[pictureWindow, pBox];
[] ← LinesThru[p, StretchLine];
END;
RETURN[FALSE];
END;
IF BadMove[delta] THEN RETURN;
[] ← SelectedLines[MoveLine];
[] ← SelectedLabels[MoveLabel];
[] ← SelectedPoints[MovePoint];
header.selectedLines ← LTNull;
header.selectedPoints ← PTNull;
header.selectedLabels ← LBNull;
Window.ValidateTree[];
RedrawItems[pending];
pending ← NIL;
ASetSourcePos[[x: originalSource.x + delta.x,
y: originalSource.y + delta.y]];
ASetDestPos[originalSource];
END;
BadRotate: PROC [source, dest: APosition] RETURNS [BOOLEAN] =
BEGIN
BadLine: LineScan =
BEGIN
p1: PTIndex = lth.p1;
p2: PTIndex = lth.p2;
RETURN [lth.selected AND
(dest.x - (ptb[p1].pos.y - source.y) < 0 OR
dest.y + (ptb[p1].pos.x - source.x) < 0 OR
dest.x - (ptb[p2].pos.y - source.y) < 0 OR
dest.y + (ptb[p2].pos.x - source.x) < 0)]
END;
BadLabel: LabelScan =
BEGIN
RETURN [lbh.selected AND
(dest.x - (lbh.pos.y - source.y) < 0 OR
dest.y + (lbh.pos.x - source.x) < 0)];
END;
IF SelectedLines[BadLine] # LTNull THEN GO TO bad;
IF SelectedLabels[BadLabel] # LBNull THEN GO TO bad;
RETURN[FALSE];
EXITS
bad => {UserTerminal.BlinkDisplay[]; RETURN[TRUE]};
END;
MoveAndRotate: PUBLIC PROC [source, dest: APosition] =
BEGIN
originalSource: APosition = Absolute[GetSourcePos[FALSE]];
MoveLine: LineScan =
BEGIN
IF lth.selected THEN
BEGIN
new: Redraw = Storage.Node[SIZE[line RedrawObject]];
pos1: APosition = ptb[lth.p1].pos;
pos2: APosition = ptb[lth.p2].pos;
new↑ ← [next: pending, var: line[
pos1: [dest.x - (pos1.y - source.y), dest.y + (pos1.x - source.x)],
pos2: [dest.x - (pos2.y - source.y), dest.y + (pos2.x - source.x)],
width: lth.width,
texture: lth.texture]];
pending ← new;
DeleteLine[l];
END;
RETURN[FALSE];
END;
MoveLabel: LabelScan =
BEGIN
IF lbh.selected THEN
BEGIN
new: Redraw = Storage.Node[SIZE[label RedrawObject]];
pos: APosition = lbh.pos;
new↑ ← [next: pending, var: label[
font: lbh.font, mode: lbh.mode,
pos: [dest.x - (pos.y - source.y), dest.y + (pos.x - source.x)],
hti: lbh.hti]];
pending ← new;
DeleteLabel[lb];
END;
RETURN[FALSE];
END;
IF BadRotate[source, dest] THEN RETURN;
[] ← SelectedLines[MoveLine];
[] ← SelectedLabels[MoveLabel];
header.selectedLines ← LTNull;
header.selectedLabels ← LBNull;
ClearSelections[];
Window.ValidateTree[];
RedrawItems[pending];
pending ← NIL;
END;
RedrawItems: PUBLIC PROC [rd: Redraw] =
BEGIN
WHILE rd # NIL DO
this: Redraw = rd;
WITH this SELECT FROM
line =>
BEGIN
l: LTIndex = InsertLine[
InsertPoint[pos1], InsertPoint[pos2], width, texture];
ltb[l].selNext ← header.selectedLines;
header.selectedLines ← l;
MaybeDisplayLine[l];
END;
label =>
BEGIN
lb: LBIndex =
InsertLabel[pos: pos, hti: hti, font: font, mode: mode];
lbb[lb].selNext ← header.selectedLabels;
header.selectedLabels ← lb;
PaintLabel[lb];
END;
ENDCASE;
rd ← this.next;
ENDLOOP;
END;
END.