GetLineStack

From Pickwiki
Jump to navigationJump to search

Back to BasicSource

SUBROUTINE GET.LINE(X,XXLENGTH,DISP.LEN,XXDATA,RTN.CHAR)
************************************************************************
* Program: GET.LINE.STACK
* Author : Ian [[McGowan]]
* Date   : 6/24/89
* Edited : 16:49:22 Nov 19 1998 By MCGOWAN 
* Comment: Get a line of data with editing keys
************************************************************************
* Date     By   Desc
* -------- ---- ----------------------------------------------------
*10/02/95* IAN  Attempt to fix 'turd' bug -- JIM
*10/02/95  IAN  Add CTRL-B for back -- JIM
*09/14/95  IAN  Add CTRL-W for delete word -- JIM
*01/02/94  IAN  Trap HOME and END key, Also add del word and line -- JIM
*01/25/94  IAN  CTRL-D exits -- JIM
*08/22/97  IAN  Add wyse support, make more emacs like
************************************************************************
* X           = X POS
* XXLENGTH    = MAX ALLOWED LENGTH
* DISP.LEN    = MAX DISPLAYED XXLENGTH
* XXDATA      = ON INPUT  VARIABLE XXDATA
*             = ON OUTPUT RETURNED STRING
* RTN.CHAR    = SEQ(CHAR PRESSED TO EXIT)

* Important globals
* CP          = Cursor Position, Y coordinate on the screen 0 -> DISP.LEN
* CH.PTR      = Pointer into string being edited            1 -> XXLENGTH
* POS         = Pointer to first char currently displayed   1 -> XXLENGTH
* ASC.CH      = The numeric value of the key just entered

INIT:
   EQU INSERT TO '1',REPLACE TO '-1',BEEP TO CHAR(7)
   EQU ESC TO CHAR(27),AM TO CHAR(254)
   EQU NUL TO '',TRUE TO 1,FALSE TO 0,SPACE TO ' '
   TERM=UPCASE(GETENV("TERM"))
   IF INDEX(TERM,'WY',1) THEN TERM='W'   
   PROMPT NUL
   ECHO OFF
   MODE = REPLACE ; TEMP.XXDATA =XXDATA
   BASE = @(X) ; MASK = 'L#':DISP.LEN
   PRINT BASE:
   CURR.LEN = LEN(XXDATA)
   GOSUB GO.END
   RTN.CHAR=''

MAIN:
   LOOP
      PRINT @(X+CP):
      CH=IN()
      ASC.CH = SEQ(CH)
      EXIT.FLAG=FALSE
      BEGIN CASE
         CASE ASC.CH = 1
            GOSUB GO.BEGIN
         CASE ASC.CH = 2
            GOSUB LEFT
         CASE ASC.CH = 4
            GOSUB DEL
         CASE ASC.CH = 5
            GOSUB GO.END
         CASE ASC.CH = 6
            GOSUB RIGHT
         CASE ASC.CH = 8 AND TERM='W'
            GOSUB LEFT
         CASE ASC.CH = 8
            GOSUB BACK
         CASE ASC.CH = 9
            GOSUB FORWARD.WORD
         CASE ASC.CH = 10 AND TERM='W'
            RTN.CHAR=2
            EXIT.FLAG=TRUE
         CASE ASC.CH = 10
            GOSUB DEL.TO.END
         CASE ASC.CH=11 AND TERM='W'
            RTN.CHAR=1
            EXIT.FLAG=TRUE
         CASE ASC.CH=12 AND TERM='W'
            GOSUB RIGHT
         CASE ASC.CH = 13
            EXIT.FLAG = TRUE
            RTN.CHAR=13
         CASE ASC.CH = 14
            RTN.CHAR=2
            EXIT.FLAG=TRUE
         CASE ASC.CH = 16
            RTN.CHAR=1
            EXIT.FLAG=TRUE
         CASE ASC.CH = 18
            GOSUB INSRT
         CASE ASC.CH = 23
            GOSUB DELETE.WORD
         CASE ASC.CH = 24
            GOSUB FORWARD.WORD
         CASE ASC.CH = 25
            XXDATA = ''
            EXIT.FLAG=TRUE
            RTN.CHAR=13
         CASE ASC.CH = 26
            GOSUB BACK.WORD
         CASE ASC.CH = 27
            GOSUB ESC.KEY
         CASE ASC.CH < 27
            PRINT @(0):ASC.CH:
         CASE ASC.CH = 127
            GOSUB BACK
         CASE 1
            GOSUB ORD
      END CASE
      CURR.LEN = LEN(XXDATA)
   UNTIL EXIT.FLAG DO
   REPEAT
   IF XXDATA[CURR.LEN,1] = SPACE THEN XXDATA = XXDATA[1,CURR.LEN-1]
   ECHO ON ; PRINT BASE:XXDATA MASK
