// F E D I T L O O P  (PREPRESS)
//
// FEDIT -- font editor for low resolution fonts.
//
//Bcpl/f FEditLoop.bcpl
//
//Last modified February 5, 1980  1:16 PM by Kerry A. LaPrade, XEOS
//  Split out initialization stuff. Added full octal capability
//    for "Show TypeIn". Reworked "File Character" section of
//    user interface.
//Modified:  December 17, 1979  10:40 AM (by LaPrade)
//  Added gnat to update box; changed Consec box default

//Modified:  December 10, 1979  11:15 AM (by LaPrade)
//Modified:  December 3, 1979  10:52 AM (by LaPrade)
//Modified:  November 30, 1979  9:47 AM (by LaPrade)
//Modified:  September 12, 1978 (by LaPrade)

get "AuxiliaryMenuDefs.d"
get "FEdit.DFS"
get "FEditNames.d"

// outgoing procedures
external
   [
   ClearEditWindow
   FEditLoop
   WriteCharCodes
   ]

// outgoing statics
external
   [
   @backgroundArea
   @backgroundOn
   @backgroundView
   @bits		//bits!0 = #100000
   @currentCharacterCode	//0 to 377b
   @currentCharacterWidths
   @editBox
   @editBoxXSize
   @editBoxYSize
   @EFactorX		//Enlargement factor of background
   @EFactorY
   @FEditMenu
   @foregroundView
   @readBBT
   @readBit
   @showBox
   @showBoxXSize
   @showBoxYSize
   @showTypeInString
   @updateBox
   @WidthChanged	//true if width marker moved
   @WidthMarker	//Vector of width information (index by border #)
   ]

static
   [
   @backgroundArea
   @backgroundOn = true
   @backgroundView
   @bits		//bits!0 = #100000
   @currentCharacterCode = 377b	//0 to 377b
   @currentCharacterWidths
   @editBox
   @editBoxXSize
   @editBoxYSize
   @EFactorX		//Enlargement factor of background
   @EFactorY
   @FEditMenu
   @foregroundView
   @readBBT
   @readBit
   @showBox
   @showBoxXSize
   @showBoxYSize
   @showTypeInString
   @updateBox
   @WidthChanged = false	//true if width marker moved
   @WidthMarker	//Vector of width information (index by border #)
   ]

// incoming procedures
external
   [
//FEDITFILE
//         Area
   EditFindChar
   EditReadChar
   EditUnWriteChar
   EditWriteChar
//   EFileFinish
//   EFileStart

//FEDITUTIL
   FEditMemoryAlloc
   FlipGrid
   PaintString
   PaintWidthMarker
   ReadCharBit
   TurnGridOff
   WriteCharBit

//FLOAT
   FDV
   FLDI

//PREPRESSUTIL
   FSGetX
   FSPut
   IllCommand

//SCAN
   AppendChar
   SearchChar
   StringToValue
   TypeForm

//STRINGSTREAMS
   CreateStringStream

//STRINGUTIL
   CopyString
   ExtractSubstring
   ]

// incoming statics
external
   [
//FEDITFILE
   @LastCharCodeWritten

//FEDITUTIL
   @gridIsDesired	//True if user wants grid displayed
   @gridIsOn	//True if grid is on

//PREPRESSMENU1
   @selectedBoxNum
   @selectedButton
   ]

// internal statics
static
   [
   editState
   currentBackgroundOffsetx	//Current background offset in x.
   currentBackgroundOffsety	//	ditto for y
   cursorPattern
   gnatIsDesired = true
   needToGetBackground
   startSequenceChar = 0
   widthsMode = false
   ]

manifest
   [
   backSpace = 10b

   editStateGetDelete = 1
   editStatePutCancel = 2
   editStateGetUnwrite = 3
   ]

//Procedures

