C*******************************************************************************

      SUBROUTINE  CURSOR_EDIT (TTY_NAME, IOCHAN, ECHO,
     +                         POSITION_TABLE, NUM_FIELDS, ORIENTATION,
     +                         KEY_PRESSED, FIELD, STATUS)

C*******************************************************************************
C
C PROGRAM TITLE:
C
C	Move Cursor among Different Fields on Screen.
C
C PROJECT:
C	GENERAL ELECTRIC COMPANY, LANDSAT-D GROUND SEGMENT
C
C GROUP:
C	FACILITY - IGF
C	SUBSYSTEM - TIPS
C	PACKAGE ID - TAG
C
C PROGRAMMED BY: ALEX MEASDAY				DATE: 16-JUN-1984
C
C CREATION ENVIRONMENT:
C	COMPUTER SYSTEM: VAX 11/780
C	OPERATING SYSTEM: VMS, VERSION 2.2
C
C PURPOSE:
C
C          Subroutine CURSOR_EDIT is a utility routine designed for use
C      in programs that require interactive screen editing. The calling
C      routine displays the screen of data and calls CURSOR_EDIT, passing
C      a table of field positions to the subroutine. CURSOR_EDIT uses
C      the table to position the cursor to various fields on the screen
C      under the direction of the user (via cursor control keys).
C      A non-cursor control key causes exit from the CURSOR_EDIT, which
C      returns the key to the calling routine. Example usage in a calling
C      routine might be as follows:
C
C            field := first data field
C            DO WHILE key_pressed <> exit_key
C                Display screen
C                CALL CURSOR_EDIT (position_table, key_pressed, field)
C                IF key_pressed = change_key THEN
C                    change data item located at field
C                ENDIF
C            ENDDO
C
C          The movement of the cursor on the screen is governed by the
C      position table that is passed to CURSOR_EDIT. The table is
C      dimensioned as POSITION_TABLE(3,NUM_FIELDS). For field i,
C      POSITION_TABLE[1,i] is the row (1..24) on the screen of the data
C      field; POSITION_TABLE[2,i] is the column (1..132) on the screen
C      of the data field; and POSITION_TABLE[3,i] indicates (.TRUE. or
C      .FALSE.) whether this data field is the last one in its logical
C      column or row (all fields in a logical column need not have the
C      same physical screen column).
C
C            The position table can be set up with two orientations,
C      "by row" and "by column". The "by row" orientation is useful in
C      situations where there is a variable amount of data; i.e., dumping
C      and editing a variable number of bytes from a file. The end of row
C      indications (POSITION_TABLE[3,i]) for 22 data items might be set
C      up as follows, where 0 (.FALSE.) indicates a data item and -1
C      (.TRUE.) indicates a data item at the end of the logical row:
C
C            0  0  0  0  0  -1
C            0  0  0  0  0  -1
C            0  0  0  0  0  -1
C            0  0  0  -1
C
C      In "by row" editing, cursors left and right move to the previous
C      and succeeding entries, respectively, in the position table.
C      Cursors up and down move to the previous and succeeding logical
C      rows, respectively, in the position table.
C
C          The "by column" orientation is useful in situations where
C      there is a fixed amount of data; i.e., editing a certain piece
C      of data for each of the 100 TM detectors. The end of column
C      indications (POSITION_TABLE[3,i]) for 23 data items might be
C      set up as follows, where 0 (.FALSE.) indicates a data item and
C      -1 (.TRUE.) indicates a data item at the end of the logical
C      column:
C
C            0  0  0  0  0
C            0  0  0  0  0
C            0  0  0 -1  0
C            0  0 -1     0
C           -1 -1        0
C                       -1
C
C      In "by column" editing, cursors left and right move to the
C      previous and succeeding logical columns, respectively, in the
C      position table. Cursors up and down move to the previous and
C      succeeding entries, respectively, in the position table.
C
C
C CALLING SEQUENCE:
C	[NAME]		[ARG-TYP - I,I/O,O]		[DESCRIPTION]
C
C	TTY_NAME		C*(*)	I	Terminal name (if it needs to be assigned).
C
C	IOCHAN			I*4	I/O	I/O channel number; if zero, GET assigns the terminal.
C
C	ECHO			L	I	If true, then echo characters as they are typed.
C						If false, then don't echo characters as they are typed.
C
C	POSITION_TABLE		I*4	I	Array of cursor positions (see Purpose above).
C
C	NUM_FIELDS		I*4	I	Number of entries in the position table.
C
C	ORIENTATION		I*4	I	Logical interpretation of position table, where
C						    0 = By column.
C						    1 = By row.
C						See Purpose above for "THE MEANING".
C
C	KEY_PRESSED		C*(*)	O	Most recent key pressed that caused exit from CURSOR_EDIT.
C
C	FIELD			I*4	O	Index in the position table of the data field at which
C						the cursor was positioned upon exit from CURSOR_EDIT.
C
C	STATUS			I*4	O	System status.
C
C
C INPUTS/OUTPUTS:
C	NAME - NAMED COMMON,		PARAMETER		DESCRIPTION
C	GLOBAL COMMON, OR FILE NAME	  NAME
C	***************************	*********		***********
C
C	FILES -
C
C            TTY_NAME                   IOCHAN                 Terminal to be read.
C
C
C ERROR HANDLING:
C
C      Errors assigning or reading the terminal cause a message to be
C      written to the user's terminal (SYS$OUTPUT) and an error status
C      to be returned to the calling program.
C
C ASSUMPTIONS AND RESTRICTIONS:
C
C      See purpose above.
C
C*******************************************************************************


      IMPLICIT  NONE