RETURN

ORD:
   * Ordinary key pressed
   IF CH.PTR # XXLENGTH+1 THEN
      IF MODE = INSERT THEN
         IF CURR.LEN = XXLENGTH THEN
            PRINT BEEP:
            GOTO SKIP1
         END ELSE
            XXDATA = XXDATA[1,CH.PTR-1]:CH:XXDATA[CH.PTR,CURR.LEN]
         END
      END ELSE
         XXDATA = XXDATA[1,CH.PTR-1]:CH:XXDATA[CH.PTR+1,CURR.LEN]
      END
      CH.PTR = CH.PTR + 1
      IF CP # DISP.LEN THEN
         PRINT @(X+CP):CH:
         IF MODE = INSERT THEN
            PRINT XXDATA[CH.PTR,DISP.LEN-CP-1]:
         END
         CP = CP + 1
      END ELSE
         POS = POS + 1
         PRINT BASE:XXDATA[POS,DISP.LEN] MASK:
      END
   END ELSE
      PRINT BEEP:
   END
SKIP1:
RETURN

RIGHT:
* There are 3 situations here -
* 1 We're pressing the right arrow thru existing text       (CH.PTR = CURR.LEN)
* 2 We've typed text and are at the end when we press right (CH.PTR > CURR.LEN)
* 3 We're in the middle of text, pressing the right arrow   (CH.PTR < CURR.LEN)
   IF CH.PTR < XXLENGTH THEN
      IF CH.PTR > CURR.LEN THEN PRINT BEEP: ; GOTO SKIP2
      IF CH.PTR = CURR.LEN THEN
         * If the last char is not a space make it one
         IF XXDATA[CURR.LEN,1] # SPACE THEN
            XXDATA = XXDATA:SPACE
            IF CP # DISP.LEN THEN PRINT @(X+CP+1):SPACE:
            CURR.LEN = CURR.LEN + 1
         END ELSE
            PRINT BEEP:
            GOTO SKIP2
         END
      END
      CH.PTR = CH.PTR + 1
      IF CP # DISP.LEN THEN
         * We're not at the end of display so just move the cursor
         CP = CP + 1
      END ELSE
         * We are at the end of the display so leave cursor where
         * it is and scroll through line
         POS = POS + 1
         PRINT BASE:XXDATA[POS,DISP.LEN] MASK:
      END
   END ELSE
      PRINT BEEP:
   END
SKIP2:
RETURN

FORWARD.WORD:
   * Tab key pressed - move forwards a word
   IF CH.PTR >= CURR.LEN THEN
      PRINT BEEP:
   END ELSE
      LOOP
         CH.PTR = CH.PTR + 1
         CP = CP + 1
      UNTIL XXDATA[CH.PTR,1] = SPACE OR CH.PTR = CURR.LEN DO
      REPEAT
      IF CH.PTR # CURR.LEN THEN
         LOOP
            CH.PTR = CH.PTR + 1
            CP = CP + 1
         UNTIL XXDATA[CH.PTR,1] # SPACE OR CH.PTR = CURR.LEN DO
         REPEAT
      END
      IF CP > DISP.LEN THEN
         CP = DISP.LEN
         POS = CH.PTR - DISP.LEN
         PRINT BASE:XXDATA[POS,DISP.LEN] MASK:
      END
   END
RETURN

LEFT:
   * If we're not at the start of data, move left
   IF CH.PTR # 1 THEN
      CH.PTR = CH.PTR - 1
      IF CP # 0 THEN
         * We're not at the start of the display so just move the cursor
         CP = CP - 1
      END ELSE
         * We are at the start of the display so leave cursor and scroll
         POS = POS - 1
         PRINT BASE:XXDATA[POS,DISP.LEN] MASK:
      END
   END ELSE
      PRINT BEEP:
   END
RETURN

DEL:
   * Delete the character at the cursor and redisplay from this point
   XXDATA = XXDATA[1,CH.PTR-1]:XXDATA[CH.PTR+1,CURR.LEN]
   CURR.LEN = CURR.LEN - 1
   PRINT BASE:XXDATA[POS,DISP.LEN] MASK:
RETURN