//*********************************************************
let FEditLoop() be
//*********************************************************
   [FEditLoop
      [EditLooprepeat

      unless (gridIsDesired eqv gridIsOn) do FlipGrid()

   //Poll "active" boxes.
   // ScanMenu(Menu, loopOverMenu [true], returnKey [false], sweep [false])
      let s = ScanMenu(FEditMenu, false, true)
      selectedBoxNum = s<<SELECTION.boxNumber
      selectedButton = GetMouseButton(s)
      let selectedBox = FEditMenu!selectedBoxNum
      test selectedBoxNum
      ifso
         [ifso
         Wl("")  //Scroll message window so user won't be confused by old messages
         test  selectedBoxNum eq bWidths
         ifso
            [
            test  widthsMode
            ifso
               [
               FillBox(selectedBox, flip)
               DeActivateWidths()
               ]
            ifnot ActivateWidths()
            ]
         ifnot if widthsMode then DeActivateWidths()
         switchon selectedBoxNum into
            [
            case bSymbol:
            case bOctal:
               ReadCharCode(selectedBoxNum)
            endcase

            case bGetPut:
               test  editState eq editStatePutCancel
               ifso
                  [
                  PutChar()
                  ClearEditWindow()
                  ChangeBoxes(editStateGetUnwrite)
                  FillBox(selectedBox, flip)
                  ]
               ifnot GetNewCharacter()
            endcase

            case bAuto:
               Auto()
            endcase

            case bDeleteCancelUnwrite:
               DeleteCancelUnwrite(s)
            endcase

           case bGrid:
               gridIsDesired = not gridIsDesired
            endcase

            case bShiftBackground:
               ShiftBackground()
            endcase

            //case bWidths:
            //   unless widthsMode do ActivateWidths()
            //endcase

            case bBkgrnd:
               backgroundOn = not backgroundOn
               GetBackground(currentCharacterCode, currentBackgroundOffsetx, currentBackgroundOffsety)
            endcase

            case bArea:
               Area()
            endcase

            case bUpdateBox:
               gnatIsDesired = not gnatIsDesired
            endcase

            case bQuit:
               if ((LastCharCodeWritten eq -1 & editState eq editStateGetDelete) ? true, ConfirmMenuSelection("Quit", s)) then
                  [
                  PutChar()
                  return
                  ]
            endcase

            case bShowBox:
            case bShowSequence:
            case bShowTypeIn:
               Show()
            endcase
            ]
         unless selectedBoxNum eq bWidths do DeSelect(selectedBox)
         ]ifso
   //Poll "edit" box (inactive)
      ifnot
         [
         test widthsMode
         ifso TickMarks()
         ifnot
            if CursorInside(editBox, 0, 0) then Draw()
         ]

      ]EditLooprepeat
      repeat
   ]FEditLoop


//Utilities....

//*********************************************************
and DeleteThisCharacter() be
//*********************************************************
   [
   FillBox(editBox, flip, 0)
   unless ConfirmMenuSelection("Are you sure you want to delete this character?") do
      [
      FillBox(editBox, flip, 0)
      return
      ]
   let w = vec (size CharWidth / 16)
   if EditFindChar(currentCharacterCode, w, 1) % EditFindChar(currentCharacterCode, w, 2) then
       EditWriteChar(currentCharacterCode, true, w)
   ]

//*********************************************************
and ReadCharCode(boxNumber) be
//*********************************************************
   [
   until Endofs(keys) do Gets(keys)
   if selectedBoxNum eq bGetPut then FillBox(FEditMenu!boxNumber, flip)
   switchon boxNumber into
      [
      case bGetPut:
         currentCharacterCode = LastCharCodeWritten
      endcase

      case bSymbol:
            [
            currentCharacterCode = Gets(keys)
            ]
         repeatuntil Endofs(keys)
      endcase

      case bOctal:
         let string = vec 1; string!0 = 0
         for I = 1 to 3 do
            [
            let temp = Gets(keys)
            unless (temp ge $0) & (temp le $7) do break
            AppendChar(temp, string)
            FillBox(FEditMenu!bOctal, white, 0)
            WriteBox(FEditMenu!bOctal, string)
            FillBox(FEditMenu!bOctal, flip, 0)
            ]
         currentCharacterCode = StringToValue(string, 8)
      endcase
      ]

   WriteCharCodes(currentCharacterCode)
   unless selectedBoxNum eq bGetPut do FillBox(FEditMenu!boxNumber, flip)
   ]

