// F E D I T U T I L (PREPRESS)
//
//Bcpl/f FEditUtil.bcpl
//
// FEDIT -- font editor for low resolution fonts.

//Last modified September 28, 1980 3:28 PM by Lyle Ramshaw, PARC
// Added check in FlipGrid to eliminate a call to BitBlt with
// a negative value of topy. This means that Area now works even
// when Grid is on and DotSize divides 400.

//Modified September 17, 1980 12:04 PM by Kerry LaPrade, XEOS
// "Improved" handling when dotsize eq 1.

//Modified March 11, 1980 10:26 AM (by LaPrade)
//Modified January 24, 1980 11:33 AM (by LaPrade)
// PaintString uses rounded rather than truncated widths.
// Background tick-marks off screem warning messages removed.
//Modified January 8, 1980 10:16 AM (by LaPrade)
// Moved FEditMemoryAlloc, FindBoundingBox procedures to other files
//Modified August 28, 1978 5:05 PM by (by LaPrade)
//

get "AuxiliaryMenuDefs.d"
//get "BitBlt.d"
get "FEdit.DFS"
get "FeditNames.d"
//get "GoodFoo.d"
//get "IX.DFS"

// outgoing procedures
external
[
//
FEditMemoryAlloc - Moved to FEdit.bcpl
// FindBoundingBox - Moved to FEditFile.bcpl
FlipGrid
PaintString
PaintWidthMarker
ReadCharBit
SetUpBBT
TurnGridOff
WriteCharBit
]

// outgoing statics
external
[
@gridIsDesired
@gridIsOn
]
static
[
@gridIsDesired = true //True if user wants grid to be displayed.
@gridIsOn //True if grid is displayed
]

// incoming procedures
external
[
//FEditFile
EditFindChar
//
PauseForCR
WindowRead
WindowReadBlock

//PrepressUtil
RoundDp
]

// incoming statics
external
[
@backgroundView
@bits
@editBox
@editBoxXSize
@editBoxYSize
@FEditMenu
@foregroundView
@readBBT
@readBit
@showBox
@showBoxXSize
@showBoxYSize
@updateBox
@WidthMarker

//PrepressMenu1
@selectedButton
]

// internal statics
//static
// [
// ]

// File-wide structure and manifest declarations.

manifest
[
infinity = 1000 //Close enuf
]

// Procedures

//*********************************************************
let WriteCharBit(view, x, y, xExtent, yExtent; numargs na) = valof
//*********************************************************
[
DefaultArgs(lv na, 1, -1, -1, 1, 1)
let bbt, itFits = view>>VIEW.BBT, true
test na eq 1
ifso //Erase view.
[
if view eq foregroundView then FillBox(updateBox, white, 0)
TurnGridOff()

let eraseBBT = vec (size BBT/16 + 1)
eraseBBT = (eraseBBT + 1) & not 1
//SetUpBBT(BBT, sourceType, operation, dbca, dbmr, dlx, dty, dw, dh, sbca, sbmr, slx, sty, gray0, gray1, gray2, gray3)
SetUpBBT(eraseBBT, sGrayBlock, opErase, 0, 0, 0, 0, 606, 808, 0, 0, 0, 0, bbt>> BBT.gray↑0, bbt>> BBT.gray↑1)

BoxBitBlt(eraseBBT, editBox)
]

ifnot
[
// if x ls 0 % y ls 0 % x + xExtent gr view>>VIEW.nDotsX % y + yExtent gr view>>VIEW.nDotsY then return
if x ls 0 % y ls 0 % x + xExtent gr view>>VIEW.nDotsX % y + yExtent gr view>>VIEW.nDotsY then
[
itFits = false
if x gr view>>VIEW.nDotsX % y gr view>>VIEW.nDotsY % x + xExtent ls 0 % y + yExtent ls 0 then resultis itFits
]
if view eq foregroundView & view>>VIEW.dotSize ge 3 then
PartialFillBox(updateBox, flip, x, view>>VIEW.nDotsY - y - yExtent, xExtent, yExtent)
let temp = editBoxYSize - (y + yExtent) * (view>>VIEW.dotSize)
bbt>> BBT.dlx = x * (view>>VIEW.dotSize)
bbt>> BBT.dty = temp
bbt>> BBT.dw = xExtent * (view>>VIEW.dotSize)
bbt>> BBT.dh = yExtent * (view>>VIEW.dotSize)
temp = temp & 1
if temp then FixBBTGrayBlock(bbt)
BoxBitBlt(bbt, editBox, 0)
if temp then FixBBTGrayBlock(bbt)
]
resultis itFits
]