BACK:

   * Backspace key pressed
   IF CH.PTR # 1 THEN
      CH.PTR = CH.PTR - 1
      XXDATA = XXDATA[1,CH.PTR-1]:XXDATA[CH.PTR+1,CURR.LEN]
      CURR.LEN = CURR.LEN - 1
      IF CP # 0 THEN
         CP = CP - 1
      END ELSE
         POS = POS - 1
      END
      PRINT BASE:XXDATA[POS,DISP.LEN] MASK:
   END ELSE
      PRINT BEEP:
   END
RETURN

INSRT:
   * Toggle between insert and replace modes
   MODE = -MODE
RETURN

ESC.KEY:
   * ESC pressed, or extended key
   * Get next char of extended command
   ALLOW = 0
   EXT.KEY=IN()
   EXT = SEQ(EXT.KEY)
   EXT.KEY = OCONV(EXT.KEY,'MCU')
   BEGIN CASE
      CASE EXT.KEY = 'D'
        GOSUB DELETE.WORD
      CASE EXT.KEY = '[' OR EXT.KEY = 'O'
         EXT.KEY=IN()
         BEGIN CASE
            CASE EXT.KEY = 'C'
               GOSUB RIGHT
            CASE EXT.KEY = 'D'
               GOSUB LEFT
            CASE EXT.KEY = 'A'
               RTN.CHAR=1
               EXIT.FLAG=TRUE
            CASE EXT.KEY = 'B'
               RTN.CHAR=2
               EXIT.FLAG=TRUE
         END CASE
   END CASE
RETURN ; * From ESC key

BACK.WORD:
   * Shift tab pressed - go back a word
   IF CH.PTR = 1 THEN
      PRINT BEEP:
   END ELSE
      * 2 situations - either we're in a word already or
      * we're at the start of a word
      * If in a word - loop to the start of the word
      * otherwise skip spaces, and then move to start of word
      IF XXDATA[CH.PTR-1,1] # SPACE THEN
         LOOP
         UNTIL XXDATA[CH.PTR-1,1] = SPACE OR CH.PTR = 1 DO
            CH.PTR = CH.PTR - 1
            CP = CP - 1
         REPEAT
      END ELSE
         * Skip spaces
         LOOP
         UNTIL XXDATA[CH.PTR-1,1] # SPACE OR CH.PTR = 1 DO
            CH.PTR = CH.PTR - 1
            CP = CP - 1
         REPEAT
         IF CH.PTR > 1 THEN
            * At word end - move to start of word
            LOOP
            UNTIL XXDATA[CH.PTR-1,1] = SPACE OR CH.PTR = 1 DO
               CH.PTR = CH.PTR - 1
               CP = CP - 1
            REPEAT
         END
      END
      IF CP < 0 THEN
         CP = 0
         POS = CH.PTR
         PRINT BASE:XXDATA[POS,DISP.LEN] MASK:
      END
   END
RETURN

DEL.TO.END:
   * Delete from cursor to end of line
   IF CH.PTR = 1 THEN
      XXDATA = ''
      CP = 0
      POS = 1
   END ELSE
      XXDATA = XXDATA[1,CH.PTR-1]
   END
   CURR.LEN = LEN(XXDATA)
   PRINT BASE:XXDATA[POS,DISP.LEN] MASK:
RETURN

DELETE.WORD:
   * Delete to space at right of cursor
   IF CH.PTR >= CURR.LEN THEN
      PRINT BEEP:
   END ELSE
      C = CH.PTR
      LOOP
         C = C + 1
      UNTIL XXDATA[C,1] = SPACE OR C = CURR.LEN DO
      REPEAT
      XXDATA = XXDATA[1,CH.PTR-1]:XXDATA[C+1,CURR.LEN]
      CURR.LEN = CURR.LEN - C + CH.PTR - 1
      PRINT BASE:XXDATA[POS,DISP.LEN] MASK:
   END
RETURN

GO.BEGIN:
   * Go to the start of data and redisplay
   CP = 0
   CH.PTR = 1
   POS = 1
   PRINT BASE:XXDATA MASK:
RETURN

GO.END:
   * Move to the end of data and redisplay
   IF XXDATA[CURR.LEN,1] # SPACE THEN
      XXDATA = XXDATA:SPACE
      CURR.LEN = CURR.LEN + 1
   END
   IF CURR.LEN < DISP.LEN THEN
      CP = CURR.LEN - 1
      POS = 1
   END ELSE
      CP = DISP.LEN - 1
      POS = CURR.LEN - DISP.LEN + 1
   END
   CH.PTR = CURR.LEN
   PRINT BASE:XXDATA[POS,DISP.LEN] MASK:
RETURN