//*********************************************************
and WriteCharCodes(code) be
//*********************************************************
   [
   if code ge 400b then code = 0
   if code ls 0 then code = 377b

   let string = vec 1; string!0 = 0

   test code ls $*s
   ifso
      [
      AppendChar($↑, string)
      AppendChar(code + $@, string)
      ]
   ifnot AppendChar(code, string)

   OverwriteBox(FEditMenu!bSymbol, string)

   let stringStream = CreateStringStream(string, 3)
   if code ls 100b then
      [
      Puts(stringStream, $0)
      if code ls 10b then Puts(stringStream, $0)
      ]
   Wns(stringStream, code, 0, 10b)
   Closes(stringStream)

   OverwriteBox(FEditMenu!bOctal, string)

   ChangeBoxes(((code eq LastCharCodeWritten) ? editStateGetUnwrite, editStateGetDelete))

   currentCharacterCode = code
   ]

//*********************************************************
and Auto() be
//*********************************************************
   [
   PutChar()
   let w = vec (size CharWidth / 16)
   let direction = selecton selectedButton into
      [
      case 1:  1
      case 2:  0
      case 3: -1
      ]
   if direction ne 0 then
      [
      let quitLoop = false
         [
         currentCharacterCode = currentCharacterCode + direction
         WriteCharCodes(currentCharacterCode)
         for J = 1 to 3 do
            if EditFindChar(currentCharacterCode, w, J) then
               quitLoop = true
         if quitLoop then break
         switchon direction into
            [
            case -1: if currentCharacterCode eq 0 then return
            endcase

            case 1: if currentCharacterCode eq 377b then return
            ]
         ]
      repeat
      ]
   NewChar() 
   ]

//*********************************************************
and GetNewCharacter() be
//*********************************************************
   [
   let boxNum = selecton selectedButton into
      [
      case 1: bSymbol
      case 2: bOctal
      case 3: selectedBoxNum
      ]
   ReadCharCode(boxNum)
   NewChar()
   FillBox(FEditMenu!bGetPut, flip)
   ]

//*********************************************************
and NewChar(useLatestVersionOfChar; numargs na) be
//*********************************************************
   [
   DefaultArgs(lv na, 0, true)
   FillBox(FEditMenu!bGetPut, flip)
   let changeIt = false
   let a = EditFindChar(currentCharacterCode, currentCharacterWidths, 1)	//Check scratch file.
   test (a ne 0) & useLatestVersionOfChar
   ifso
      [
      Wl("Getting character from *"ACEdits*" file . . .")
      changeIt = currentCharacterCode eq LastCharCodeWritten
      WidthChanged = valof
         [
         let w = vec (size CharWidth / 16)
         let temp = EditFindChar(currentCharacterCode, w, 2)
         for I = 0 to (size CharWidth / 16) - 1 do
            if w!I ne currentCharacterWidths!I then resultis true
         resultis false
         ]
      a = EditFindChar(currentCharacterCode, currentCharacterWidths, 1)  //Position file window again just in case.
      ]
   ifnot
      [
      a = EditFindChar(currentCharacterCode, currentCharacterWidths, 2)  //Use original file.
      if a ne 0 then Wl("Getting character from original file . . .")
      ]

   TurnGridOff()
   WriteCharBit(foregroundView)  //Erase foreground
   if a ne 0 then
      EditReadChar(foregroundView, a, currentCharacterWidths)
   GetBackground(currentCharacterCode, 0, 0)

   ChangeBoxes(changeIt ? editStateGetUnwrite, editStateGetDelete)
   ]

//*********************************************************
and ClearEditWindow() be
//*********************************************************
   [
   FillBox(editBox, flip, 0)

//   changes = false
//   changesPending = false
//   widthsMode = false
   WidthChanged = false

   ChangeBoxes(editStateGetDelete)

   //Erase tick marks
   FillBox(FEditMenu!bLeftBorder, white, 0)
   FillBox(FEditMenu!bRightBorder, white, 0)
   FillBox(FEditMenu!bBottomBorder, white, 0)
   FillBox(FEditMenu!bTopBorder, white, 0)
   Zero(WidthMarker, 7)

   //Erase updateBox
    FillBox(updateBox, white, 0)

   //Erase edit area
   FillBox(editBox, white, 0)
   gridIsOn = false
   ]

