/*
** Listing 4. 
**
** The new keyboard handling function
** with more useful text validation.
*/

/*
** Some keycodes:
*/

#define ESCAPE      0x011B
#define BACKSPACE   0x0E08
#define TAB         0x0F09
#define RETURN      0x1C0D
#define UP          0x4800
#define SHFTUP      0x4838
#define LEFT        0x4B00
#define SHFTLF      0x4B34
#define RIGHT       0x4D00
#define SHFTRT      0x4D36
#define DOWN        0x5000
#define SHFTDN      0x5032
#define DELETE      0x537F
#define ENTER       0x720D

#define V_MAGIC     0x31415926

/*
** Function prototypes.
*/

int xform_keybd(OBJECT *,short,short,short *,short *,short *);
short form_valid(char *,short,short *);

short  (* _validate)(short *, char *, short *) = NULL;
int _valmagic = 0;


/*
** Define the large table of character properties
** used to determine character validity. Note that
** 0 is not checked because it is never valid.
** The validation codes are specified as a bitmap in
** the following format: (bit set -> character acceptable)
**   MSB  ..xpnaXTPNMHFBA9  LSB  (. unused bit)
*/

short _valtab[] = {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, /*  1- 8  */
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, /*  9-16  */
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, /* 17-24  */
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x2E66, /* 25-32  */
0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, /* ! to ( */
0x2200, 0x3208, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x3BF9, /* ) to 0 */
0x3BF9, 0x3BF9, 0x3BF9, 0x3BF9, 0x3BF9, 0x3BF9, 0x3BF9, 0x3BF9, /* 1 to 8 */
0x3BF9, 0x3288, 0x2200, 0x2200, 0x2200, 0x2200, 0x3208, 0x2200, /* 9 to @ */
0x3FFE, 0x3FFE, 0x3FFE, 0x3FFE, 0x3FFE, 0x3FFE, 0x3FEE, 0x3FEE, /* A to H */
0x3FEE, 0x3FEE, 0x3FEE, 0x3FEE, 0x3FEE, 0x3FEE, 0x3FEE, 0x3FEE, /* I to P */
0x3FEE, 0x3FEE, 0x3FEE, 0x3FEE, 0x3FEE, 0x3FEE, 0x3FEE, 0x3FEE, /* Q to X */
0x3FEE, 0x3FEE, 0x2200, 0x3280, 0x2200, 0x2200, 0x1B88, 0x2200, /* Y to ` */
0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, /* a to h */
0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, /* i to p */
0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, 0x2E00, /* q to x */
0x2E00, 0x2E00, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200          /* to $7F */
                  };


/*
** Function to process the result of a keypress.
** The arguments are a pointer to the tree, the index
** of the current editable object, the keyboard shift
** state, a pointer to the index of the next object to
** go to after this, a pointer to the 2 unsigned char keycode of
** the key pressed and a pointer to the character index
** within the editable object.  The function may alter
** 'next' (for moving the edit cursor around), 'key'
** (to force certain key interpretations, 0 if no work
** to be done) and 'index' (for moving about in the line).
** The function returns FALSE if the edit is over, TRUE
** if not.
**
** Usage:   result = xform_keybd(tree,edobj,shift,next,key,index);
*/

int xform_keybd(tree,edobj,shift,next,key,index)

register OBJECT *tree;
short edobj;
register short shift;
register short *next, *key, *index;

{
    short new_key;
    char temp;
    char *text;

    if (*key == TAB)
        *key = (shift & 0x03 ? UP : DOWN);
    if (edobj == -1 && *key != ENTER && *key != RETURN)
        return (TRUE);
    switch (*key)
        {
        case DELETE: case BACKSPACE: case ESCAPE:
        case RIGHT:  case LEFT:      case UP:
        case DOWN:   case ENTER:
        case RETURN:    return(form_keybd(tree,0,edobj,*key,next,key));

        case SHFTRT:    objc_edit(tree,edobj,0,index,EDEND);
                        objc_edit(tree,edobj,0,index,EDINIT);
                        break;

        case SHFTLF:    objc_edit(tree,edobj,0,index,EDEND);
                        *index = 0;
                        objc_edit(tree,edobj,0,index,EDEND);
                        break;

        default:        text = ((TEDINFO *)(tree+edobj)->ob_spec)->te_pvalid + *index;
                        if (*text == '\0')
                            text--;
                        if (new_key = form_valid(text,*key,&edobj))
                            {
                            temp = *text;
                            *text = 'X';
                            objc_edit(tree,edobj,new_key,index,ED_CHAR);
                            *text = temp;
                            }
                        else
                            return(form_keybd(tree,0,edobj,*key,next,key));
                        break;
        }
    *key = 0;
    return (TRUE);
}


/*
** Function to determine whether the key pressed
** by the user is valid for this position in the
** field.  The function accepts a pointer to the
** current position in the validation string, the
** proposed character and a pointer to the index
** of the object being edited. It returns 0 if the
** character is to be ignored, or a character code
** (possibly modified) to be used.
**
** Usage:   result = form_valid(text,keypress,obj);
**
**          char result, form_valid();
**          char *text;
**          short keypress;
**          short *obj;
*/

short form_valid(text,keypress,obj)

register char *text;
short keypress;
short *obj;

{
    register short mask;

    mask = 0;
    keypress &= 0xFF;
    if (keypress == '\0' || keypress > 127 && *text != 'X')
        return (0);
    switch (*text)
        {
        case '9':   mask = 0x0001;
                    break;
        case 'A':   mask = 0x0002;
                    break;
        case 'B':   mask = 0x0004;
                    keypress = toupper(keypress);
                    break;
        case 'F':   mask = 0x0008;
                    break;
        case 'H':   mask = 0x0010;
                    keypress = toupper(keypress);
                    break;
        case 'M':   mask = 0x0020;
                    keypress = toupper(keypress);
                    break;
        case 'N':   mask = 0x0040;
                    break;
        case 'P':   mask = 0x0080;
                    keypress = toupper(keypress);
                    break;
        case 'T':   mask = 0x0100;
                    keypress = toupper(keypress);
                    break;
        case 'X':   mask = 0x0200;
                    break;
        case 'a':   mask = 0x0400;
                    break;
        case 'n':   mask = 0x0800;
                    break;
        case 'p':   mask = 0x1000;
                    keypress = toupper(keypress);
                    break;
        case 'x':   mask = 0x2000;
                    break;
        default:    if (_valmagic == V_MAGIC && _validate != NULL)
                        return ( (* _validate)(obj,text,&keypress));
                    return (0);
        }
    if (_valtab[keypress-1] & mask)
        return (keypress);
    return (0);
}

