-- 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.