//*********************************************************
and PutChar() be
//*********************************************************
   [
   unless editState eq editStatePutCancel do return
   unless selectedBoxNum eq bGetPut do FillBox(FEditMenu!bGetPut, flip)
   EditWriteChar(currentCharacterCode, false, currentCharacterWidths)
   unless selectedBoxNum eq bGetPut do FillBox(FEditMenu!bGetPut, flip)
//   ClearEditWindow()
   ChangeBoxes(editStateGetUnwrite)
   if selectedBoxNum eq bGetPut then FillBox(FEditMenu!bGetPut, flip)
   ]

//*********************************************************
and GetBackground(char, xof, yof) be
//*********************************************************
   [
   currentBackgroundOffsetx = xof  //Save for motions.
   currentBackgroundOffsety = yof
   WriteCharBit(backgroundView)	//Clear it.
   unless backgroundOn do return
   let w = vec (size CharWidth / 16)
   let a = EditFindChar(char, w, 3)	//Background
   if a then EditReadChar(backgroundView, a, w, xof, yof)
   ]


//*********************************************************
and ShiftBackground() be
//*********************************************************
   [
   let xyPair1 = vec 1
   let xyPair2 = vec 1
   until CursorInside(FEditMenu!bShiftBackground) & GetButtons() do
      [
      if CursorInside2(editBox, 0, 0, xyPair1) & GetButtons() then
         [
         while GetButtons() do Noop()
            [
            if CursorInside2(editBox, 0, 0, xyPair2) & GetButtons() then
               [
               let dx = (xyPair2!0 - xyPair1!0)  / backgroundView>>VIEW.dotSize
               let dy = - (xyPair2!1 - xyPair1!1) / backgroundView>>VIEW.dotSize
               GetBackground(currentCharacterCode, currentBackgroundOffsetx + dx, currentBackgroundOffsety + dy)

return //               break
               ]
            ]
         repeatuntil CursorInside(FEditMenu!bShiftBackground) &  GetButtons()
         ]
      ]
   while GetButtons() do Noop()
   ]

//*********************************************************
and ActivateWidths() be
//*********************************************************
   [
   OutlineBorderBoxes(black)
   let t = table
      [
      000000b;
      000000b;
      000000b;
      000000b;
      000400b;
      000400b;
      000400b;
      007740b;
      000400b;
      000400b;
      000400b;
      000000b;
      000000b;
      000000b;
      000000b;
      000000b;
      ]
   cursorPattern = t
   ChangeCursor(cursorPattern)
   needToGetBackground = false
   widthsMode = true
   ]

//*********************************************************
and TickMarks() be
//*********************************************************
   [
   CheckTickMark(bLeftBorder, left, 1)
   CheckTickMark(bRightBorder, right, 1)
   CheckTickMark(bBottomBorder, bottom, 0)
   CheckTickMark(bTopBorder, top, 0)
   ]

//*********************************************************
and CheckTickMark(borderBoxNum, border, n) be
//*********************************************************
   [
   // n = 0 if interested in x coordinate
   // n = 1 if interested in y coordinate
   let xyPair = vec 1
   if CursorInside2(FEditMenu!borderBoxNum, 4, 4, xyPair) & GetButtons() then
      [
      ChangeBoxes(editStatePutCancel)
      PaintWidthMarker(border, (n * foregroundView>>VIEW.nDotsY) + (1 - 2 * n) * (xyPair!n / (foregroundView>>VIEW.dotSize)))
      WidthChanged = true
      if (border eq left) % (border eq bottom) then needToGetBackground = true
      ]
   ]

//*********************************************************
and DeActivateWidths() be
//*********************************************************
   [
   ChangeCursor(cursorPattern)
   OutlineBorderBoxes(white)
   if needToGetBackground then GetBackground(currentCharacterCode, currentBackgroundOffsetx, currentBackgroundOffsety)
   FillBox(FEditMenu!bWidths, flip)
   widthsMode = false
   ]

//*********************************************************
and OutlineBorderBoxes (color) be
//*********************************************************
   [
   OutlineBox(FEditMenu!bLeftBorder, 1, color)
   OutlineBox(FEditMenu!bRightBorder, 1, color)
   OutlineBox(FEditMenu!bBottomBorder, 1, color)
   OutlineBox(FEditMenu!bTopBorder, 1, color)
   ]