//Read a "bit" of a character.

//*********************************************************
and ReadCharBit(view, x, y) = valof
//*********************************************************
[
//Idea here is to bitblt a sample of the "bit" to a test area.

if x ls 0 % y ls 0 % x ge view>>VIEW.nDotsX % y ge view>>VIEW.nDotsY then resultis false

Zero(readBit, 2)
//Get appropriate gray block
//Note: BBT + 12 = beginning of gray block
MoveBlock(lv (readBBT!12), lv ((view>>VIEW.BBT)!12), 4)

let viewSpacing = view>>VIEW.dotSize
let temp1 = x * view>>VIEW.dotSize
let temp2 = editBoxYSize - (y + 1) * view>>VIEW.dotSize
readBBT>> BBT.slx = temp1
readBBT>> BBT.sty = temp2

temp1 = temp1 & 1
if temp1 then
for I = 12 to 15 do
// readBBT!I = (readBBT!I rshift 1) % (readBBT!I rshift 1)
readBBT!I = (readBBT!I lshift 1) % (readBBT!I rshift 1)

temp2 = temp2 & 1
if temp2 then FixBBTGrayBlock(readBBT)

BoxBitBlt(readBBT, 0, editBox)

resultis (readBit!0 ne 0) % (readBit!1 ne 0)
]

//*********************************************************
and FixBBTGrayBlock(BBT) be
//*********************************************************
//Important: assumes gray pattern repeats mod 2 in y direction.
[
MoveBlock(lv (BBT!12), lv (BBT!13), 2)
MoveBlock(lv (BBT!14), lv (BBT!12), 2)
]

//*********************************************************
and PaintWidthMarker(border,val; numargs na) be
//*********************************************************
[
// Set width markers in the margins of the edit area. First argument is
// border number to deal with. Second argument is value of marker.
// For borders 5 & 6, these are the markers that mark the point of the
//
unsampled widths; in this case, value is in Alto units.

if border eq 0 then return
let color = nil
test na eq 2
ifso
[
PaintWidthMarker(border)
//erase old one.
let otherBorder = selecton border into
[
case left: 5
case bottom: 6
default:0
]
PaintWidthMarker(otherBorder)
//erase it.
color = black
//Write
WidthMarker!border = val
//Save new value
PaintWidthMarker(otherBorder, WidthMarker!otherBorder)
]
ifnot
[
color = white
val = WidthMarker!border
//Turn off old value.
]
let Altoval = selecton border into
[
case left:
case right:
case bottom:
case top:
val * foregroundView>> VIEW.dotSize
case 5:
val + (WidthMarker!1 * foregroundView>>VIEW.dotSize)
case 6:
val + (WidthMarker!3 * foregroundView>>VIEW.dotSize)
]

let w, h = nil, nil
switchon border into
[
case left:
case right:
w = borderWidth
h = 1
endcase

case bottom:
case top:
w = 1
h = borderWidth
endcase

default:
w = 2
h = 2
endcase
]

if (border le 4) % backgroundView do
[
let x,y = 0,0
test valof
[
if Altoval ls 0 then resultis false
switchon border into
[
case left:
case right:
case 5:
if Altoval ge editBoxYSize then resultis false
y = 2 + editBoxYSize - Altoval
endcase

case 6:
y = borderWidth - 2
case bottom:
case top:
if Altoval ge editBoxXSize then resultis false
x = 3 + Altoval
endcase
]
resultis true
]
ifso
[
let box = selecton border into
[
case left: FEditMenu!bLeftBorder
case right:
case 5: FEditMenu!bRightBorder
case bottom: FEditMenu!bBottomBorder
case top:
case 6: FEditMenu!bTopBorder
default: 0
]
PartialFillBox(box, color, x, y, w, h)
]
ifnot
[
if na ls 2 then return
if border ge 5 then return
Ws("Warning: ")
switchon border into
[
case left: Ws("left-hand")
endcase

case right: Ws("right-hand")
endcase

// case 5: Ws("right-hand background")
// endcase

case bottom: Ws("bottom")
endcase

case top:Ws("top")
endcase

// case 6: Ws("top background")
]
Wl(" tick mark lies off screen!")
]
]
]

