/****************************************************************************
    USERFLD.C   This file shows how to create an user defined field type
    to use with the Fields2 function.

    We'll create a field that displays one of several predefined strings.
    The user can select one by using the UP and DOWN arrow keys or cliking
    the mouse at two small arrows. To create an user defined field we need
    to create the function that will handle the new field type. This
    function must follow the following requirements:

    1) Handle the following messages:

        Message         Action

        RESET       -   Reset all internal variables to start-up conditions.
        DRAW        -   Draw itself
        ACTIVE      -   Get user input
        CHECK_MOUSE -   Check if a mouse event has ocuured inside field
                        area.

    2) Return the following messages:

        Message         Action

        OK          -   Action requested was succesfully performed.
        EXIT_KEY    -   A key defined as an exit key was pressed.
        ILLEGAL_KEY -   An unknown key was pressed.
        MOUSE_EVENT -   A mouse event not related to this function has
                        ocurred.
        MY_MOUSE    -   A CHECK_MOUSE message is answered with this message
                        if the mouse event is related to this function.
                        Fields will give control to this function when a
                        MY_MOUSE event is returned.

        NEW_POSITION-   the user has moved to a new position or selected
                        a new element. (This one is not required but it
                        is highly recomended).

    3) All local data (color, position, etc) must be stored in a structure.
       A pointer to this structure will be passed as parameter whenever the
       function is called. Take a look at any SCL1 dialog function for an
       example. The use of a structure to hold local data makes possible
       for a single LineEditor struct to handle all LineEditors fields in
       a data-entry screen for example.

    4) The follwing parameters are passed each time the function is called:

        Message (int)   one of the messages described above in #1.

        st (void *)     pointer to the field related structure.

        fd3 (FData3 *)  pointer to a FData3 struct. This structure has
                        the following format:

            typedef struct{
                int EventInfo;
                int (* cdecl UserField)();
                }FData3;

    5) When called the function must:

        1) service the message received
        2) In case of an illegal or exit key copy the key's SCAN/ASCII code
           to the EventInfo element of the FData3 struct.
        3) return the correct message.

    Before calling Fields2 you must properly initialize the user defined and 
    the FData3 structure. In the case of the FData3 struct only the UserField 
    element needs to be initialized (points to the function that will handle
    the user defined field type).

****************************************************************************/

#include <scl1.h>
#include <scl1keys.h>

/* function protoype of our new field handler */

int SelectString(int Message,void *st,FData3 *fd3);

    /* new field data */

char *MyText[]={
    "111111111111111111",
    "222222222222222222",
    "333333333333333333",
    0,
    };

typedef struct{     /* our new field structure */
    int Color;
    int Row;
    int Col;
    int Position;
    int Items;
    int Lenght;
    char **Text;
    }SSData;

    /* initialize our field struct */

SSData ssd={7,7,13,0,3,0,MyText};

    /* a mouse button struct */

MBData mb={7,112,9,13,9,18,9,13,"< OK >",0,0,0,0};

    /* our FData3 struct. It is very important to initialize this struct
       so that the UserField element points to the function that handles
       the new field type */

FData3 ssfd3={0,SelectString};

    /* FData1 struct */

FData1 fd1[]={

    /* First field is our used defined field, the SSData pointer
       is used as Structure1 element and the FData3 pointer as
       Structure2 element

       We'll use SCL1's default FieldCheck function */

    USER_DEFINED,&ssd,&ssfd3,FieldCheck,
    MOUSE_BUTTON,&mb,0,FieldCheck,
    0};

main()
{
FData2 fd2;

Cls(7,CLS_ALL);                 /* Clear screen */
InitMouse(IM_SHOW);             /* initialize mouse */
Fields2(F_INIT,fd1,&fd2);       /* init fields */
Fields2(F_DRAW,fd1,&fd2);       /* draw */
Fields2(F_ACTIVE,fd1,&fd2);     /* give control */
}

/* our new field type. Here we'll send received messages for processing */

int SelectString(int Message,void *st,FData3 *fd3)
{
SSData *ssd;

ssd=(SSData *)st;

    /* you could define messages for your field, but the value assigned
      to each message must equal those assigned to regular dialog
      functions. In our case we'll use LineEditor messages */

switch(Message)
    {
    case LE_DRAW:
        DrawUF(ssd);
        return(LE_OK);
    case LE_ACTIVE:
        return(ActiveUF(ssd,fd3));
    case LE_CHECK_MOUSE:
        return(CheckUFMouse(ssd,fd3));
    case LE_RESET:
        ssd->Position=0;        /* reset position to 0 */
        return(LE_OK);
    }
return(LE_OK);
}

/* draws our field to the screen and initializes several elements of our
   struct */

int DrawUF(SSData *ssd)
{
int i=0,j,maxlenght=0;

while(ssd->Text[i] != 0)        /* count the number of items */
    {
    j=strlen(ssd->Text[i]);     /* save the largest element size */
    if(j > maxlenght)
        maxlenght=j;
    ++i;
    }
ssd->Items=i;                   /* save in struct */
ssd->Lenght=maxlenght;

    /* write string */

WriteScreenLen(ssd->Color,ssd->Row,ssd->Col+2,maxlenght,ssd->Text[ssd->Position]);

    /* draw up and down arrows */

WriteChar(ssd->Color,ssd->Row,ssd->Col,1,24);
WriteChar(ssd->Color,ssd->Row,ssd->Col+maxlenght+3,1,25);
}

    /* This functions is called when our field is active */

ActiveUF(SSData *ssd,FData3 *fd3)
{
unsigned int key;

SetCurPos(ssd->Row,ssd->Col+2);         /* position cursor */
do
    {
    if(MSE_LPress)                      /* mouse event? */
        {
        if(CheckUFMouse(ssd)==LE_MY_MOUSE)  /* our mouse? */
            {
            if(MSE_LpX==ssd->Col)       /* clicked at the up arrow key */
                {
                WaitTime(25);
                goto UpKey;
                }
                
                /* clicked at the down arrow key */

            else if(MSE_LpX==ssd->Col+ssd->Lenght+3)
                {
                WaitTime(25);
                goto DownKey;
                }
            }

        else

          /* clicked outside our area, return a MOUSE_EVENT message */

            return(LE_MOUSE_EVENT);
        }

    if(KeyReady())      /* check keystrokes */
        {
        key=GetKey();
        switch(key)
            {
            case UP:
UpKey:
                if(ssd->Position > 0)
                    --ssd->Position;        /* change position */
                else
                    ssd->Position=ssd->Items-1;
                DrawUF(ssd);                /* update screen */
                return(LE_NEW_POSITION);
            case DOWN:
DownKey:
                if(ssd->Position + 1 < ssd->Items)

                    ++ssd->Position;        /* change position */
                else
                    ssd->Position=0;
                DrawUF(ssd);                /* update screen */
                return(LE_NEW_POSITION);
            case TAB:
            case SHIFTTAB:                  /* handle exit keys */
            case ESC:
                fd3->EventInfo=key;         /* save key value */
                return(LE_EXIT_KEY);
            default:                        /* handle unknown keys */
                fd3->EventInfo=key;         /* save key value */
                return(LE_ILLEGAL_KEY);
            }
        }
    }while(1);
}


    /* this function checks if the mouse has been clicked in our area */

CheckUFMouse(SSData *ssd)
{
if(MSE_LpY==ssd->Row && MSE_LpX >= ssd->Col && MSE_LpX <= ssd->Col+ssd->Lenght+3)

    /* return MY_MOUSE if YES */

    return(LE_MY_MOUSE);

else
    return(LE_MOUSE_EVENT);

}