C...      Subroutine arguments.

      CHARACTER*(*)  KEY_PRESSED, TTY_NAME
      INTEGER*4  FIELD, IOCHAN, NUM_FIELDS, ORIENTATION, STATUS
      INTEGER*4  POSITION_TABLE(3,NUM_FIELDS)
      LOGICAL  ECHO

C...      Parameters and external definitions.

      INCLUDE 'ALEX$LIB:VTERM.INC'
      PARAMETER  MAX_NUM_COLUMNS = 132
      PARAMETER  ROW = 1, COLUMN = 2, END_OF_COLUMN = 3, END_OF_ROW = 3
      PARAMETER  BY_COLUMN = 0, BY_ROW = 1

C...      Local variables.

      CHARACTER*8  BUFFER
      INTEGER*4  I
      INTEGER*4  LAST_COLUMN, LOGICAL_COLUMN, NUM_LOGICAL_COLUMNS
      INTEGER*4  LAST_ROW, LOGICAL_ROW, NUM_LOGICAL_ROWS
      INTEGER*4  SCREEN_COLUMN, SCREEN_ROW
      INTEGER*4  START_OF(MAX_NUM_COLUMNS)





      STATUS = 1

C...      Build the start-of-column/row table. START_OF[i] is the
C      index in the position table of the first data field in logical
C      column/row i. Also, determine the logical column/row into which
C      the initial cursor position falls.

      NUM_LOGICAL_COLUMNS = 1
      START_OF(1) = 1
      LOGICAL_COLUMN = 1
      DO I = 1, NUM_FIELDS - 1
          IF (POSITION_TABLE(END_OF_COLUMN,I)) THEN
              NUM_LOGICAL_COLUMNS = NUM_LOGICAL_COLUMNS + 1
              START_OF(NUM_LOGICAL_COLUMNS) = I + 1
              IF (FIELD .GT. I)  LOGICAL_COLUMN = NUM_LOGICAL_COLUMNS
          ENDIF
      ENDDO
      LOGICAL_ROW = LOGICAL_COLUMN
      NUM_LOGICAL_ROWS = NUM_LOGICAL_COLUMNS



C...      Move the cursor around among the data fields until a
C      non-cursor control key is entered.

      LAST_ROW = POSITION_TABLE(ROW,FIELD)
      LAST_COLUMN = POSITION_TABLE(COLUMN,FIELD)
      KEY_PRESSED = CHAR(0)

      DO WHILE (((KEY_PRESSED .EQ. CURSOR_UP) .OR.
     +           (KEY_PRESSED .EQ. CURSOR_DOWN) .OR.
     +           (KEY_PRESSED .EQ. CURSOR_LEFT) .OR.
     +           (KEY_PRESSED .EQ. CURSOR_RIGHT) .OR.
     +           (KEY_PRESSED .EQ. CHAR(0))) .AND. STATUS)


C...      Position the cursor.

          SCREEN_ROW = POSITION_TABLE(ROW,FIELD)
          SCREEN_COLUMN = POSITION_TABLE(COLUMN,FIELD)
          CALL LIB$SET_CURSOR (SCREEN_ROW, SCREEN_COLUMN)


C...      Input a key from the user.

          CALL GET (TTY_NAME, IOCHAN, ECHO, KEY_PRESSED, 1, STATUS)
          IF (.NOT. STATUS)  RETURN

          CALL STR$UPCASE (KEY_PRESSED, KEY_PRESSED)

          IF (KEY_PRESSED .EQ. ESC) THEN     ! Escape sequence?
              BUFFER = KEY_PRESSED
              CALL GETFUNC (TTY_NAME, IOCHAN, ECHO,
     +                      BUFFER, 0, KEY_PRESSED, STATUS)
              IF (.NOT. STATUS)  RETURN
              ! ... N O T E :  Do not "upper-case" function key pseudo-characters. ...
          ENDIF


C...      Move cursor up.

          IF (KEY_PRESSED .EQ. CURSOR_UP) THEN

              IF (ORIENTATION .EQ. BY_COLUMN) THEN	! Move to the previous entry in the table.

                  FIELD = MAX (FIELD - 1, 1)
                  LAST_ROW = POSITION_TABLE(ROW,FIELD)
                  IF (POSITION_TABLE(END_OF_COLUMN,FIELD)) THEN
                      LOGICAL_COLUMN = MAX (LOGICAL_COLUMN - 1, 1)
                  ENDIF

              ELSEIF (ORIENTATION .EQ. BY_ROW) THEN	! Move to the previous logical row.

                  IF (LOGICAL_ROW .GT. 1) THEN
                      LOGICAL_ROW = LOGICAL_ROW - 1
                      FIELD = START_OF(LOGICAL_ROW)
                      DO WHILE				! Preserve logical columns over shorter rows.
     +                  ((POSITION_TABLE(COLUMN,FIELD) .LT. LAST_COLUMN)
     +                   .AND.
     +                   (.NOT. POSITION_TABLE(END_OF_ROW,FIELD)))
                          FIELD = FIELD + 1
                      ENDDO
                  ELSE
                      FIELD = 1
                      LAST_COLUMN = POSITION_TABLE(COLUMN,FIELD)
                  ENDIF

              ENDIF


