############################################################################
#
#	File:     keypunch.icn
#
#	Subject:  Program to simulate a keypunch
#
#	Author:   Gregg M. Townsend
#
#	Date:     February 7, 1995
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#     In the olden days, computer data was encoded by rectangular holes
#  punched in thin pieces of cardboard about the size of an old dollar.
#  This program simulates a "keypunch", a mechanical device for punching
#  those holes.  (Keypunches themselves were programmable, but there's
#  no way to program this one; tab stops are set permanently.)
#
#     A carriage return feeds a new card.  Illegal characters punch a
#  lace column.  As with a real keypunch, you can backspace, but the
#  holes don't go away.
#
#     The shift key turns "UIOJKLM<>" into "123456789".  The meta key
#  serves (imperfectly) as the multipunch key.
#
#     The font was chosen on a Sun workstation and may not be portable.
#
############################################################################
#
#  Requires:  Version 9 graphics
#
############################################################################
#
#  Links: options, optwindw, graphics
#
############################################################################


link options
link optwindw
link graphics


global hsiz, vsiz, hsep, vsep, tsep, bsep, lsep, rsep

procedure main(args)
   local win, col, card, c, s, opts

   opts := options(args, winoptions())

   s := ""
   while s ||:= get(args) || " "

   hsiz := 5
   vsiz := 12
   hsep := 3
   vsep := 12
   tsep := 20
   bsep := 20
   lsep := 20
   rsep := 20

   /opts["B"] := "pale moderate reddish yellow"
   /opts["W"] := lsep + 80 * hsiz + 79 * hsep + rsep
   /opts["H"] := tsep + 12 * vsiz + 11 * vsep + bsep
   win := optwindow(opts)
   card := WOpen("canvas=hidden", "width="||opts["W"], "height="||opts["H"])

   Font(win, "-misc-fixed-medium-r-semi*--13-120-*")
   initcard(win)
   CopyArea(win, card)

   col := 1
   every c := !map(s, &lcase, &ucase) | keyevent(win) do {
      if upto('\^c\^d\d', c) then
         exit()
      else if upto('\n\r\^u', c) then {
         CopyArea(card, win)
         col := 1
         }
      else if c == '\b' then {
         if (col -:= 1) < 1 then
            col := 1
         key(win, col, " ")
         }
      else if c == '\t' then {
         col := col + 10 - (col - 1) % 10
         if col > 80 then
            col := 80
         }
      else {
         key(win, col, map(c, &lcase, &ucase))
         if ((not &meta) & (col +:= 1)) > 80 then
            col := 80
         }
      GotoXY(win, lsep + col * (hsiz + hsep), tsep / 2)
      }
end


procedure keyevent(win)
   local e
   repeat {
      e := Event(win)
      if type(e) == "string" then {
         if &shift | &meta then
            suspend map(e, "uiojklm,.UIOJKLM<>", "123456789123456789")
         else
            suspend map(e, &lcase, &ucase)
         }
      }
end


procedure initcard(win)
   local i, c

   EraseArea(win)
   GotoXY(win, lsep, tsep / 2)
   every i := 12 to 3 by -1 do {
      c := "  0123456789"[i]
      every punch(win, 1 to 80, i, c)
      }
end


procedure key(win, col, ch)
   Fg(win, "black")
   every punch(win, col, holes(ch))
   punch(win, col, 0, ch)
end


procedure punch(win, col, row, ch)
   local x, y, w, h
   x := lsep + (col - 1) * (hsiz + hsep)
   if row = 0 then
      y := 0
   else
      y := tsep + (row - 1) * (vsiz + vsep)
   if \ch then
      DrawString(win, x, y + vsiz - 3, ch)
   else
      FillRectangle(win, x, y, hsiz, vsiz)
end


# Hole codes from CDC SCOPE 3.4 SPRM, Rev. A, 10-15-71, page A-4 (026 encoding).

procedure holes(c)
   static s0, s1, s2, s3, n
   initial {
      s0 := " 0123456789+ABCDEFGHI-JKLMNOPQR/STUVWXYZ:=@%'[.)^;]$*@?>!,(_#&\"\\"
      s1 := "           AAAAAAAAAABBBBBBBBBB000000000     AAAAABBBBBB000000 A"
      s2 := " 0123456789 123456789 123456789123456789235672346723456723456745"
      s3 := "                                        888888888888888888888888"
      }
   if n := find(c, s0) then
      suspend find((s1 | s2 | s3)[n], "AB0123456789")
   else
      suspend 1 to 12
end