//*********************************************************
and FlipGrid() be
//*********************************************************
[
//First create one scan-line worth of grid, then bitblt it in a line
// at a time.
test foregroundView>>VIEW.dotSize gr 2
ifso
[
let squareSize = foregroundView>>VIEW.dotSize
let sourceBlock = vec 38 //Screen width in words
sourceBlock = (sourceBlock + 1) & not 1
Zero(sourceBlock, 38)
let indexBit = 0
for I = 0 to foregroundView>>VIEW.nDotsX do
[
let index = indexBit rshift 4
sourceBlock!index = (sourceBlock!index) % (bits!(indexBit & 17b))
indexBit = indexBit + squareSize
]

let gridBBT = vec (size BBT / 16)
gridBBT = (gridBBT + 1) & not 1
//SetUpBBT(BBT, sourceType, operation, dbca, dbmr, dlx, dty, dw, dh, sbca, sbmr, slx, sty, gray0, gray1, gray2, gray3)
SetUpBBT(gridBBT, sBitMap, opInvert, 0, 0, 0, editBoxYSize - 1, 606, 1, sourceBlock, 38)

for I = 0 to foregroundView>>VIEW.nDotsY do
[
if gridBBT>>BBT.dty ge 0 then BoxBitBlt(gridBBT, editBox)
gridBBT>> BBT.dty = gridBBT>> BBT.dty - squareSize
]
gridIsOn = not gridIsOn
]
ifnot
[
Wl("Dot size too small for grid.")
gridIsDesired = false
]
]

//*********************************************************
and TurnGridOff() be if gridIsOn then FlipGrid()
//*********************************************************


//*********************************************************
and PaintString(string) be
//*********************************************************
[
//Paint a string on the display, using the edited character font
// as a source of characters.

let numCharactersShown = 0
let startXY = vec 1

until Endofs(keys) do Gets(keys)

until numCharactersShown eq string>>STRING.length do
[
let endIndex = ClipString(string, numCharactersShown + 1, startXY)
PaintClippedString(string, numCharactersShown + 1, endIndex, startXY)
if endIndex ge string>>STRING.length then break
numCharactersShown = endIndex - 1
unless (selectedButton eq 2) & GetConfirmation("Show more?") do break
]
]


//*********************************************************
and ClipString(string, beginIndex, startXY) = valof
//*********************************************************
[
//Compute width of string
let w = vec (size CharWidth / 16)
let widths = vec 1
let offsets = vec 1
let boundingBox = vec 1
let startPosition = vec 1; Zero(startPosition, 2)
let minStart = vec 1; Zero(minStart, 2)
let maxStart = vec 1; Zero(maxStart, 2)
let firstGoddamBit = vec 1; SetBlock(firstGoddamBit, infinity, 2)
let lastGoddamBit = vec 1; SetBlock(lastGoddamBit, -infinity, 2)
let stringIndex = nil //string>> STRING.length + 1

for I = beginIndex to string>> STRING.length do
[
let c = string>> STRING.char↑I
let s = EditFindChar(c, w, 1) //Look on scratch file
unless s do s = EditFindChar(c, w, 2) //Look on original file
if s then
[
offsets!0 = w>>CharWidth.XL
offsets!1 = w>>CharWidth.YB
boundingBox!0 = w>>CharWidth.W
boundingBox!1 = w>>CharWidth.H
for J = 0 to 1 do //0 for X, 1 for Y
[
let temp = nil
temp = startPosition!J + offsets!J
if temp ls firstGoddamBit!J then firstGoddamBit!J = temp
temp = temp + boundingBox!J
if temp gr lastGoddamBit!J then lastGoddamBit!J = temp

startXY!J = -firstGoddamBit!J + 1
]

if lastGoddamBit!1 - firstGoddamBit!1 ls showBoxYSize - 2 then
startXY!1 = (showBoxYSize - (lastGoddamBit!1 - firstGoddamBit!1)) / 2
if lastGoddamBit!0 - firstGoddamBit!0 gr showBoxXSize - 2 then resultis I

// widths!0 = @(lv w>>CharWidth.WX)
// widths!1 = @(lv w>>CharWidth.WY)
widths!0 = RoundDp(lv w>>CharWidth.WX)
widths!1 = RoundDp(lv w>>CharWidth.WY)
for J = 0 to 1 do //0 for X, 1 for Y
[
startPosition!J = startPosition!J + widths!J
if startPosition!J ls minStart!J then minStart!J = startPosition!J
if startPosition!J gr maxStart!J then maxStart!J = startPosition!J
]

//if maxStart!0 - minStart!0 gr showBoxXSize - 2 then resultis I
if maxStart!1 - minStart!1 gr showBoxYSize - 2 then resultis I
]
]
startXY!0 = (showBoxXSize - (lastGoddamBit!0 - firstGoddamBit!0)) / 2
resultis string>> STRING.length
]