//*********************************************************
and Show() be
//*********************************************************
   [
   let string = vec 127
   switchon selectedBoxNum into
      [
      case bShowSequence:
	[
         string!0 = 0
         unless selectedButton eq 3 do
            [
            until Endofs(keys) do Gets(keys)
            Ws("Start string with: ")
            startSequenceChar = Gets(keys)
            if startSequenceChar eq $*N then
               startSequenceChar = currentCharacterCode
            Puts(dsp, startSequenceChar); Wl("")
            ]

         for I = startSequenceChar to startSequenceChar + 376b do
            AppendChar(((I gr 377b)?I - 400b, I), string)

      endcase
      ]

      case bShowTypeIn:
         unless selectedButton eq 3 do
            [
            let numberString = nil
            let numberPosition = nil
            let char = nil
            until Endofs(keys) do Gets(keys)
            OverwriteBox(FEditMenu!bShowBox, showTypeInString)
            FillBox(showBox, flip)
            char = Gets(keys)
            unless char eq $*n do
               [
               showTypeInString!0 = 0
                  [
                  switchon char into
                     [
                     case $*n:
               break
                     endcase

                     case backSpace:
                        unless showTypeInString>> STRING.length eq 0 do
                           showTypeInString>> STRING.length =
                              showTypeInString>> STRING.length - 1
                     endcase

                     case $x:
                        test  kbdAd!3 eq 177773b
                        ifso
                           [
                           numberPosition = valof
                              [
                              for I = showTypeInString>> STRING.length to 1 by -1 do
                                 if showTypeInString>> STRING.char↑I eq $# then resultis I
                              resultis 0
                              ]
                           if numberPosition gr 0 then
                              [
                              numberString = ExtractSubstring(showTypeInString, numberPosition + 1)
                              if numberString>> STRING.length le 3 then
                                 [
                                 showTypeInString>> STRING.length = numberPosition - 1
                                 AppendChar(StringToValue(numberString, 8), showTypeInString)
                                 ]
                              Free(sysZone, numberString)
                              ]
                           ]
                        ifnot AppendChar(char, showTypeInString)
                     endcase

                     default:
                        AppendChar(char, showTypeInString)
                     endcase
                     ]
                  OverwriteBox(showBox, showTypeInString)
                  FillBox(showBox, flip)
                  char = Gets(keys)
                  ]
               repeat
               ]
            ]
         CopyString(string, showTypeInString)
      endcase

      case bShowBox:
         string!0 = 0
         let tempChar = (selectedButton eq 3) ? currentCharacterCode, $H
         AppendChar(tempChar, string)
         AppendChar(currentCharacterCode, string)
         AppendChar(tempChar, string)
      endcase
      ] //end switchon

   if SearchChar(string, currentCharacterCode) then PutChar()
   PaintString(string)
   if selectedBoxNum eq bShowBox then FillBox(showBox, flip)
   ]

//*********************************************************
and Draw() be
//*********************************************************
   [Draw
   let xyPair, squareSize = vec 1, foregroundView>>VIEW.dotSize
   let lastX, strokeBeginX, lastY, strokeBeginY = -1, -1, nil, nil

   while CursorInside2(editBox, -4, -4, xyPair) do
      [
      let x = xyPair!0 / squareSize
      let y = (editBoxYSize - xyPair!1) / squareSize
//foregroundView>>VIEW.nDotsY - xyPair!1 / squareSize

      if x ne lastX % y ne lastY then
         [
         FlipGnat(lastX, lastY)  //Gets clipped if lastX ls 0
         FlipGnat(x, y)
         lastX, lastY = x, y
         ]

      let buttons = GetButtons()
      test  buttons
      ifso
         [
         let paint, string = true, "Flip"

         test  buttons<<MOUSE.key1
         ifso
            [
            paint = not ReadCharBit(foregroundView, x, y)
            string = "Draw"
            ]
         ifnot
            [
            if buttons<<MOUSE.key3 then
               [
               paint = ReadCharBit(foregroundView, x, y)
               string = "Erase"
               ]
            ]

         if strokeBeginX ls 0 then
            [
            Wl("")
            Ws(string)
            Ws(" from")
            WriteCoordinates(x, y)
            strokeBeginX, strokeBeginY = x, y
            Ws(" to")
            ]

         if paint then
            [
            unless editState eq editStatePutCancel do ChangeBoxes(editStatePutCancel)
            WriteCharBit(foregroundView, x, y)
//            FlipGnat(x, y)
//            lastX = -1

               [
               buttons = GetButtons()
               ]
            repeatwhile buttons<<MOUSE.key2
            ]
         ]
      ifnot
         [
         if strokeBeginX ge 0 then
            [
            WriteCoordinates(x, y)
            Ws(".  Extent is")
            let deltaX, deltaY = (x - strokeBeginX, y - strokeBeginY)
            WriteCoordinates(Max(deltaX, -deltaX) + 1, Max(deltaY, -deltaY) + 1)
            Wl(".")
            strokeBeginX = -1
            ]
         ]
      ]
   FlipGnat(lastX, lastY)
   ]Draw