C...      Move cursor down.

          ELSEIF (KEY_PRESSED .EQ. CURSOR_DOWN) THEN

              IF (ORIENTATION .EQ. BY_COLUMN) THEN	! Move to the succeeding entry in the table.

                  IF (POSITION_TABLE(END_OF_COLUMN,FIELD)) THEN
                      LOGICAL_COLUMN = MIN (LOGICAL_COLUMN + 1,
     +                                      NUM_LOGICAL_COLUMNS)
                  ENDIF
                  FIELD = MIN (FIELD + 1, NUM_FIELDS)
                  LAST_ROW = POSITION_TABLE(ROW,FIELD)

              ELSEIF (ORIENTATION .EQ. BY_ROW) THEN	! Move to the succeeding logical row.

                  IF (LOGICAL_ROW .LT. NUM_LOGICAL_ROWS) THEN
                      LOGICAL_ROW = LOGICAL_ROW + 1
                      FIELD = START_OF(LOGICAL_ROW)
                      DO WHILE				! Preserve logical columns over shorter rows.
     +                  ((POSITION_TABLE(COLUMN,FIELD) .LT. LAST_COLUMN)
     +                   .AND.
     +                   (.NOT. POSITION_TABLE(END_OF_ROW,FIELD)))
                          FIELD = FIELD + 1
                      ENDDO
                  ELSE
                      FIELD = NUM_FIELDS
                      LAST_COLUMN = POSITION_TABLE(COLUMN,FIELD)
                  ENDIF

              ENDIF


C...      Move cursor left.

          ELSEIF (KEY_PRESSED .EQ. CURSOR_LEFT) THEN

              IF (ORIENTATION .EQ. BY_COLUMN) THEN	! Move to the previous logical column.

                  IF (LOGICAL_COLUMN .GT. 1) THEN
                      LOGICAL_COLUMN = LOGICAL_COLUMN - 1
                      FIELD = START_OF(LOGICAL_COLUMN)
                      DO WHILE				! Preserve logical rows over shorter columns.
     +                    ((POSITION_TABLE(ROW,FIELD) .LT. LAST_ROW)
     +                     .AND.
     +                     (.NOT. POSITION_TABLE(END_OF_COLUMN,FIELD)))
                          FIELD = FIELD + 1
                      ENDDO
                  ELSE
                      FIELD = 1
                      LAST_ROW = POSITION_TABLE(ROW,FIELD)
                  ENDIF

              ELSEIF (ORIENTATION .EQ. BY_ROW) THEN	! Move to the previous entry in the table.

                  FIELD = MAX (FIELD - 1, 1)
                  LAST_COLUMN = POSITION_TABLE(COLUMN,FIELD)
                  IF (POSITION_TABLE(END_OF_ROW,FIELD)) THEN
                      LOGICAL_ROW = MAX (LOGICAL_ROW - 1, 1)
                  ENDIF

              ENDIF


C...      Move cursor right.

          ELSEIF (KEY_PRESSED .EQ. CURSOR_RIGHT) THEN

              IF (ORIENTATION .EQ. BY_COLUMN) THEN	! Move to the succeeding logical column.

                  IF (LOGICAL_COLUMN .LT. NUM_LOGICAL_COLUMNS) THEN
                      LOGICAL_COLUMN = LOGICAL_COLUMN + 1
                      FIELD = START_OF(LOGICAL_COLUMN)
                      DO WHILE				! Preserve logical rows over shorter columns.
     +                    ((POSITION_TABLE(ROW,FIELD) .LT. LAST_ROW)
     +                     .AND.
     +                     (.NOT. POSITION_TABLE(END_OF_COLUMN,FIELD)))
                          FIELD = FIELD + 1
                      ENDDO
                  ELSE
                      FIELD = NUM_FIELDS
                      LAST_ROW = POSITION_TABLE(ROW,FIELD)
                  ENDIF

              ELSEIF (ORIENTATION .EQ. BY_ROW) THEN	! Move to the succeeding entry in the table.

                  IF (POSITION_TABLE(END_OF_ROW,FIELD)) THEN
                      LOGICAL_ROW = MIN (LOGICAL_ROW + 1,
     +                                   NUM_LOGICAL_ROWS)
                  ENDIF
                  FIELD = MIN (FIELD + 1, NUM_FIELDS)
                  LAST_COLUMN = POSITION_TABLE(COLUMN,FIELD)

              ENDIF

          ENDIF


      ENDDO     ! Until non-cursor control character entered.


      RETURN


      END