//*********************************************************
and PaintClippedString(string, beginIndex, endIndex, startXY) be
//*********************************************************
[
FillBox(showBox, white, 0) //Erase showBox
let x = startXY!0
let y = startXY!1
let outline = showBox>>BOX.bits
let Xo = showBox>>BOX.xorigin + outline
//let Xc = showBox>>BOX.xcorner - outline
let Yc = showBox>>BOX.ycorner - outline
//Get dcb
let dcb = showBox>>BOX.dcb
let map = dcb>>DCB.bitmap
let width = dcb>>DCB.width

let p = vec 100
let w = vec CharWidthsize
for i = beginIndex to endIndex do
[
let c = string>> STRING.char↑i
let s = EditFindChar(c, w, 1)
//Look on scratch file
unless s do s = EditFindChar(c, w, 2) //Look on original file
if s then
[
let xl = Xo + x + w>>CharWidth.XL
let yb = y + w>>CharWidth.YB
let adr = map + (Yc - yb) * width + xl / 16
let testdp = map + (outline + 2)* width
xl = xl rem 16
let b = WindowRead(s)
//FHEAD
let hw = b<<FHEAD.hw
//Words high.
let ns = b<<FHEAD.ns
//Number of scan lines.
for i = 1 to ns do
[
if (x + w>>CharWidth.XL + i) ge showBoxXSize then break
let dp = adr
WindowReadBlock(s, p, hw)
for pc = 0 to hw - 1 do
for j = 0 to 15 do
[
if dp le testdp then break
if ((p!pc) & (bits!j)) ne 0 then
@dp = @dp % (bits!xl)
dp = dp - width
]
xl = xl + 1
if xl eq 16 then
[
xl = 0
adr = adr + 1
]
]
// x = x + @(lv w>>CharWidth.WX)
// y = y + @(lv w>>CharWidth.WY)
x = x + RoundDp(lv w>> CharWidth.WX)
y = y + RoundDp(lv w>> CharWidth.WY)
]
]
]
//*********************************************************
and SetUpBBT(bitBltTable, sourceType, operation, dbca, dbmr, dlx, dty, dw, dh, sbca, sbmr, slx, sty, g0, g1; numargs na) be
//*********************************************************
[
if (na gr 0) & (bitBltTable ne 0) then Zero(bitBltTable, (size BBT / 16))
switchon na into
[
default:
case 15:
bitBltTable>> BBT.gray↑1 = g1
bitBltTable>> BBT.gray↑3 = g1
case 14:
bitBltTable>> BBT.gray↑0 = g0
bitBltTable>> BBT.gray↑2 = g0
case 13:
bitBltTable>> BBT.sty = sty
case 12:
bitBltTable>> BBT.slx = slx
case 11:
bitBltTable>> BBT.sbmr = sbmr
case 10:
bitBltTable>> BBT.sbca = sbca
case 9:
bitBltTable>> BBT.dh = dh
case 8:
bitBltTable>> BBT.dw = dw
case 7:
bitBltTable>> BBT.dty = dty
case 6:
bitBltTable>> BBT.dlx = dlx
case 5:
bitBltTable>> BBT.dbmr = dbmr
case 4:
bitBltTable>> BBT.dbca = dbca
case 3:
bitBltTable>> BBT.op = operation
case 2:
bitBltTable>> BBT.sType = sourceType
case 1:
case 0:
endcase
]
]