//*********************************************************
and WriteCoordinates(x, y) be
//*********************************************************
   [
   Ws(" (")
   Wns(dsp, x)
   Ws(", ")
   Wns(dsp, y)
   Ws(")")
   ]

//*********************************************************
and FlipGnat(x, y) be
//*********************************************************
   if gnatIsDesired then
      PartialFillBox(updateBox, flip, x, foregroundView>>VIEW.nDotsY - y - 1, 1, 1)

//*********************************************************
and GetButtons() = (not @177030b) & 7
//*********************************************************

//*********************************************************
and ChangeBoxes(newEditState; numargs na) be
//*********************************************************
   [
   DefaultArgs(lv na, 0, editStateGetDelete)
   editState = newEditState
   let string, inactive = nil, true
   OverwriteBox(FEditMenu!bGetPut, "Get")
   switchon editState into
      [
      case editStateGetDelete:
         string = "Delete"
         inactive = false 
      endcase

      case editStatePutCancel:
         OverwriteBox(FEditMenu!bGetPut, "Put")
         string = "Cancel" 
      endcase

      case editStateGetUnwrite:
         string = "Unput" 
      endcase
      ]
   OverwriteBox(FEditMenu!bDeleteCancelUnwrite, string)
   (FEditMenu!bSymbol) >>BOX.inactive = inactive
   (FEditMenu!bOctal) >>BOX.inactive = inactive
   ]

//*********************************************************
and DeleteCancelUnwrite(s) be
//*********************************************************
   [
   switchon editState into
      [
      //Delete
      case editStateGetDelete:
         unless ConfirmMenuSelection("Remove this character from file?", s) do return
         DeleteThisCharacter()
         ClearEditWindow()
      endcase

      //Cancel
      case editStatePutCancel:
         PartialFillBox(editBox, grayFlip, 0)
         unless ConfirmMenuSelection("Wipe the screen?", s) do
            [
            PartialFillBox(editBox, grayFlip, 0)
            return
            ]
         ClearEditWindow()
         NewChar()
      endcase

      //Unwrite
      case editStateGetUnwrite:
         PartialFillBox(editBox, grayFlip, 0)
         test ConfirmMenuSelection("*"Undo*" previously-Put changes to this character?", s)
         ifso
            [
            Wl("Reverting to prior version of this character.")
            EditUnWriteChar(currentCharacterCode)
            ClearEditWindow()
            NewChar(selectedButton ne 2)
            ChangeBoxes(editStatePutCancel)
            PutChar()
            ]
         ifnot
            [
            PartialFillBox(editBox, grayFlip, 0)
            return
            ]
      endcase
      ]
   FillBox(FEditMenu!bDeleteCancelUnwrite, flip)
   ]

//*********************************************************
and Area() be
//*********************************************************
   [
   //Compute area of char on screen
   let ar = 0
   Wl("Computing area")
   for x = 0 to foregroundView>>VIEW.nDotsX - 1 do
      for y = 0 to foregroundView>>VIEW.nDotsY - 1 do
         if ReadCharBit(foregroundView, x, y) then ar = ar + 1
   TypeForm("Area: ", 10, ar, ".  Background area: ")
   FLDI(1, backgroundArea)
   FLDI(2, EFactorX); FLDI(3, EFactorY)
   FDV(1, 2); FDV(1, 3)
   TypeForm(2, 1, 0)
   ]