Tuesday 17 March 2020

Simple C Unit Test



#ifndef _CUNITTEST_ENGINE_H_
#define _CUNITTEST_ENGINE_H_
/*
 * File Name: cuteng.h
 * Author: Seree Rakwong
 * Date: 9-NOV-2017
 */
#include <stdio.h>

enum const_operation_enum
{
    kEQ = 0,    /* == */
    kLT,        /* <  */
    kLE,        /* <= */
    kGT,        /* >  */
    kGE,        /* >= */
    kNE,        /* != */
    kAND,       /* && */
    kOR,        /* || */
    kNOT,       /* !  */
    kXOR,       /* ^  */
    kEQS,       /* strcmp == 0 */
    kLTS,       /* strcmp <  0 */
    kGTS,       /* strcmp >  0 */
    kNES        /* strcmp != 0 */
};

typedef long    (*unit_test_fn)(char*);
typedef long    (*unit_test_parms_fn)(char*, void*);

struct unit_test_s
{
    char            alias[64];
    unit_test_fn    func;
    int             operation;
    long            expected;
};
typedef struct unit_test_s unit_test_t;

struct unit_test_parms_s
{
    char*                   alias;
    unit_test_parms_fn      func;
    void*                   args;
    int                     operation;
    long                    expected;
};
typedef struct unit_test_parms_s unit_test_parms_t;

struct results_s
{
    int passed_cases;
    int failed_cases;
};
typedef struct results_s results_t;

void run_test( char *title, unit_test_t* test_cases );
void run_test_params( char *title, unit_test_parms_t* test_cases );
void run_test_to_file( FILE* fp, char *title, unit_test_parms_t* test_cases );
void run_test_to_html( FILE* fp, char *title, unit_test_parms_t* test_cases );

#define BEGIN_UNIT_TEST( test_case, test_type ) \
test_type test_case[] = \
{

#define BEGIN_UNIT_TEST_INT( test_case ) \
unit_test_t test_case[] = \
{

#define ON_UNIT_TEST( alias, func, op, val ) \
    { alias, func, op, val },

#define END_UNIT_TEST() \
    { "", 0, 0, 0L }   /* end of test cases */ \
};

#define BEGIN_UNIT_TEST_PARAMS( test_case ) \
unit_test_parms_t test_case[] = \
{

#define ON_UNIT_TEST_PARAMS( alias, func, args, op, val ) \
    { alias, func, args, op, val },
    

#define END_UNIT_TEST_PARAMS() \
    { "", 0, 0, 0, 0L }   /* end of test cases */ \
};


#define RUN_TEST( alias, test_case ) \
run_test( alias, test_case );

#define RUN_TEST_PARAMS( alias, test_case ) \
run_test_params( alias, test_case );

#define RUN_TEST_FILE( fp, alias, test_case ) \
run_test_to_file( fp, alias, test_case );

#define RUN_TEST_HTML( fp, alias, test_case ) \
run_test_to_html( fp, alias, test_case );


#define BEGIN_RUN_TEST_FILE(fp, fname) \
{ \
    char buffer[64]; \
    if ( fp == NULL ) \
    { \
        sprintf( buffer, "./log/%s", fname ); \
        fp = fopen( buffer, "w+" ); \
    }

#define END_RUN_TEST_FILE(fp) \
    fflush( fp ); \
    fclose( fp ); \
}

#define BEGIN_RUN_TEST_HTML(fp, fname) \
{ \
    char buffer[64]; \
    if ( fp == NULL ) \
    { \
        sprintf( buffer, "./log/%s", fname ); \
        fp = fopen( buffer, "w+" ); \
    } \
    fprintf( fp, "<!DOCTYPE html><html><body>" ); \
    fprintf( fp, "<head><style>" \
                 "#reports {font-family:\"Trebuchet MS\",Arial,Helvetica,sans-serif;border-collapse:collapse;width:100%%;}" \
                 "#reports td,#reports th{border:1px solid #ddd;padding:8px;}" \
                 "#reports tr:nth-child(even){background-color:#f2f2f2;}" \
                 "#reports tr:hover{background-color:#ddd;}" \
                 "#reports th{padding-top:12px;padding-bottom:12px;text-align:center;background-color:#4CAF50;color:white;}" \
                 "#reports th.passed{padding-top:12px;padding-bottom:12px;text-align:center;background-color:#4CAF50;color:white;}" \
                 "#reports th.failed{padding-top:12px;padding-bottom:12px;text-align:center;background-color:#AF4C50;color:white;}" \
                 "</style></head>" ); \
    fflush( fp );

#define END_RUN_TEST_HTML(fp) \
    fprintf( fp, "</body></html>" ); \
    fflush( fp ); \
    fclose( fp ); \
}

void dump_bin( FILE* fp, char* bin, int len );

#endif /* _CUNITTEST_ENGINE_H_ */

/*
 * File Name: cuteng.c
 * Author: Seree Rakwong
 * Date: 9-NOV-2017
 */
#include "cuteng.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* op_to_str[] =
{
    "EQ ",
    "LT ",
    "LE ",
    "GT ",
    "GE ",
    "NE ",
    "AND",
    "OR ",
    "NOT",
    "XOR",
    "EQS",
    "LTS",
    "GTS",
    "NES",
    0
};

void run_test( char *title, unit_test_t* test_cases )
{
    int  i      = 0;
    long rc     = 0;
    long rc2    = 0;
    results_t   results = { 0, 0 };
    char line_break[] = "---------------------------------------------------"
                        "---------------------------------------------------";
    /* title */
    printf( "\n\n" );
    printf( "%s\n", line_break );
    printf( "%*s\n", ( strlen( line_break ) + strlen( title ) ) / 2, title );
    printf( "%s\n", line_break );
    printf( "%4s %-64s %s  %8s %8s %s\n", "No", "Test cases", "Op", "Expected", "Returned", "Result" );
    printf( "%s\n", line_break );
    for ( ; ; ++i )
    {
        if ( !test_cases[i].func )
            break;

        rc = test_cases[i].func( test_cases[i].alias );
        switch ( test_cases[i].operation )
        {
            case kEQ:  rc2 = ( rc == test_cases[i].expected ); break;
            case kLT:  rc2 = ( rc <  test_cases[i].expected ); break;
            case kLE:  rc2 = ( rc <= test_cases[i].expected ); break;
            case kGT:  rc2 = ( rc >  test_cases[i].expected ); break;
            case kGE:  rc2 = ( rc >= test_cases[i].expected ); break;
            case kNE:
            default:   rc2 = ( rc != test_cases[i].expected ); break;
        }

        printf( "%4d %-64s %s %8ld %8ld [ %s ]\n", 
                (i+1),
                test_cases[i].alias,
                op_to_str[test_cases[i].operation], 
                test_cases[i].expected,
                rc,
                ( rc2 == 0 ? "failed" : "PASSED" ) );
        if ( rc2 == 0 )
        {
            ++results.failed_cases;
        }
        else
        {
            ++results.passed_cases;
        }
    }
    printf( "%s\n", line_break );
    printf( "[PASSED: %4d] | [FAILED: %4d]\n", results.passed_cases, results.failed_cases );
    printf( "%s\n", line_break );
}

void run_test_params( char *title, unit_test_parms_t* test_cases )
{
    int  i      = 0;
    long rc     = 0;
    long rc2    = 0;
    char        alias[64];
    results_t   results = { 0, 0 };
    char line_break[] = "---------------------------------------------------"
                        "---------------------------------------------------";
    /* title */
    printf( "\n\n" );
    printf( "%s\n", line_break );
    printf( "%*s\n", ( strlen( line_break ) + strlen( title ) ) / 2, title );
    printf( "%s\n", line_break );
    printf( "%4s %-64s %s  %8s %8s %s\n", "No", "Test cases", "Op", "Expected", "Returned", "Result" );
    printf( "%s\n", line_break );
    for ( ; ; ++i )
    {
        if ( !test_cases[i].func )
            break;

        rc = test_cases[i].func( test_cases[i].alias, test_cases[i].args );
        switch ( test_cases[i].operation )
        {
            case kEQ:  rc2 = ( rc == test_cases[i].expected ); break;
            case kLT:  rc2 = ( rc <  test_cases[i].expected ); break;
            case kLE:  rc2 = ( rc <= test_cases[i].expected ); break;
            case kGT:  rc2 = ( rc >  test_cases[i].expected ); break;
            case kGE:  rc2 = ( rc >= test_cases[i].expected ); break;
            case kNE:
            default:   rc2 = ( rc != test_cases[i].expected ); break;
        }

        strncpy( alias, test_cases[i].alias, 63 );
        printf( "%4d %-64s %s %8ld %8ld [ %s ]\n", 
                (i+1),
                alias,
                op_to_str[test_cases[i].operation], 
                test_cases[i].expected,
                rc,
                ( rc2 == 0 ? "failed" : "PASSED" ) );
        if ( rc2 == 0 )
        {
            ++results.failed_cases;
        }
        else
        {
            ++results.passed_cases;
        }
    }
    printf( "%s\n", line_break );
    printf( "[PASSED: %4d] | [FAILED: %4d]\n", results.passed_cases, results.failed_cases );
    printf( "%s\n", line_break );
}

void run_test_to_file( FILE* fp, char *title, unit_test_parms_t* test_cases )
{
    int  i      = 0;
    long rc     = 0;
    long rc2    = 0;
    char        alias[64];
    results_t   results = { 0, 0 };
    char line_break[] = "---------------------------------------------------"
                        "---------------------------------------------------";
    /* title */
    fprintf( fp, "\n\n" );
    fprintf( fp, "%s\n", line_break );
    fprintf( fp, "%*s\n", ( strlen( line_break ) + strlen( title ) ) / 2, title );
    fprintf( fp, "%s\n", line_break );
    fprintf( fp, "%4s %-64s %s  %8s %8s %s\n", "No", "Test cases", "Op", "Expected", "Returned", "Result" );
    fprintf( fp, "%s\n", line_break );
    for ( ; ; ++i )
    {
        if ( !test_cases[i].func )
            break;

        rc = test_cases[i].func( test_cases[i].alias, test_cases[i].args );
        switch ( test_cases[i].operation )
        {
            case kEQ:  rc2 = ( rc == test_cases[i].expected ); break;
            case kLT:  rc2 = ( rc <  test_cases[i].expected ); break;
            case kLE:  rc2 = ( rc <= test_cases[i].expected ); break;
            case kGT:  rc2 = ( rc >  test_cases[i].expected ); break;
            case kGE:  rc2 = ( rc >= test_cases[i].expected ); break;
            case kNE:
            default:   rc2 = ( rc != test_cases[i].expected ); break;
        }

        strncpy( alias, test_cases[i].alias, 63 );
        fprintf( fp, "%4d %-64s %s %8ld %8ld [ %s ]\n", 
                (i+1),
                alias,
                op_to_str[test_cases[i].operation], 
                test_cases[i].expected,
                rc,
                ( rc2 == 0 ? "failed" : "PASSED" ) );
        if ( rc2 == 0 )
        {
            ++results.failed_cases;
        }
        else
        {
            ++results.passed_cases;
        }
    }
    fprintf( fp, "%s\n", line_break );
    fprintf( fp, "[PASSED: %4d] | [FAILED: %4d]\n", results.passed_cases, results.failed_cases );
    fprintf( fp, "%s\n", line_break );
    fflush( fp );
    
    printf( "\n\n" );
    printf( "%s\n", line_break );
    printf( "%*s\n", ( strlen( line_break ) + strlen( title ) ) / 2, title );
    printf( "%s\n", line_break );
    printf( "[PASSED: %4d] | [FAILED: %4d]\n", results.passed_cases, results.failed_cases );
    printf( "%s\n", line_break );
}

void run_test_to_html( FILE* fp, char *title, unit_test_parms_t* test_cases )
{
    int  i      = 0;
    long rc     = 0;
    long rc2    = 0;
    results_t   results = { 0, 0 };
    char line_break[] = "---------------------------------------------------"
                        "---------------------------------------------------";
    /* title */
    fprintf( fp, "<table id=\"reports\"><tr><th colspan=\"6\">%s</th></tr>", title );
    fprintf( fp, "<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>", "No", "Test cases", "Op", "Expected", "Returned", "Result" );
    
    for ( ; ; ++i )
    {
        if ( !test_cases[i].func )
            break;

        rc = test_cases[i].func( test_cases[i].alias, test_cases[i].args );
        switch ( test_cases[i].operation )
        {
            case kEQ:  rc2 = ( rc == test_cases[i].expected ); break;
            case kLT:  rc2 = ( rc <  test_cases[i].expected ); break;
            case kLE:  rc2 = ( rc <= test_cases[i].expected ); break;
            case kGT:  rc2 = ( rc >  test_cases[i].expected ); break;
            case kGE:  rc2 = ( rc >= test_cases[i].expected ); break;
            case kNE:
            default:   rc2 = ( rc != test_cases[i].expected ); break;
        }

        fprintf( fp, "<tr><td align=\"right\">%d</td><td>%s</td><td>%s</td><td align=\"center\">%ld</td><td align=\"center\">%ld</td><td>%s</td></tr>", 
                (i+1),
                test_cases[i].alias,
                op_to_str[test_cases[i].operation], 
                test_cases[i].expected,
                rc,
                ( rc2 == 0 ? "<span style=\"color:red\">failed</span>" : "<span style=\"color:green\">PASSED</span>" ) );
        if ( rc2 == 0 )
        {
            ++results.failed_cases;
        }
        else
        {
            ++results.passed_cases;
        }
    }
    fprintf( fp, 
        "<tr><th>PASSED:</th><td colspan=\"5\">%d</td></tr>" 
        "<tr><th class=\"failed\">FAILED:</th><td colspan=\"5\">%d</td></tr>", 
        results.passed_cases, results.failed_cases );
    fprintf( fp, "</table><br>" );
    fflush( fp );
    
    printf( "\n\n" );
    printf( "%s\n", line_break );
    printf( "%*s\n", ( strlen( line_break ) + strlen( title ) ) / 2, title );
    printf( "%s\n", line_break );
    printf( "[PASSED: %4d] | [FAILED: %4d]\n", results.passed_cases, results.failed_cases );
    printf( "%s\n", line_break );
}

void dump_bin( FILE* fp, char* bin, int len )
{
    int i = 0;
    int j = 0;

    /* parameters or buffer */
    for (; i < (int)len; i += 16 )
    {
        /* address */
        fprintf( fp, "%08x ", i );

        /* hex */
        for( j = 0; j < 16; ++j )
        {
            if ( i+j >= (int)len )
            {
                if (j == 8 )
                {
                    fprintf( fp, "    " );
                }
                else
                {
                    fprintf( fp, "   " );
                }
            }
            else
            {
                if (j == 8 )
                {
                    fprintf( fp, "  %02X", bin[i+j] & 0xff );
                }
                else
                {
                    fprintf( fp, " %02X", bin[i+j] & 0xff );
                }
            }
        }

        /* characters */
        fprintf( fp, "  " );

        for( j = 0; j < 16; ++j )
        {
            if ( i+j >= (int)len )
            {
                fprintf( fp, " " );
            }
            else
            {
                if ( bin[i+j] < 0x20 )
                {
                    fprintf( fp, "." );
                }
                else
                {
                    fprintf( fp, "%c", bin[i+j] & 0xff );
                }
            }
        }

        /* next line */
        fprintf( fp, "\n" );
    }

}


#ifndef __ets_unittest_h__
#define __ets_unittest_h__

/*
 *
 * File Name: ets_unittest.h
 * Author: Seree Rakwong
 * Date: 28-NOV-2017
 *
 */

#include "etssup_2750.h"
#include "etssup_2775.h"
#include "etssup_2822.h"
#include "etssup_2838.h"
#include "etssup_2954.h"
#include "etssup_3023.h"
#include "etssup_3379.h"
#include "etssup_3552.h"
#include "etssup_3660.h"
#include "etssup_3702.h"
#include "etssup_3707.h"
#include "etssup_3720.h"
#include "etssup_3814.h"

#endif /* __ets_unittest_h__ */

/*
 *
 * File Name: ets_unittest.c
 * Author: Seree Rakwong
 * Date: 28-NOV-2017
 * Purpose:
 *   Main entry to run ETS server unit tests
 *
 */
#include "msedef.h"
#include "ets_unittest.h"
#include <stdio.h>

DECLARE_PROCEDURE_ID("ets_unittest");

typedef void (*run_test_fn)(char*);
struct test_function_s
{
    run_test_fn func;
    char title[64];
};

struct test_function_s ets_functions[] =
{
    /* ADD THE NEW RUN FUNCTION HERE */
    { etssup_2750_run, "etssup_2750" },
    { etssup_2775_run, "etssup_2775" },
    { etssup_2822_run, "etssup_2822" },
    { etssup_2838_run, "etssup_2838" },
    { etssup_2954_run, "etssup_2954" },
    { etssup_3023_run, "etssup_3023" },
    { etssup_3379_run, "etssup_3379" },
    { etssup_3552_run, "etssup_3552" },
    { etssup_3660_run, "etssup_3660" },
    { etssup_3702_run, "etssup_3702" },
    { etssup_3707_run, "etssup_3707" },
    { etssup_3720_run, "etssup_3720" },
    { etssup_3814_run, "etssup_3814" },
    /* DO NOT DELETE THE LAST ELEMENT */
    { 0, "" }
};

int main(int argc, char* argv[])
{
    int i = 0;
    printf("ETS Unit Test running...\n");

    for(;; ++i)
    {
        if (!ets_functions[i].func)
        {
            break;
        }
        ets_functions[i].func(ets_functions[i].title);
    }
    printf("\nCompleted!!\n");
    return 0;
}

#ifndef __etssup_2750_h__
#define __etssup_2750_h__

/*
 *
 * File Name: etssup_2750.h
 * Author: ETS Trading Solution
 * Date: Tue Feb  6 15:31:49 GMT 2018

 * Purpose:
 *   Generated by genut
 *
 */

void etssup_2750_run(char* title);

#endif /* __etssup_2750_h__ */

/*
 *
 * File Name: etssup_2750.c
 * Author: ETS Trading Solution
 * Date: Tue Feb  6 15:31:49 GMT 2018

 * Purpose:
 *   Generated by genut
 *
 */

#ifdef __UNIX__
#undef __UNIX__
#define __UNIX
#endif

#include "cuteng.h"
#include "etssup_2750.h"
#include <string.h>
#include <bu_validate.h>
#include <b_boscom.h>
#include <b_usrmap.h>

struct IsAdvOrdRequiredToCheckCrdt_parms_s
{
    char cSpecialOrdType;
    char cSide;
    char cBAdvOrdChkAtKey;
    char cSAdvOrdChkAtKey;
    unsigned char bPayBitFlag33;
};

long test_IsAdvOrdRequiredToCheckCrdt(char* alias, void* args)
{
    long lReturned = 0L;
    struct IsAdvOrdRequiredToCheckCrdt_parms_s* params = 
        (struct IsAdvOrdRequiredToCheckCrdt_parms_s*)args;
    lReturned = IsAdvOrdRequiredToCheckCrdt(
                    params->cSpecialOrdType,
                    params->cSide,
                    params->cBAdvOrdChkAtKey,
                    params->cSAdvOrdChkAtKey,
                    params->bPayBitFlag33
                );
    return lReturned;
}

struct IsAdvOrdRequiredToCheckCrdt_parms_s IsAdvOrdRequiredToCheckCrdt_parms[] =
{
    { 'S', 'B', 'N', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'S', 'B', 'N', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'S', 'B', 'Y', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'S', 'B', 'Y', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'T', 'B', 'N', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'T', 'B', 'N', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'T', 'B', 'Y', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'T', 'B', 'Y', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'S', 'B', 'N', 'N', 0x00 },
    { 'S', 'B', 'N', 'Y', 0x00 },
    { 'S', 'B', 'Y', 'N', 0x00 },
    { 'S', 'B', 'Y', 'Y', 0x00 },
    { 'T', 'B', 'N', 'N', 0x00 },
    { 'T', 'B', 'N', 'Y', 0x00 },
    { 'T', 'B', 'Y', 'N', 0x00 },
    { 'T', 'B', 'Y', 'Y', 0x00 },
    { 'S', 'S', 'N', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'S', 'S', 'N', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'S', 'S', 'Y', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'S', 'S', 'Y', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'T', 'S', 'N', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'T', 'S', 'N', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'T', 'S', 'Y', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'T', 'S', 'Y', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'S', 'S', 'N', 'N', 0x00 },
    { 'S', 'S', 'N', 'Y', 0x00 },
    { 'S', 'S', 'Y', 'N', 0x00 },
    { 'S', 'S', 'Y', 'Y', 0x00 },
    { 'T', 'S', 'N', 'N', 0x00 },
    { 'T', 'S', 'N', 'Y', 0x00 },
    { 'T', 'S', 'Y', 'N', 0x00 },
    { 'T', 'S', 'Y', 'Y', 0x00 },
    { 'A', 'S', 'N', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'A', 'S', 'N', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'A', 'S', 'Y', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'A', 'S', 'Y', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'A', 'S', 'N', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'A', 'S', 'N', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'A', 'S', 'Y', 'N', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'A', 'S', 'Y', 'Y', BIT33_PAY_ADVORD_CRDTCHK_MODE },
    { 'A', 'S', 'N', 'N', 0x00 },
    { 'A', 'S', 'N', 'Y', 0x00 },
    { 'A', 'S', 'Y', 'N', 0x00 },
    { 'A', 'S', 'Y', 'Y', 0x00 },
    { 'A', 'S', 'N', 'N', 0x00 },
    { 'A', 'S', 'N', 'Y', 0x00 },
    { 'A', 'S', 'Y', 'N', 0x00 },
    { 'A', 'S', 'Y', 'Y', 0x00 },
    { 0, 0, 0, 0, 0 }
};

/*ETSSUP-3726*/
BEGIN_UNIT_TEST_PARAMS( etssup_2750_1 )
    ON_UNIT_TEST_PARAMS( "type='S', side='B', BKey='N', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[0],    kEQ,    0L )
    ON_UNIT_TEST_PARAMS( "type='S', side='B', BKey='N', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[1],    kEQ,    0L )
    ON_UNIT_TEST_PARAMS( "type='S', side='B', BKey='Y', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[2],    kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='S', side='B', BKey='Y', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[3],    kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='B', BKey='N', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[4],    kEQ,    0L )
    ON_UNIT_TEST_PARAMS( "type='T', side='B', BKey='N', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[5],    kEQ,    0L )
    ON_UNIT_TEST_PARAMS( "type='T', side='B', BKey='Y', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[6],    kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='B', BKey='Y', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[7],    kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='S', side='B', BKey='N', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[8],    kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='S', side='B', BKey='N', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[9],    kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='S', side='B', BKey='Y', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[10],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='S', side='B', BKey='Y', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[11],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='B', BKey='N', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[12],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='B', BKey='N', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[13],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='B', BKey='Y', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[14],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='B', BKey='Y', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[15],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='S', side='S', BKey='N', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[16],   kEQ,    0L )
    ON_UNIT_TEST_PARAMS( "type='S', side='S', BKey='N', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[17],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='S', side='S', BKey='Y', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[18],   kEQ,    0L )
    ON_UNIT_TEST_PARAMS( "type='S', side='S', BKey='Y', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[19],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='S', BKey='N', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[20],   kEQ,    0L )
    ON_UNIT_TEST_PARAMS( "type='T', side='S', BKey='N', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[21],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='S', BKey='Y', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[22],   kEQ,    0L )
    ON_UNIT_TEST_PARAMS( "type='T', side='S', BKey='Y', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[23],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='S', side='S', BKey='N', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[24],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='S', side='S', BKey='N', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[25],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='S', side='S', BKey='Y', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[26],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='S', side='S', BKey='Y', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[27],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='S', BKey='N', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[28],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='S', BKey='N', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[29],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='S', BKey='Y', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[30],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='T', side='S', BKey='Y', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[31],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='N', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[32],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='N', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[33],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='Y', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[34],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='Y', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[35],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='N', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[36],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='N', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[37],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='Y', SKey='N', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[38],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='Y', SKey='Y', pay=BIT33_PAY_ADVORD_CRDTCHK_MODE",  test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[39],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='N', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[40],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='N', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[41],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='Y', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[42],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='Y', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[43],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='N', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[44],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='N', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[45],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='Y', SKey='N', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[46],   kEQ,    1L )
    ON_UNIT_TEST_PARAMS( "type='A', side='S', BKey='Y', SKey='Y', pay=0x00",                           test_IsAdvOrdRequiredToCheckCrdt,   &IsAdvOrdRequiredToCheckCrdt_parms[47],   kEQ,    1L )
END_UNIT_TEST_PARAMS()

void etssup_2750_run(char* title)
{
    FILE* fp = NULL;
    BEGIN_RUN_TEST_HTML(fp, "etssup_2750_3726_utr.html" )
        RUN_TEST_HTML( fp, title,  etssup_2750_1 )
    END_RUN_TEST_HTML(fp)
}













Tuesday 18 September 2018

TUI

##############################################
# File Name: makefile
# Author: Seree Rakwong
# Date: 28-NOV-2017
#

TARGET = tui_test

CC     = gcc
CFLAGS = -g -Wall
LFLAGS = -lm -lcurses

.PHONY: default all clean


default: $(TARGET)

all: default

BIN_DIR  = ./bin

SRC_DIR  = ./src
INC_DIR  = ./include
SOURCES  = $(SRC_DIR)/tui.c \
           $(SRC_DIR)/tdc.c \
           $(SRC_DIR)/tmsgbx.c \
           $(SRC_DIR)/tstc.c \
           $(SRC_DIR)/tedt.c \
           $(SRC_DIR)/tbtn.c \
           $(SRC_DIR)/tlb.c \
           $(SRC_DIR)/tlctl.c \
           $(SRC_DIR)/tui_test.c


OBJECTS  = $(SOURCES:.c=.o)

INCLUDES = -I$(INC_DIR)
DEFINES  = -D__UNIX__ -D__LINUX__


%.o: %.c

$(CC) $(CFLAGS) $(INCLUDES) $(DEFINES) -c $< -o $@

$(TARGET): $(OBJECTS)

$(CC) $(CFLAGS) $(LFLAGS) -o $@ $^

clean:

rm -f src/*.o $(TARGET)*


##############################################
# File Name: makefile
# Author: Seree Rakwong
# Date: 28-NOV-2017
#

TARGET = tui_test_sol8

CC     = gcc
CFLAGS = -g -Wall
LFLAGS = -lm -lcurses

.PHONY: default all clean


default: $(TARGET)

all: default

BIN_DIR  = ./bin

SRC_DIR  = ./src
INC_DIR  = ./include
SOURCES  = $(SRC_DIR)/tui.c \
           $(SRC_DIR)/tdc.c \
           $(SRC_DIR)/tmsgbx.c \
           $(SRC_DIR)/tstc.c \
           $(SRC_DIR)/tedt.c \
           $(SRC_DIR)/tbtn.c \
           $(SRC_DIR)/tlb.c \
           $(SRC_DIR)/tlctl.c \
           $(SRC_DIR)/tui_test.c


OBJECTS  = $(SOURCES:.c=.o)

INCLUDES = -I$(INC_DIR)
DEFINES  = -D__UNIX__ -D__USE_CURSES__


%.o: %.c

$(CC) $(CFLAGS) $(INCLUDES) $(DEFINES) -c $< -o $@

$(TARGET): $(OBJECTS)

$(CC) $(CFLAGS) $(LFLAGS) -o $@ $^

clean:

rm -f src/*.o $(TARGET)*



##############################################
# File Name: makefile
# Author: Seree Rakwong
# Date: 28-NOV-2017
#

TARGET = tui_test_linux

CC     = gcc
CFLAGS = -g -Wall
LFLAGS = -lm -lcurses

.PHONY: default all clean


default: $(TARGET)

all: default

BIN_DIR  = ./bin

SRC_DIR  = ./src
INC_DIR  = ./include
SOURCES  = $(SRC_DIR)/tui.c \
           $(SRC_DIR)/tdc.c \
           $(SRC_DIR)/tmsgbx.c \
           $(SRC_DIR)/tstc.c \
           $(SRC_DIR)/tedt.c \
           $(SRC_DIR)/tbtn.c \
           $(SRC_DIR)/tlb.c \
           $(SRC_DIR)/tlctl.c \
           $(SRC_DIR)/tui_test.c


OBJECTS  = $(SOURCES:.c=.o)

INCLUDES = -I$(INC_DIR)
DEFINES  = -D__UNIX__ -D__LINUX__ -D__USE_CURSES__


%.o: %.c

$(CC) $(CFLAGS) $(INCLUDES) $(DEFINES) -c $< -o $@

$(TARGET): $(OBJECTS)

$(CC) $(CFLAGS) $(LFLAGS) -o $@ $^

clean:

rm -f src/*.o $(TARGET)*
/*-------------------------------------------------------------------
 * File name: tui.h
 * Author: Seree Rakwong
 * Date: 17-SEP-18
 *-----------------------------------------------------------------*/
#ifndef __TEXTUSERINTERFACE_H__
#define __TEXTUSERINTERFACE_H__

#ifdef __cplusplus
extern "C" {
#endif

/*-------------------------------------------------------------------
 * defines
 *-----------------------------------------------------------------*/
#define TUI_CONTINUE     1
#define TUI_OK           0
#define TUI_ERROR       -1
#define TUI_MEM         -2

/* miscellaneous */
#define TUI_MAX_WNDTEXT    80

#define TVK_BACK           0x08
#define TVK_TAB            0x09
#define TVK_ENTER          0x0A

#define TVK_SPACE          0x20
#define TVK_PRIOR          0x21
#define TVK_NEXT           0x22
#define TVK_END            0x23
#define TVK_HOME           0x24
#define TVK_LEFT           0x25
#define TVK_UP             0x26
#define TVK_RIGHT          0x27
#define TVK_DOWN           0x28
#define TVK_INSERT         0x2D
#define TVK_DELETE         0x2E
#define TVK_HELP           0x2F


#define TVK_ESCAPE         0x1B
#define TVK_NUMPAD0        0x60
#define TVK_NUMPAD1        0x61
#define TVK_NUMPAD2        0x62
#define TVK_NUMPAD3        0x63
#define TVK_NUMPAD4        0x64
#define TVK_NUMPAD5        0x65
#define TVK_NUMPAD6        0x66
#define TVK_NUMPAD7        0x67
#define TVK_NUMPAD8        0x68
#define TVK_NUMPAD9        0x69
#define TVK_MULTIPLY       0x6A
#define TVK_ADD            0x6B
#define TVK_SEPARATOR      0x6C
#define TVK_SUBTRACT       0x6D
#define TVK_DECIMAL        0x6E
#define TVK_DIVIDE         0x6F
#define TVK_F1             0x70
#define TVK_F2             0x71
#define TVK_F3             0x72
#define TVK_F4             0x73
#define TVK_F5             0x74
#define TVK_F6             0x75
#define TVK_F7             0x76
#define TVK_F8             0x77
#define TVK_F9             0x78
#define TVK_F10            0x79
#define TVK_F11            0x7A
#define TVK_F12            0x7B
#define TVK_F13            0x7C
#define TVK_F14            0x7D
#define TVK_F15            0x7E
#define TVK_F16            0x7F
#define TVK_F17            0x80
#define TVK_F18            0x81
#define TVK_F19            0x82
#define TVK_F20            0x83
#define TVK_F21            0x84
#define TVK_F22            0x85
#define TVK_F23            0x86
#define TVK_F24            0x87

/*
 * 0x88 - 0x8F : unassigned
 */

#define TVK_NUMLOCK        0x90
#define TVK_SCROLL         0x91

/*-------------------------------------------------------------------
 * types
 *-----------------------------------------------------------------*/
typedef long                WPARAM;
typedef long                LPARAM;
typedef long                LONG;
typedef int                 INT;
typedef void                VOID;
typedef void*               LPVOID;
typedef const char*         LPCSTR;
typedef char*               LPSTR;
typedef char                CHAR;
typedef double              DOUBLE;
  
typedef unsigned long       DWORD;
typedef unsigned int        UINT;
  
typedef char                INT8;
typedef unsigned char       UINT8;
typedef short               INT16;
typedef unsigned short      UINT16;
typedef int                 INT32;
typedef unsigned int        UINT32;
typedef long long           INT64;
typedef unsigned long long  UINT64;

struct _TUIDEVICECONTEXSTRUCT;
typedef struct _TUIDEVICECONTEXSTRUCT _TDC;
typedef struct _TUIDEVICECONTEXSTRUCT *TDC;

struct _TUIWINDOWSTRUCT;
typedef struct _TUIWINDOWSTRUCT _TWND;
typedef struct _TUIWINDOWSTRUCT *TWND;

typedef long (*TWNDPROC)(TWND, UINT, WPARAM, LPARAM);

struct _TUIENVSTRUCT;
typedef struct _TUIENVSTRUCT _TENV;
typedef struct _TUIENVSTRUCT *TENV;

struct _WNDTEMPLSTRUCT
{
  LPCSTR clsname;
  LPCSTR text;
  INT    id;
  INT    y;
  INT    x;
  INT    lines;
  INT    cols;
  DWORD  style;
  INT    (*validate)(TWND, LPCSTR);
};
typedef struct _WNDTEMPLSTRUCT WNDTEMPL;

struct _MSGSTRUCT
{
  TWND   wnd;
  UINT   msg;
  WPARAM wparam;
  LPARAM lparam;
};
typedef struct _MSGSTRUCT MSG;

struct _NMHDRSTRUCT
{
  INT  id;
  TWND ctl;
  UINT code;
};
typedef struct _NMHDRSTRUCT NMHDR;

struct _RECTSTRUCT
{
  INT y;
  INT x;
  INT lines;
  INT cols;
};
typedef struct _RECTSTRUCT RECT;

struct _DRAWITEMSTRUCT
{
  INT  idx;
  RECT rcitem;
};
typedef struct _DRAWITEMSTRUCT DRAWITEM;

/* colors */
/*
 216 #define COLOR_BLACK     0
 217 #define COLOR_RED       1
 218 #define COLOR_GREEN     2
 219 #define COLOR_YELLOW    3
 220 #define COLOR_BLUE      4
 221 #define COLOR_MAGENTA   5
 222 #define COLOR_CYAN      6
 223 #define COLOR_WHITE     7

*/
#define CYAN_BLACK              1
#define BLACK_CYAN              2
#define GREEN_BLACK             3
#define BLACK_GREEN             4
#define YELLOW_BLACK            5
#define BLACK_YELLOW            6
#define BLUE_YELLOW             7
#define YELLOW_BLUE             8

#define ALIGN_LEFT              0
#define ALIGN_CENTER            1
#define ALIGN_RIGHT             2

#define TW_HIDE                 0
#define TW_SHOW                 1

/* window styles */
#define TWS_WINDOW              0x00000001
#define TWS_CHILD               0x00000002
#define TWS_VISIBLE             0x00000004
#define TWS_ENABLE              0x00000008
#define TWS_DISABLED            0x00000010
#define TWS_LEFT                0x00000000             /* shared controls style */
#define TWS_CENTER              0x00010000
#define TWS_RIGHT               0x00020000

/* window messages */
#define TWM_FIRST               100
#define TWM_CREATE              (TWM_FIRST +    1)
#define TWM_DESTROY             (TWM_FIRST +    2)
#define TWM_INITDIALOG          (TWM_FIRST +    3)
#define TWM_PAINT               (TWM_FIRST +    4)
#define TWM_SETFOCUS            (TWM_FIRST +    5)
#define TWM_KILLFOCUS           (TWM_FIRST +    6)
#define TWM_KEYDOWN             (TWM_FIRST +    7)
#define TWM_KEYUP               (TWM_FIRST +    8)
#define TWM_CHAR                (TWM_FIRST +    9)
#define TWM_NOTIFY              (TWM_FIRST +   10)
#define TWM_ERASEBK             (TWM_FIRST +   11)
#define TWM_SETTEXT             (TWM_FIRST +   12)
#define TWM_GETTEXT             (TWM_FIRST +   13)
#define TWM_SETTEXTALIGN        (TWM_FIRST +   14)
#define TWM_COMMAND             (TWM_FIRST +   15)
#define TWM_SETTEXTATTRS        (TWM_FIRST +   16)
#define TWM_DRAWITEM            (TWM_FIRST +   17)

/* application user messages */
#define TWM_USER          10000

/* notification control message */
#define TSN_FIRST               (TWM_USER +  100)
#define TEN_FIRST               (TWM_USER +  150)
#define TLBN_FIRST              (TWM_USER +  200)
#define TBN_FIRST               (TWM_USER +  250)
#define TLCN_FIRST              (TWM_USER +  300)

/*-------------------------------------------------------------------
 * static control
 *-----------------------------------------------------------------*/
#define STATIC                  "STATIC"
      
#define TSS_LEFT                TWS_LEFT
#define TSS_CENTER              TWS_CENTER
#define TSS_RIGHT               TWS_RIGHT
      
/*-------------------------------------------------------------------
 * edit control
 *-----------------------------------------------------------------*/
#define EDIT                    "EDIT"

#define TES_LEFT                TWS_LEFT
#define TES_CENTER              TWS_CENTER
#define TES_RIGHT               TWS_RIGHT
#define TES_NUMBER              0x00040000
#define TES_UPPERCASE           0x00080000
#define TES_LOWERCASE           0x00100000
#define TES_PASSWORD            0x00200000
#define TES_APPENDMODE          0x00400000
#define TES_AUTOHSCROLL         0x00800000
#define TES_DECIMAL             0x01000000

#define TEM_LIMITTEXT           (TWM_USER  +    1)
#define TEM_SETPASSWDCHAR       (TWM_USER  +    2)
#define TEM_SHOWPASSWDCHAR      (TWM_USER  +    3)
#define TEM_SETDECWIDTH         (TWM_USER  +    4)


/* edit notified message */
#define TEN_CHANGED             (TEN_FIRST  +   0)
#define TEN_SETFOCUS            (TEN_FIRST  +   1)
#define TEN_KILLFOCUS           (TEN_FIRST  +   2)

/* edit macros */
#define TEDT_LimitText(edt, lim)    \
  TuiSendMsg(edt, TEM_LIMITTEXT, (WPARAM)lim, (LPARAM)0)
#define TEDT_SetPasswdChar(edt, ch)    \
  TuiSendMsg(edt, TEM_SETPASSWDCHAR, (WPARAM)ch, (LPARAM)0)
#define TEDT_ShowPasswdChar(edt, show)    \
  TuiSendMsg(edt, TEM_SHOWPASSWDCHAR, (WPARAM)show, (LPARAM)0)
#define TEDT_SetDecimalWidth(edt, wid)    \
  TuiSendMsg(edt, TEM_SETDECWIDTH, (WPARAM)wid, (LPARAM)0)

/*-------------------------------------------------------------------
 * listbox control
 *-----------------------------------------------------------------*/
#define LISTBOX                 "LISTBOX"

#define LB_OK                   TUI_OK
#define LB_ERROR                TUI_ERROR
#define LB_UNCHECKED            0
#define LB_CHECKED              1

#define TLBS_LEFT               TWS_LEFT
#define TLBS_CENTER             TWS_CENTER
#define TLBS_RIGHT              TWS_RIGHT
#define TLBS_OWNERDRAW          0x00100000
#define TLBS_CHECKBOX           0x00200000
#define TLBS_RADIOBOX           0x00400000

#define TLBM_ADDITEM            (TWM_USER  +    1)
#define TLBM_DELETEITEM         (TWM_USER  +    2)
#define TLBM_GETCURSEL          (TWM_USER  +    3)
#define TLBM_SETCURSEL          (TWM_USER  +    4)
#define TLBM_DELETEALLITEMS     (TWM_USER  +    5)
#define TLBM_GETITEMCOUNT       (TWM_USER  +    6)
#define TLBM_SETITEMDATA        (TWM_USER  +    7)
#define TLBM_GETITEMDATA        (TWM_USER  +    8)
#define TLBM_SETITEMTEXT        (TWM_USER  +    9)
#define TLBM_GETITEMTEXT        (TWM_USER  +   10)
#define TLBM_SETITEMCHECKED     (TWM_USER  +   11)
#define TLBM_GETITEMCHECKED     (TWM_USER  +   12)
#define TLBM_COUNTITEMCHECKED   (TWM_USER  +   13)

/* listbox notified message */
#define TLBN_SETFOCUS           (TLBN_FIRST  +    0)
#define TLBN_KILLFOCUS          (TLBN_FIRST  +    1)
#define TLBN_SELCHANGED         (TLBN_FIRST  +    2)

/* listbox macros */
#define TLB_AddItem(lbx, text)    \
  TuiSendMsg(lbx, TLBM_ADDITEM, (WPARAM)0, (LPARAM)text)
#define TLB_DeleteItem(lbx, idx)  \
  TuiSendMsg(lbx, TLBM_DELETEITEM, (WPARAM)idx, (LPARAM)0)
#define TLB_GetCurSel(lbx)        \
  TuiSendMsg(lbx, TLBM_GETCURSEL, (WPARAM)0, (LPARAM)0)
#define TLB_SetCurSel(lbx, idx)   \
  TuiSendMsg(lbx, TLBM_SETCURSEL, (WPARAM)idx, (LPARAM)0)
#define TLB_DeleteAllItems(lbx)   \
  TuiSendMsg(lbx, TLBM_DELETEALLITEMS, (WPARAM)0, (LPARAM)0)
#define TLB_GetItemCount(lbx)   \
  TuiSendMsg(lbx, TLBM_GETITEMCOUNT, (WPARAM)0, (LPARAM)0)
#define TLB_SetItemData(lbx, idx, data)   \
  TuiSendMsg(lbx, TLBM_SETITEMDATA, (WPARAM)idx, (LPARAM)data)
#define TLB_GetItemData(lbx, idx)   \
  (LPVOID)TuiSendMsg(lbx, TLBM_GETITEMDATA, (WPARAM)idx, (LPARAM)0)
#define TLB_SetItemText(lbx, idx, text)   \
  TuiSendMsg(lbx, TLBM_SETITEMTEXT, (WPARAM)idx, (LPARAM)text)
#define TLB_GetItemText(lbx, idx, text)   \
  TuiSendMsg(lbx, TLBM_GETITEMTEXT, (WPARAM)idx, (LPARAM)text)
#define TLB_SetItemChecked(lbx, idx, check)   \
  TuiSendMsg(lbx, TLBM_SETITEMCHECKED, (WPARAM)idx, (LPARAM)check)
#define TLB_GetItemChecked(lbx, idx)   \
  TuiSendMsg(lbx, TLBM_GETITEMCHECKED, (WPARAM)idx, (LPARAM)0)
#define TLB_CountItemChecked(lbx)   \
  TuiSendMsg(lbx, TLBM_COUNTITEMCHECKED, (WPARAM)0, (LPARAM)0)

/*-------------------------------------------------------------------
 * button control
 *-----------------------------------------------------------------*/
#define BUTTON                  "BUTTON"

#define TBS_RELEASED            0x0000
#define TBS_PRESSED             0x0001
#define TBS_FOCUSED             0x0002

#define TBN_SETFOCUS            (TBN_FIRST  +   0)
#define TBN_KILLFOCUS           (TBN_FIRST  +   1)

/*-------------------------------------------------------------------
 * listctrl control
 *-----------------------------------------------------------------*/
#define LISTCTRL                "LISTCTRL"


#define TLCM_ADDCOLUMN          (TWM_USER  +    1)
#define TLCM_DELETECOLUMN       (TWM_USER  +    2)
#define TLCM_DELETEALLCOLUMNS   (TWM_USER  +    3)
#define TLCM_ADDITEM            (TWM_USER  +    4)
#define TLCM_DELETEITEM         (TWM_USER  +    5)
#define TLCM_DELETEALLITEMS     (TWM_USER  +    6)
#define TLCM_SETITEM            (TWM_USER  +    7)
#define TLCM_GETITEM            (TWM_USER  +    8)
#define TLCM_GETITEMCOUNT       (TWM_USER  +    9)

/*
#define TLCN_FIRST              (TWM_USER +  300)
*/

#define  LCFM_TEXT              0x0001
#define  LCFM_ATTRS             0x0002
#define  LCFM_DATA              0x0004
struct _SUBITEMSTRUCT
{
  INT       col;
  INT       idx;
  CHAR*     text;
  DWORD     attrs;
  VOID*     data;
};
typedef struct _SUBITEMSTRUCT SUBITEM;

struct _HEADERITEMSTRUCT
{
  CHAR*     caption;
  INT       cols;
  INT       align;
  DWORD     attrs;
};
typedef struct _HEADERITEMSTRUCT HEADERITEM;

/* listctrl macros */
#define TLC_AddColumn(lc, text, width, al, at)    \
do {\
  HEADERITEM hdr; \
  hdr.caption = text; \
  hdr.cols    = width; \
  hdr.align   = al; \
  hdr.attrs   = at; \
  TuiSendMsg(lc, TLCM_ADDCOLUMN, (WPARAM)0, (LPARAM)&hdr); \
} while (0)
  
#define TLC_DeleteColumn(lc, col)    \
  TuiSendMsg(lc, TLCM_DELETECOLUMN, (WPARAM)col, (LPARAM)0)
#define TLC_DeleteAllColumns(lc)    \
  TuiSendMsg(lc, TLCM_DELETEALLCOLUMNS, (WPARAM)0, (LPARAM)0)
#define TLC_AddItem(lc, text, nitems)    \
  TuiSendMsg(lc, TLCM_ADDITEM, (WPARAM)nitems, (LPARAM)text)
#define TLC_DeleteItem(lc, idx)    \
  TuiSendMsg(lc, TLCM_DELETEITEM, (WPARAM)idx, (LPARAM)0)
#define TLC_DeleteAllItems(lc)    \
  TuiSendMsg(lc, TLCM_DELETEALLITEMS, (WPARAM)0, (LPARAM)0)  
#define TLC_SetItem(lc, flags, item)    \
  TuiSendMsg(lc, TLCM_SETITEMTEXT, (WPARAM)flags, (LPARAM)&subitem)
#define TLC_GetItem(lc, flags, item)    \
  TuiSendMsg(lc, TLCM_GETITEMTEXT, (WPARAM)flags, (LPARAM)&subitem);
  
#define TLC_GetItemCount(lc)    \
  TuiSendMsg(lc, TLCM_GETITEMCOUNT, (WPARAM)0, (LPARAM)0)
  
/*-------------------------------------------------------------------
 * window functions
 *-----------------------------------------------------------------*/
/*
 * TuiStartup()
 *   Start TUI environment
 */
LONG TuiStartup();
/*
 * TuiShutdown()
 *   End TUI environment
 */
void TuiShutdown();

/*
 * TuiGetEnv()
 *   Get TUI environment
 */
TENV TuiGetEnv();
VOID TuiSetNextMove(LONG nextmove);
UINT TuiGetDlgMsgID();

/* window message functions */
LONG TuiDefWndProc(TWND, UINT, WPARAM, LPARAM);
LONG TuiRegisterCls(LPCSTR clsname, TWNDPROC wndproc);
LONG TuiGetMsg(MSG* msg);
LONG TuiDispatchMsg(MSG* msg);
LONG TuiTranslateMsg(MSG* msg);
LONG TuiPostQuitMsg(LONG exitcode);
LONG TuiSendMsg(TWND, UINT, WPARAM, LPARAM);
LONG TuiPostMsg(TWND, UINT, WPARAM, LPARAM);

/* window functions */
TWND TuiCreateWnd(
  LPCSTR   clsname,
  LPCSTR   wndname,
  DWORD    style,
  INT      y,
  INT      x,
  INT      lines,
  INT      cols,
  TWND     parent,
  INT      id,
  LPVOID   param
);
TWND TuiCreateWndTempl(
  WNDTEMPL* templs,
  LPVOID    param
);
void TuiDestroyWnd(TWND);
TWND TuiGetActiveChildWnd(TWND);
TWND TuiGetFirstActiveChildWnd(TWND);
TWND TuiGetLastActiveChildWnd(TWND);
TWND TuiGetNextActiveChildWnd(TWND);
TWND TuiGetPrevActiveChildWnd(TWND);
TWND TuiGetActiveWnd();
TWND TuiGetFirstWnd();
TWND TuiGetLastWnd();
TWND TuiGetNextWnd(TWND wnd);
TWND TuiGetPrevWnd(TWND wnd);
LONG TuiInvalidateWnd(TWND);
LONG TuiShowWnd(TWND, LONG);
LONG TuiEnableWnd(TWND, LONG);
LONG TuiIsWndEnabled(TWND);
LONG TuiIsWndVisible(TWND);
TWND TuiGetWndItem(TWND, INT);
LONG TuiGetWndText(TWND, LPSTR, LONG);
VOID TuiSetWndText(TWND, LPCSTR);
TWND TuiGetParent(TWND);
LONG TuiSetFocus(TWND);
LONG TuiMoveWnd(TWND, INT, INT, INT, INT);
LONG TuiGetWndRect(TWND, RECT*);
DWORD TuiGetWndStyle(TWND);
DWORD TuiSetWndStyle(TWND, DWORD);
DWORD TuiGetWndTextAttrs(TWND);
DWORD TuiSetWndTextAttrs(TWND, DWORD);
UINT TuiGetWndID(TWND);
LPVOID TuiGetWndParam(TWND);
LPVOID TuiSetWndParam(TWND, LPVOID);
LONG   TuiIsWndValidate(TWND, LPCSTR);
VOID TuiSetWndValidate(TWND, LONG (*)(TWND, LPCSTR));

#define MB_INVALID           0x00000000
#define MB_CAPTION           0x00010000
#define MB_YES               0x00000001
#define MB_NO                0x00000002
#define MB_CANCEL            0x00000004
#define MB_YESNOCANCEL       (MB_YES|MB_NO|MB_CANCEL)
#define MB_YESNO             (MB_YES|MB_NO)

#define MB_OK                0x00000008
#define MB_OKCANCEL          (MB_OK|MB_CANCEL)
UINT TuiEndDlg(TWND, UINT);
UINT TuiMsgBox(TWND, LPCSTR, LPCSTR, UINT);
/* device context */
#define DT_LEFT              0x0001
#define DT_CENTER            0x0002
#define DT_RIGHT             0x0003

struct _TUIDEVICECONTEXSTRUCT
{
#ifdef __USE_CURSES__
  WINDOW*             win;
#elif defined __USE_QIO__
  /* QIO for VMS implement here */
  struct qio_fields*  win;
#endif
};

TDC  TuiGetDC(TWND);
LONG TuiDrawText(TDC, INT, INT, LPCSTR, DWORD);
LONG TuiDrawTextEx(TDC, INT, INT, INT, LPCSTR, LONG, DWORD, INT);
LONG TuiPutChar(TDC, INT, INT, CHAR, DWORD);
LONG TuiMoveYX(TDC, INT, INT);
LONG TuiGetYX(TDC, INT*, INT*);
LONG TuiPrintTextAlignment(LPSTR out, LPSTR in, LONG limit, INT align);

/* simple macros */
#define MIN(a, b)    ((a) < (b) ? (a) : (b))
#define MAX(a, b)    ((a) > (b) ? (a) : (b))

#ifdef __cplusplus
}
#endif
#endif /*__TEXTUSERINTERFACE_H__*/

/*-------------------------------------------------------------------
 * File name: tui.c
 * Author: Seree Rakwong
 * Date: 17-SEP-18
 *-----------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>

#ifdef __USE_CURSES__
#include <curses.h>
#elif (defined __USE_QIO__ && defined __VMS__)
#include <qio_init.h>
#endif

#include "tui.h"

struct _TWNDPROCSTRUCT
{
  TWNDPROC     wndproc;
  LPCSTR       clsname;
  struct _TWNDPROCSTRUCT *prev;
  struct _TWNDPROCSTRUCT *next;
};
typedef struct _TWNDPROCSTRUCT twndproc_t;

struct _TUIMSGQUEUESTRUCT
{
  TWND     wnd;
  UINT     msg;
  WPARAM   wparam;
  LPARAM   lparam;

  struct _TUIMSGQUEUESTRUCT *next;
};
typedef struct _TUIMSGQUEUESTRUCT tmsgq_t;

struct _TUIENVSTRUCT
{
  /* doubly linked-list */
  twndproc_t*      firstproc;
  twndproc_t*      lastproc;

  /* que */
  tmsgq_t*         headq;
  tmsgq_t*         tailq;

  /* doubly linked-list */
  TWND             firstwnd;
  TWND             lastwnd;
  TWND             activewnd;

  LONG             quitcode;
  LONG             exitcode;

  LONG             nextmove; /* to identify the next control moved */
  LONG             prevmove; /* to identify the prev control moved */
  LONG             notifykey;
  
  _TDC             dc;
  /* last dialog returned message */
  UINT             dlgmsgid;
};

struct _TUIWINDOWSTRUCT
{
  LPCSTR           clsname;
  CHAR             wndname[TUI_MAX_WNDTEXT+1];
  DWORD            style;
  DWORD            exstyle;
  INT              x;
  INT              y;
  INT              cols;
  INT              lines;
  TWND             parent;
  UINT             id;
  LPVOID           param;
  TWNDPROC         wndproc;
  LONG             enable;
  LONG             visible;
  LONG             (*validate)(TWND, LPCSTR);
  /* curses lib */
#ifdef __USE_CURSES__
  WINDOW*            win;
#elif defined __USE_QIO__
  /* QIO for VMS implement here */
  struct qio_fields* win;
#endif
  DWORD            attrs;
  /* last dialog returned message */
  UINT             dlgmsgid;
  
  /* links */
  TWND             prevwnd;
  TWND             nextwnd;
  TWND             firstchild;
  TWND             lastchild;
  TWND             activechild;
};

/*-------------------------------------------------------------------
 * global storages
 *-----------------------------------------------------------------*/
TENV genvptr = 0;

/*-------------------------------------------------------------------
 * internal functions
 *-----------------------------------------------------------------*/
twndproc_t* _TuiFindWndProc(LPCSTR clsname);
TWND _TuiCreateWnd(
  LPCSTR   clsname,
  LPCSTR   wndname,
  DWORD    style,
  INT      y,
  INT      x,
  INT      lines,
  INT      cols,
  TWND     parent,
  UINT     id,
  LPVOID   param
);
VOID _TuiDestroyWnd(TWND wnd);
LONG _TuiInvalidateWnd(TWND wnd);
LONG _TuiDequeMsg();
LONG _TuiRemoveAllMsgs();
LONG _TuiAlignmentPrint(LPSTR out, LPSTR in, LONG limit, INT align);

LONG STATICPROC(TWND, UINT, WPARAM, LPARAM);
LONG EDITPROC(TWND, UINT, WPARAM, LPARAM);
LONG LISTBOXPROC(TWND, UINT, WPARAM, LPARAM);
LONG BUTTONPROC(TWND, UINT, WPARAM, LPARAM);
LONG LISTCTRLPROC(TWND, UINT, WPARAM, LPARAM);

#define MSGBOX          "MSGBOX"
LONG MSGBOXPROC(TWND, UINT, WPARAM, LPARAM);

/*-------------------------------------------------------------------
 * functions
 *-----------------------------------------------------------------*/
 #ifdef __USE_QIO__
int printf(const char *format, ...);
int sprintf(char *str, const char *format, ...);

LONG QIO_Initialize();
VOID QIO_Get();
VOID QIO_Put();

VOID DoUpdate() /* required by QIO */
{
    return;
}
VOID CheckTerm() /* required by QIO */
{
    return;
}
#endif /*__USE_QIO__*/

LONG TuiStartup()
{
  TENV env = (TENV)malloc(sizeof(_TENV));
  if (env)
  {
    genvptr = env;
    memset(env, 0, sizeof(_TENV));

    /* register standard controls */
    TuiRegisterCls(STATIC, STATICPROC);
    TuiRegisterCls(EDIT, EDITPROC);
    TuiRegisterCls(LISTBOX, LISTBOXPROC);
    TuiRegisterCls(BUTTON, BUTTONPROC);
    TuiRegisterCls(LISTCTRL, LISTCTRLPROC);
    TuiRegisterCls(MSGBOX, MSGBOXPROC);

    /* device context */
#ifdef __USE_CURSES__
    /* curses lib */
    initscr();
    cbreak();
    keypad(stdscr, TRUE);
    noecho();
    
    start_color();
    
    init_pair(CYAN_BLACK, COLOR_CYAN, COLOR_BLACK);
    init_pair(BLACK_CYAN, COLOR_BLACK, COLOR_GREEN);
    init_pair(GREEN_BLACK, COLOR_GREEN, COLOR_BLACK);
    init_pair(BLACK_GREEN, COLOR_BLACK, COLOR_GREEN);
    init_pair(YELLOW_BLACK, COLOR_YELLOW, COLOR_BLACK);
    init_pair(BLACK_YELLOW, COLOR_BLACK, COLOR_YELLOW);
    init_pair(BLUE_YELLOW, COLOR_BLUE, COLOR_YELLOW);
    init_pair(YELLOW_BLUE, COLOR_YELLOW, COLOR_BLUE);
    
    env->dc.win    = stdscr;
    env->nextmove  = TVK_ENTER;
    env->prevmove  = KEY_BTAB;
    env->notifykey = TVK_ENTER;
    
#elif defined __USE_QIO__
  /* QIO for VMS implement here */
    if (!QIO_Initialize())
    {
        free(env);
        genvptr = 0;
    }
    env->dc.win = &qio_field;
#endif
  }

  return (genvptr ? TUI_OK : TUI_ERROR);
}

VOID TuiShutdown()
{
  TWND wnd = genvptr->firstwnd;
  TWND temp = 0;
  
  while (wnd)
  {
    temp = wnd;
    wnd = wnd->nextwnd;
    TuiDestroyWnd(temp);
  }
  free(genvptr);
  genvptr = 0;
  
#ifdef __USE_CURSES__
  echo();
  nocbreak();
  endwin();
#elif defined __USE_QIO__
  /* QIO for VMS implement here */
#endif
}

twndproc_t* _TuiFindWndProc(LPCSTR clsname)
{
  TENV env = genvptr;
  twndproc_t* proc = env->firstproc;
  while (proc)
  {
    if (strcmp(proc->clsname, clsname) == 0)
    {
      break;
    }
    proc = proc->next;
  }
  return proc;
}

TENV TuiGetEnv()
{
  return genvptr;
}

VOID TuiSetNextMove(LONG nextmove)
{
  TENV env = genvptr;
  env->nextmove = nextmove;
}

UINT TuiGetDlgMsgID()
{
  TENV env = genvptr;
  return env->dlgmsgid;
}

TDC TuiGetDC(TWND wnd)
{
  static _TDC dc;
#ifdef __USE_CURSES__
  if (wnd)
  {
    dc.win = wnd->win;
  }
  else
  {
    dc.win = stdscr;
  }
#elif defined __USE_QIO__
  /* QIO for VMS implement here */
  if (wnd)
  {
    dc.win = wnd->win;
  }
  else
  {
    dc = genvptr->dc;
  }
#endif
  return (TDC)&dc;
}

LONG TuiRegisterCls(LPCSTR clsname, TWNDPROC wndproc)
{
  TENV env = genvptr;
  twndproc_t* proc = _TuiFindWndProc(clsname);

  if (!proc)
  {
    proc = (twndproc_t*)malloc(sizeof(twndproc_t));
    proc->clsname = clsname;
    proc->wndproc = wndproc;
    proc->prev    = 0;
    proc->next    = 0;

    if (env->firstproc)
    {
      proc->prev = env->lastproc;
      env->lastproc->next = proc;
      env->lastproc = proc;
    }
    else
    {
      env->firstproc = env->lastproc = proc;
    }
  }
  return TUI_OK;
}

LONG TuiMoveWnd(TWND wnd, INT y, INT x, INT lines, INT cols)
{
  LONG rc = TUI_OK;
  wnd->y = y;
  wnd->x = x;
  wnd->lines = lines;
  wnd->cols  = cols;
  return rc;
}

TWND _TuiCreateWnd(
  LPCSTR   clsname,
  LPCSTR   wndname,
  DWORD    style,
  INT      y,
  INT      x,
  INT      lines,
  INT      cols,
  TWND     parent,
  UINT      id,
  LPVOID   param
)
{
  TENV env = genvptr;
  TWND wnd = 0;
  LONG len = 0;
  twndproc_t* proc = _TuiFindWndProc(clsname);

  if ((style & TWS_WINDOW) &&
      (style & TWS_CHILD))
  {
    /* not allow creating both styles at a time */
    return wnd;
  }
  else if (style & TWS_CHILD && !parent)
  {
    /* not allow child without parent */
    return wnd;
  }

  if (proc)
  {
    wnd = (TWND)malloc(sizeof(_TWND));
    if (wnd)
    {
      memset(wnd, 0, sizeof(_TWND));
      wnd->clsname = clsname;
      len = strlen(wndname);
      if (wndname && len > 0)
      {
        if (len > TUI_MAX_WNDTEXT)
        {
          strncpy(wnd->wndname, wndname, TUI_MAX_WNDTEXT);
        }
        else
        {
          strcpy(wnd->wndname, wndname);
        }
      }
      wnd->style   = style;
      wnd->parent  = parent;
      wnd->id      = id;
      wnd->wndproc = proc->wndproc;
      wnd->enable  = (style & TWS_DISABLED ? 0 : 1);
      wnd->visible = (style & TWS_VISIBLE ? 1 : 0);
      wnd->validate = 0;
      /* curses */
#ifdef __USE_CURSES__
      wnd->win     = stdscr;
#elif defined __USE_QIO__
  /* QIO for VMS implement here */
      wnd->win     = &qio_field;
#endif
      wnd->attrs   = 0;

      /* make link */
      if (style & TWS_WINDOW)
      {
        if (env->firstwnd)
        {
          wnd->prevwnd = env->lastwnd;
          env->lastwnd->nextwnd = wnd;
          env->lastwnd = wnd;
        }
        else
        {
          env->firstwnd = env->lastwnd = wnd;
        }
        env->activewnd = wnd;
        env->dlgmsgid  = MB_INVALID;
#ifdef __USE_CURSES__
      wnd->y       = (y > 0 && y < LINES ? y : 0);
      wnd->x       = (x > 0 && x < COLS  ? x : 0);
      wnd->lines   = (lines > 0 && lines < LINES ? lines : LINES);
      wnd->cols    = (cols  > 0 && cols  < COLS  ? cols  : COLS);
#elif defined __USE_QIO__
  /* QIO for VMS implement here */
      wnd->y       = y;
      wnd->x       = x;
      wnd->lines   = lines;
      wnd->cols    = cols;
#endif
        
      }
      else if (style & TWS_CHILD)
      {
        if (parent->firstchild)
        {
          wnd->prevwnd = parent->lastchild;
          parent->lastchild->nextwnd = wnd;
          parent->lastchild = wnd;
        }
        else
        {
          parent->firstchild = parent->lastchild = wnd;
        }
        /* re-size window rectangle */
        wnd->y       = (parent->y + y);
        wnd->x       = (parent->x + x);
        wnd->lines   = (lines > parent->lines ? parent->lines : lines);
        wnd->cols    = (cols  > parent->cols  ? parent->cols  : cols);
        /* send message */
        TuiSendMsg(wnd, TWM_ERASEBK, (WPARAM)TuiGetDC(wnd), 0);
        TuiSendMsg(wnd, TWM_CREATE, 0, 0);
      }
    }
  }
  return wnd;
}

TWND TuiCreateWnd(
  LPCSTR   clsname,
  LPCSTR   wndname,
  DWORD    style,
  INT      y,
  INT      x,
  INT      lines,
  INT      cols,
  TWND     parent,
  INT      id,
  LPVOID   param
)
{
  LONG rc  = TUI_CONTINUE;
  TWND child = 0;
  TWND wnd = _TuiCreateWnd(
               clsname,
               wndname,
               style,
               y,
               x,
               lines,
               cols,
               parent,
               id,
               param
             );
  if (wnd)
  {
    TuiSendMsg(wnd, TWM_ERASEBK, (WPARAM)TuiGetDC(wnd), 0);
    rc = TuiSendMsg(wnd, TWM_CREATE, 0, (LPARAM)param);
    if (rc != TUI_CONTINUE)
    {
      if (wnd)
      {
        _TuiDestroyWnd(wnd);
      }
      wnd = 0;
    }
    child = TuiGetFirstActiveChildWnd(wnd);
    if (child)
    {
      wnd->activechild = child;
      TuiSetFocus(child);
    }
  }
  return wnd;
}

TWND TuiCreateWndTempl(
  WNDTEMPL* templs,
  LPVOID    param
)
{
  LONG rc  = TUI_CONTINUE;
  TWND wnd = 0;
  TWND child = 0;
  int  i = 0;

  if (templs)
  {
    wnd = _TuiCreateWnd(
               templs[i].clsname,
               templs[i].text,
               templs[i].style,
               templs[i].y,
               templs[i].x,
               templs[i].lines,
               templs[i].cols,
               0,
               0,
               param
             );
    if (wnd)
    {
      for (i = 1; templs[i].clsname; ++i)
      {
        child  = _TuiCreateWnd(
                   templs[i].clsname,
                   templs[i].text,
                   templs[i].style,
                   templs[i].y,
                   templs[i].x,
                   templs[i].lines,
                   templs[i].cols,
                   wnd,
                   templs[i].id,
                   0
                 );
        if (!child)
        {
          _TuiDestroyWnd(wnd);
          wnd = 0;
        }
        else
        {
          child->validate = (LONG (*)(TWND, LPCSTR))templs[i].validate;
        }
      } /* create children */
    } /* wnd is created successfully */

    if (wnd)
    {
      TuiSendMsg(wnd, TWM_ERASEBK, (WPARAM)TuiGetDC(wnd), 0);
      rc = TuiSendMsg(wnd, TWM_INITDIALOG, (WPARAM)0, (LPARAM)0);
      if (rc != TUI_CONTINUE)
      {
        _TuiDestroyWnd(wnd);
        wnd = 0;
      }
      child = TuiGetFirstActiveChildWnd(wnd);
      if (child)
      {
        wnd->activechild = child;
        TuiSetFocus(child);
      }
    }
  } /* templs is allocated */
  return wnd;
}

void _TuiDestroyWnd(TWND wnd)
{
  TWND child = (wnd ? wnd->firstchild : 0);
  TWND temp  = child;
  while (child)
  {
    child = child->nextwnd;
    free(temp);
    temp->nextwnd = temp->prevwnd = 0;
    temp  = child;
  }
  wnd->nextwnd = wnd->prevwnd = 0;
  free(wnd);
}

void TuiDestroyWnd(TWND wnd)
{
  TENV env = genvptr;
  /* remove the link if it is TWS_WINDOW */
  if (wnd->style & TWS_WINDOW)
  {
    env->lastwnd = wnd->prevwnd;
    if (env->lastwnd)
    {
      env->lastwnd->nextwnd = 0;
    }
    env->activewnd = env->lastwnd;
  }
  /* save the last message id */
  env->dlgmsgid = wnd->dlgmsgid;
  
  TuiSendMsg(wnd, TWM_DESTROY, 0, 0);
  _TuiDestroyWnd(wnd);
  
  if (env->activewnd)
  {
    TuiInvalidateWnd(env->activewnd);
  }
}

LONG TuiPostMsg(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  if (wnd)
  {
    tmsgq_t* msgq = (tmsgq_t*)malloc(sizeof(tmsgq_t));
    if (msgq)
    {
      TENV env = genvptr;

      memset(msgq, 0, sizeof(tmsgq_t));
      msgq->wnd = wnd;
      msgq->msg = msg;
      msgq->wparam = wparam;
      msgq->lparam = lparam;

      if (env->tailq)
      {
        env->tailq->next = msgq;
      }
      else
      {
        env->headq = env->tailq = msgq;
      }
      return TUI_OK;    
    }
  }
  return TUI_ERROR;
}

LONG TuiSendMsg(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  if (wnd)
  {
/*
    TENV env = genvptr;
    tmsgq_t* msgq = 0;

    while (env->headq)
    {
      msgq = env->headq;
      msgq->wnd->wndproc(msgq->wnd,
        msgq->msg,
        msgq->wparam,
        msgq->lparam);
      env->headq = env->headq->next;
      free(msgq);
    }
    env->headq = env->tailq = 0;
 */   
    return wnd->wndproc(wnd, msg, wparam, lparam);
  }
  return TUI_ERROR;
}

LONG _TuiInvalidateWnd(TWND wnd)
{
  TWND activechild = TuiGetActiveChildWnd(wnd);
  TWND child = wnd->firstchild;

  TuiSendMsg(wnd, TWM_ERASEBK, (WPARAM)TuiGetDC(wnd), 0);
  TuiSendMsg(wnd, TWM_PAINT, 0, 0);
  while (child)
  {
    TuiSendMsg(child, TWM_ERASEBK, (WPARAM)TuiGetDC(wnd), 0);
    TuiSendMsg(child, TWM_PAINT, 0, 0);
    child = child->nextwnd;
  }
  
  if (!activechild)
  {
    activechild = TuiGetFirstActiveChildWnd(wnd);
  }
  if (activechild)
  {
    TuiMoveYX(TuiGetDC(activechild), activechild->y, activechild->x);
  }
  return TUI_OK;
}

LONG TuiInvalidateWnd(TWND wnd)
{
  return _TuiInvalidateWnd(wnd);
}

LONG TuiShowWnd(TWND wnd, LONG show)
{
  wnd->visible = show;
  return _TuiInvalidateWnd(wnd);
}

LONG TuiEnableWnd(TWND wnd, LONG enable)
{
  wnd->enable = enable;
  return _TuiInvalidateWnd(wnd);
}

TWND TuiGetFirstActiveChildWnd(TWND wnd)
{
  if (wnd)
  {
    TWND activechild = wnd->activechild;
    TWND firstwnd = 0;

    wnd->activechild = wnd->lastchild;
    firstwnd = TuiGetNextActiveChildWnd(wnd);

    wnd->activechild = activechild;
    return firstwnd;
  }
  return 0;
}

TWND TuiGetLastActiveChildWnd(TWND wnd)
{
  if (wnd)
  {
    TWND activechild = wnd->activechild;
    TWND lastwnd = 0;

    wnd->activechild = wnd->firstchild;
    lastwnd = TuiGetPrevActiveChildWnd(wnd);

    wnd->activechild = activechild;
    return lastwnd;
  }
  return 0;
}

TWND TuiGetPrevActiveChildWnd(TWND wnd)
{
  TWND activechild = (wnd ? wnd->activechild : 0);
  if (!activechild && wnd)
  {
    activechild = wnd->lastchild;
  }
  else if (activechild)
  {
    activechild = activechild->prevwnd;
    if (!activechild && wnd)
    {
      activechild = wnd->lastchild;
    }
  }
  while (activechild)
  {
    if (TuiIsWndVisible(activechild))
    {
      if (TuiIsWndEnabled(activechild))
      {
        if (strcmp(activechild->clsname, STATIC) != 0)
        {
          break; /* found the next control */
        }
      }
    }
    activechild = activechild->prevwnd;
  }
  return activechild;
}

TWND TuiGetActiveChildWnd(TWND wnd)
{
  TWND parent = (wnd ? wnd->parent : 0);
  return (wnd ? wnd->activechild : parent);
}

TWND TuiGetNextActiveChildWnd(TWND wnd)
{
  TWND activechild = (wnd ? wnd->activechild : 0);
  if (!activechild && wnd)
  {
    activechild = wnd->firstchild;
  }
  else if (activechild)
  {
    activechild = activechild->nextwnd;
    if (!activechild && wnd)
    {
      activechild = wnd->firstchild;
    }
  }

  while (activechild)
  {
    if (TuiIsWndVisible(activechild))
    {
      if (TuiIsWndEnabled(activechild))
      {
        if (strcmp(activechild->clsname, STATIC) != 0)
        {
          break; /* found the next control */
        }
      }
    }
    activechild = activechild->nextwnd;
  }
  return activechild;
}

LONG TuiIsWndEnabled(TWND wnd)
{
  if (!wnd)
  {
    return TUI_ERROR;
  }
  return wnd->enable;
}

LONG TuiIsWndVisible(TWND wnd)
{
  if (!wnd)
  {
    return TUI_ERROR;
  }
  return wnd->visible;
}

TWND TuiGetWndItem(TWND wnd, INT id)
{
  TWND child = wnd->firstchild;
  while (child)
  { 
    if (child->id == id)
    {
      break;
    }
    child = child->nextwnd;
  }
  return child;
}

LONG TuiGetWndRect(TWND wnd, RECT* rect)
{
  rect->y = wnd->y;
  rect->x = wnd->x;
  rect->lines = wnd->lines;
  rect->cols  = wnd->cols;
  return TUI_OK;
}

DWORD TuiGetWndTextAttrs(TWND wnd)
{
  return wnd->attrs;
}

DWORD TuiSetWndTextAttrs(TWND wnd, DWORD newattrs)
{
  DWORD oldattrs = wnd->attrs;
  wnd->attrs = newattrs;
  return oldattrs;
}

DWORD TuiGetWndStyle(TWND wnd)
{
  return wnd->style;
}

DWORD TuiSetWndStyle(TWND wnd, DWORD newstyle)
{
  DWORD oldstyle = wnd->style;
  wnd->style = newstyle;
  return oldstyle;
}

UINT TuiGetWndID(TWND wnd)
{
  return wnd->id;
}

LPVOID TuiGetWndParam(TWND wnd)
{
  return wnd->param;
}
LPVOID TuiSetWndParam(TWND wnd, LPVOID newparam)
{
  LPVOID oldparam = wnd->param;
  wnd->param = newparam;
  return oldparam;
}

LONG   TuiIsWndValidate(TWND wnd, LPCSTR text)
{
  LONG rc = TUI_CONTINUE;
  if (wnd->validate)
  {
    rc = wnd->validate(wnd, text);
  }
  return rc;
}

VOID
TuiSetWndValidate(TWND wnd, LONG (*validate)(TWND, LPCSTR))
{
  wnd->validate = validate;
}

LONG TuiGetWndText(TWND wnd, LPSTR text, LONG cb)
{
  LONG len = (wnd ? strlen(wnd->wndname) : 0);
  if (cb < 0)
  {
    return len;
  }
  memset(text, 0, cb);
  if (cb > len)
  {
    cb = len;
  }
  strncpy(text, wnd->wndname, cb);
  return strlen(text);
}

VOID TuiSetWndText(TWND wnd, LPCSTR text)
{
  LONG len = 0;
  if (text && wnd)
  {
    len = strlen(text);
    if (len > TUI_MAX_WNDTEXT)
    {
      strncpy(wnd->wndname, text, TUI_MAX_WNDTEXT);
    }
    else
    {
      strcpy(wnd->wndname, text);
    }
    TuiInvalidateWnd(wnd);
  }
}

TWND TuiGetParent(TWND wnd)
{
  if (wnd)
  {
    return wnd->parent;
  }
  return 0;
}

/*-------------------------------------------------------------------
 * MSG functions
 *-----------------------------------------------------------------*/
#ifdef __USE_QIO__
VOID QIO_GetCh()
{         
  QIO_InitParameter();
  while(1)
  {
    QIO_GetKeyTerminal();
    switch(bProcess)
    {
      case ENDERR:
        printf("Program abort...\n");
        exit(0);
      case BEGPROC:
        QIO_InitParameter();
        break;
      case ENDSUCC:
        CheckProcessInput();
        if(CheckValue()== TRUE)
        {
          memset(qio_field.szDefault,0,SIZE_TEXT);
          return;
        }
    }
  }

}
#endif

LONG TuiGetMsg(MSG* msg)
{
  TENV env = genvptr;
  tmsgq_t* msgq = 0;

  if (env->quitcode)
  {
    return 0;
  }
  /* deque */
  while (env->headq)
  {
    msgq = env->headq;
    msgq->wnd->wndproc(msgq->wnd,
      msgq->msg,
      msgq->wparam,
      msgq->lparam);

    env->headq = env->headq->next;
    free(msgq);
  }
  env->tailq = env->headq; /* set to nil */

  memset(msg, 0, sizeof(MSG));
  msg->wnd = env->activewnd;
#ifdef __USE_CURSES__
  msg->wparam = wgetch(stdscr);
#elif defined __USE_QIO__
  /*printf("%s", sQioClear);*/
  /*QIO_Text(5, 1, 'W', sPrompt);*/

  qio_field.lType = QIO_ANY /*| QIO_UPPERCASE*/ | QIO_NO_ECHO;
  qio_field.cColor = 'W';
  /*sprintf(qio_field.szHelp, "%s", "Enter password");*/
  /*strcpy(qio_field.szHelp, "");*/
  qio_field.lLength = 1;
  qio_field.lRow = 1;
  qio_field.lColumn = 1;/*strlen(sPrompt)+1;*/
  qio_field.bReprint = 0;

  QIO_Get();
  /*printf("\n");*/
/*
  memcpy(sInput, qio_field.szResult, MAX_PUBKEY_LEN+MAX_LEN_PWD);
  */
  
  msg->wparam = qio_field.lTerm;
#endif

  return msg->wparam;
}

LONG _TuiAlignmentPrint(LPSTR out, LPSTR in, LONG limit, INT align)
{
  LONG len = 0;
  CHAR text[TUI_MAX_WNDTEXT+1];
  INT firstlen = 0;
  INT lastlen = 0;
  
  len = strlen(in);
  if (len > limit)
  {
    len = limit;
  }
  memset(text, 0, TUI_MAX_WNDTEXT);
  
  if ((ALIGN_CENTER == align) || (TWS_CENTER & align))
  {
    firstlen = (limit - len)/2;
    lastlen  = limit - (len + firstlen);
    if (firstlen > 0 && lastlen > 0)
    {
      sprintf(text, "%*s%s%*s",
        firstlen, " ",
        in,
        lastlen, " ");
    }
    else if (lastlen > 0)
    {
      sprintf(text, "%s%*s",
        in,
        lastlen, " ");
    }
    else
    {
      sprintf(text, "%s", in);
    }
  }
  else if ((ALIGN_RIGHT == align) || (TWS_RIGHT & align))
  {
    sprintf(text, "%*s",
      (INT)(limit),
      in);
  }
  else
  { 
    sprintf(text, "%-*s",
      (INT)(limit),
      in);
  }

  strcpy(out, text);
  return strlen(out);
}

LONG _TuiRemoveAllMsgs()
{
  TENV env = genvptr;
  tmsgq_t* msgq = 0;
  /* deque */
  while (env->headq)
  {
    msgq = env->headq;
    if (env->headq)
    {
      env->headq = env->headq->next;
    }
    msgq->next = 0;
    free(msgq);
  }
  env->tailq = env->headq = 0; 
  return TUI_OK;
}

LONG _TuiDequeMsg()
{
  TENV env = genvptr;
  tmsgq_t* msgq = 0;
  /* deque */
  while (env->headq)
  {
    msgq = env->headq;
    TuiSendMsg(msgq->wnd,
      msgq->msg,
      msgq->wparam,
      msgq->lparam);

    if (env->headq)
    {
      env->headq = env->headq->next;
    }
    msgq->next = 0;
    free(msgq);
  }
  env->tailq = env->headq = 0;
  return TUI_OK;
}

LONG TuiDispatchMsg(MSG* msg)
{
  TENV env = genvptr;
  TWND wnd = env->activewnd;
  TWND child = wnd->activechild;

  if (env->quitcode)
  {
    return TUI_OK;
  }

  if (child)
  {
    msg->wnd = child;
  }
  else if (wnd)
  {
    child = TuiGetFirstActiveChildWnd(wnd);
    if (child)
    {
      msg->wnd = child;
    }
    else
    {
      msg->wnd = wnd;
    }
  }

  /* deque message */
  _TuiDequeMsg();

  return TUI_OK;
}

LONG TuiTranslateMsg(MSG* msg)
{
  TENV env = genvptr;
  INT  ch  = (int)msg->wparam;
  LONG rc  = 0;
  TWND nextwnd = 0;
  TWND prevwnd = 0;
  TWND parent = 0;
  LONG nextmove = env->nextmove;
  LONG prevmove = env->prevmove;

  if (env->quitcode)
  {
    return TUI_OK;
  }
  /* send message to the current active window */
  if (!msg->wnd)
  {
    return TUI_CONTINUE;
  }

  /* convert keyboard code to message */
  switch (ch)
  {
#ifdef __USE_CURSES__
    /* not support mouse/resize events yet */
    case KEY_MOUSE:/* Mouse event has occurred */
#ifdef __LINUX__
    case KEY_RESIZE:/* Terminal resize event */
#endif

#elif defined __USE_QIO__
#endif
      return 0;
  }

  /*case TVK_ENTER:*/ /*ENTER*/
  if (ch == nextmove)
  {
    parent = TuiGetParent(msg->wnd);
    /* kill focus the current active window */
    nextwnd = TuiGetNextActiveChildWnd(parent);
    rc = TuiSendMsg(msg->wnd, TWM_KILLFOCUS, 0, (LPARAM)nextwnd);
    if (rc != TUI_CONTINUE)
    {
      return rc;
    }

    /* set focus the new active window */
    prevwnd = msg->wnd;
    if (parent)
    {
      if (msg->wnd == TuiGetLastActiveChildWnd(parent))
      {
        msg->wnd = TuiGetFirstActiveChildWnd(parent);
      }
      else
      {
        msg->wnd = nextwnd;
      }
      parent->activechild = msg->wnd;
    }
    else
    {
      msg->wnd = TuiGetNextActiveChildWnd(env->activewnd);
      env->activewnd->activechild = msg->wnd;
    }
    if (msg->wnd)
    {
      rc = TuiPostMsg(msg->wnd, TWM_SETFOCUS, 0, (LPARAM)prevwnd);
      TuiMoveYX(TuiGetDC(msg->wnd), msg->wnd->y, msg->wnd->x);
    }
    return 0;    
  }
  else if (ch == prevmove)
  {
    parent = TuiGetParent(msg->wnd);
    /* kill focus the current active window */
    prevwnd = TuiGetPrevActiveChildWnd(parent);
    rc = TuiSendMsg(msg->wnd, TWM_KILLFOCUS, 0, (LPARAM)prevwnd);
    if (rc != TUI_CONTINUE)
    {
      return rc;
    }

    /* set focus the new active window */
    nextwnd = msg->wnd;
    if (parent)
    {
      if (msg->wnd == TuiGetFirstActiveChildWnd(parent))
      {
        msg->wnd = TuiGetLastActiveChildWnd(parent);
      }
      else
      {
        msg->wnd = prevwnd;
      }
      parent->activechild = msg->wnd;
    }
    else
    {
      msg->wnd = TuiGetPrevActiveChildWnd(env->activewnd);
      env->activewnd->activechild = msg->wnd;
    }
    if (msg->wnd)
    {
      rc = TuiPostMsg(msg->wnd, TWM_SETFOCUS, 0, (LPARAM)nextwnd);
      TuiMoveYX(TuiGetDC(msg->wnd), msg->wnd->y, msg->wnd->x);
    }
    return 0;    
  }

  /* parent window */
  rc = TuiSendMsg(env->activewnd, TWM_KEYDOWN, msg->wparam, 0);
  rc = TuiSendMsg(env->activewnd, TWM_CHAR, msg->wparam, 0);
  rc = TuiSendMsg(env->activewnd, TWM_KEYUP, msg->wparam, 0);

  if (msg->wnd != env->activewnd)
  {
    rc = TuiSendMsg(msg->wnd, TWM_KEYDOWN, msg->wparam, 0);
    rc = TuiSendMsg(msg->wnd, TWM_CHAR, msg->wparam, 0);
    rc = TuiSendMsg(msg->wnd, TWM_KEYUP, msg->wparam, 0);
  }

  /* deque */
/*
  while (env->headq)
  {
    msgq = env->headq;
    TuiSendMsg(msgq->wnd,
      msgq->msg,
      msgq->wparam,
      msgq->lparam);

    env->headq = env->headq->next;
    free(msgq);
  }
  env->tailq = env->headq;
*/
  TuiDispatchMsg(msg);

  return 0;
}

LONG TuiPostQuitMsg(LONG exitcode)
{
  TENV env = genvptr;
  env->quitcode = 1;
  env->exitcode = exitcode;

  return TUI_OK;
}


LONG TuiSetFocus(TWND wnd)
{
  TENV env = genvptr;
  TWND parent = (wnd ? wnd->parent : env->activewnd);
  TWND activewnd = TuiGetActiveChildWnd(wnd);
  LONG rc = TUI_CONTINUE;

  /* kill focus at the current control */
  rc = TuiSendMsg(activewnd, TWM_KILLFOCUS, 0, (LPARAM)wnd);
  if (rc != TUI_CONTINUE)
  {
    return rc;
  }
  if (wnd)
  {
    rc = TuiSendMsg(wnd, TWM_SETFOCUS, 0, (LPARAM)activewnd);
    TuiMoveYX(TuiGetDC(wnd), wnd->y, wnd->x);  

    parent->activechild = wnd;
  }
  return rc;
}

TWND TuiGetActiveWnd()
{
  TENV env = genvptr;
  return env->activewnd;
}

TWND TuiGetFirstWnd()
{
  TENV env = genvptr;
  return env->firstwnd;
}

TWND TuiGetLastWnd()
{
  TENV env = genvptr;
  return env->lastwnd;
}

TWND TuiGetNextWnd(TWND wnd)
{
  return wnd->nextwnd;
}

TWND TuiGetPrevWnd(TWND wnd)
{
  return wnd->prevwnd;
}

LONG TuiPrintTextAlignment(LPSTR out, LPSTR in, LONG limit, INT align)
{
  return _TuiAlignmentPrint(out, in, limit, align);
}

/*-------------------------------------------------------------------
 * DefWndProc functions
 *-----------------------------------------------------------------*/
LONG _TuiDefWndProc_OnEraseBk(TWND wnd, TDC dc);
VOID _TuiDefWndProc_OnSetText(TWND wnd, LPCSTR text);
LONG _TuiDefWndProc_OnGetText(TWND wnd, LPSTR text, LONG cb);
VOID _TuiDefWndProc_OnSetTextAlign(TWND wnd, INT align);
VOID _TuiDefWndProc_OnSetTextAttrs(TWND wnd, DWORD attrs);

LONG _TuiDefWndProc_OnEraseBk(TWND wnd, TDC dc)
{
  INT i;
  CHAR buf[TUI_MAX_WNDTEXT + 1];
  RECT rc;
  DWORD attrs = TuiGetWndTextAttrs(wnd);
  
  if (TuiIsWndVisible(wnd))
  {
    TuiGetWndRect(wnd, &rc);
    memset(buf, ' ', rc.cols);
    buf[rc.cols] = 0;
    for (i = 0; i < rc.lines; ++i)
    {
      TuiDrawText(dc, rc.y + i, rc.x, buf, attrs);
    }
  }
  return TUI_OK;
}

VOID _TuiDefWndProc_OnSetText(TWND wnd, LPCSTR text)
{
  LONG len = 0;
  if (text)
  {
    len = strlen(text);
    if (len > TUI_MAX_WNDTEXT)
    {
      strncpy(wnd->wndname, text, TUI_MAX_WNDTEXT);
    }
    else
    {
      strcpy(wnd->wndname, text);
    }
  }
}

LONG _TuiDefWndProc_OnGetText(TWND wnd, LPSTR text, LONG cb)
{
  LONG len = strlen(wnd->wndname);
  if (cb < 0 || !text)
  {
    return len;
  }
  
  if (cb > len)
  {
    cb = len;
  }
  memset(text, 0, cb);
  strncpy(text, wnd->wndname, cb);
  return strlen(text);
}

VOID _TuiDefWndProc_OnSetTextAlign(TWND wnd, INT align)
{
  wnd->style &= ~(TWS_LEFT | TWS_CENTER | TWS_RIGHT);
  
  if (ALIGN_CENTER == align)
  {
    wnd->style |= TWS_CENTER;
  }
  else if (ALIGN_RIGHT == align)
  {
    wnd->style |= TWS_RIGHT;
  }
  else
  {
    wnd->style |= TWS_LEFT;
  }
}

VOID _TuiDefWndProc_OnSetTextAttrs(TWND wnd, DWORD attrs)
{
  wnd->attrs = attrs;
}

LONG TuiDefWndProc(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
    case TWM_CREATE:
    case TWM_KILLFOCUS:
      return TUI_CONTINUE;
      
    case TWM_ERASEBK:
    {
      return _TuiDefWndProc_OnEraseBk(wnd, (TDC)wparam);
    }
    
    case TWM_SETTEXT:
    {
      _TuiDefWndProc_OnSetText(wnd, (LPCSTR)lparam);
      return 0;
    }    
    case TWM_GETTEXT:
    {
      return _TuiDefWndProc_OnGetText(wnd, (LPSTR)lparam, (LONG)wparam);
    }
    case TWM_SETTEXTALIGN:
    {
      _TuiDefWndProc_OnSetTextAlign(wnd, (INT)wparam);
    }
    case TWM_SETTEXTATTRS:
    {
      _TuiDefWndProc_OnSetTextAttrs(wnd, (DWORD)wparam);
    }
  }
  return TUI_OK;
}

UINT TuiMsgBox(TWND parent, LPCSTR caption, LPCSTR text, UINT flags)
{
  MSG msg;
  UINT rc = MB_INVALID;
  TWND box = 0;
  RECT rect;
  LONG caplen = 0, textlen = 0, wndwidth = 0;
  LONG nbtns = 0;
  LONG btnwidth = 0;
  INT  i;
  
  enum
  {
    Msgbox  = 0,
    CaptionStatic,
    YesButton,
    NoButton,
    OKButton,
    CancelButton,
    EmptyCtl
  };
  WNDTEMPL msgbox[] =
  {
    { MSGBOX, text, 0,  0,  1,  7,  1, TWS_WINDOW, 0 },
    /* text */
    { STATIC, caption,  MB_CAPTION, 0,  0,  1,   1, TWS_CHILD|TWS_VISIBLE|TSS_CENTER, 0 },
    /* buttons */
    { BUTTON, "Yes",    MB_YES,     0,  0,  1,  11, TWS_CHILD, 0 },
    { BUTTON, "No",     MB_NO,      0,  0,  1,  10, TWS_CHILD, 0 },
    { BUTTON, "OK",     MB_OK,      0,  0,  1,  10, TWS_CHILD, 0 },
    { BUTTON, "Cancel", MB_CANCEL,  0,  0,  1,  10, TWS_CHILD, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0 }
  };

  /* calculate window width */
  if (flags & MB_YES)
  {
    msgbox[YesButton].style |= TWS_VISIBLE;
    ++nbtns;
    btnwidth += msgbox[YesButton].cols + 1;
  }
  else
  {
    msgbox[YesButton].style &= ~TWS_VISIBLE;
  }
  if (flags & MB_NO)
  {
    msgbox[NoButton].style |= TWS_VISIBLE;
    ++nbtns;
    btnwidth += msgbox[NoButton].cols + 1;
  }
  else
  {
    msgbox[NoButton].style &= ~TWS_VISIBLE;
  }
  if (flags & MB_OK)
  {
    msgbox[OKButton].style |= TWS_VISIBLE;
    ++nbtns;
    btnwidth += msgbox[OKButton].cols + 1;
  }
  else
  {
    msgbox[OKButton].style &= ~TWS_VISIBLE;
  }
  if (flags & MB_CANCEL)
  {
    msgbox[CancelButton].style |= TWS_VISIBLE;
    btnwidth += msgbox[CancelButton].cols + 1;
    ++nbtns;
  }
  else
  {
    msgbox[CancelButton].style &= ~TWS_VISIBLE;
  }
  
  TuiGetWndRect(parent, &rect);
  caplen   = strlen(caption);
  textlen  = strlen(text);
  wndwidth = MAX(btnwidth, MAX(caplen, textlen));
  if (wndwidth % 2 == 1)
  {
    ++wndwidth;
  }
  
  /* move window to center */
  msgbox[Msgbox].lines = 7;
  msgbox[Msgbox].y     = rect.y + (rect.lines - msgbox[Msgbox].lines)/2;
  msgbox[Msgbox].cols  = wndwidth + 10;
  msgbox[Msgbox].x     = rect.x + (rect.cols  - msgbox[Msgbox].cols)/2;
  
  /* move caption to center */
  msgbox[CaptionStatic].lines = 1;
  msgbox[CaptionStatic].y     = 1;
  msgbox[CaptionStatic].x     = 1;
  msgbox[CaptionStatic].cols  = msgbox[Msgbox].cols - 2;
  
  /* move buttons to center */
  if (nbtns > 3)
  {
  }
  else if (nbtns > 0)
  {
    INT x = (msgbox[Msgbox].cols - btnwidth)/(nbtns == 1 ? 2 : nbtns);
    for (i = YesButton; nbtns > 0 && i < EmptyCtl; ++i)
    {
      if (msgbox[i].style & TWS_VISIBLE)
      {
        /* move buttons */
        msgbox[i].lines = 1;
        msgbox[i].y     = 5;
        msgbox[i].x     = x;
        /* next button moved */
        x += msgbox[i].cols + 1;
        --nbtns;
      }
    }
  }
  
  box = TuiCreateWndTempl(msgbox, 0);
  if (!box)
  {
    return -1;
  }
  TuiShowWnd(box, TW_SHOW);

  /* remove all messages */
  _TuiRemoveAllMsgs();
  
  rc = TuiGetDlgMsgID();
  while (rc == MB_INVALID)
  {
    TuiGetMsg(&msg);
    TuiDispatchMsg(&msg);
    TuiTranslateMsg(&msg);
    
    rc = TuiGetDlgMsgID();
  }

  return rc;
}

UINT TuiEndDlg(TWND wnd, UINT id)
{
  wnd->dlgmsgid = id;
  TuiDestroyWnd(wnd);
  return id;

}

/*-------------------------------------------------------------------
 * File name: tdc.c
 * Author: Seree Rakwong
 * Date: 17-SEP-18
 *-----------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>

#ifdef __USE_CURSES__
#include <curses.h>
#elif (defined __USE_QIO__ && defined __VMS__)
#include <qio_init.h>
#endif

#include "tui.h"

/*-------------------------------------------------------------------
 * DC functions
 *-----------------------------------------------------------------*/

LONG TuiPutChar(
  TDC dc,
  INT y,
  INT x,
  CHAR   ch,
  DWORD  attrs)
{
#ifdef __USE_CURSES__
  /* set attributes on */
  attron(attrs);
  /* print now */
  mvwaddch(dc->win, y, x, ch);
  /* set attributes off */
  attroff(attrs);
#elif defined __USE_QIO__
  /* QIO for VMS implement here */
  dc->win->lType = QIO_ANY;
  dc->win->cColor = 'W';
  dc->win->lLength = 1;
  dc->win->lRow = y;
  dc->win->lColumn = x;
  strcpy( dc->win->szHelp, "");
  sprintf(dc->win->szText, "%c", ch);
  QIO_Put();
#endif
  return TUI_OK;
}

LONG TuiDrawTextEx(TDC dc, INT y, INT x, INT cols, LPCSTR text, LONG len, DWORD attrs, INT align)
{
  if (DT_CENTER == align)
  {
    if (len)
    {
      return TuiDrawText(dc, y, x + (cols - len) / 2, text, attrs);
    }
    else
    {
      return TuiDrawText(dc, y, x, text, attrs);
    }
  }
  else if (DT_RIGHT == align)
  {
    if (len < cols)
    {
      return TuiDrawText(dc, y, x + cols - len, text, attrs);
    }
    else
    {
      return TuiDrawText(dc, y, x, text, attrs);
    }
  }
  return TuiDrawText(dc, y, x, text, attrs);
}

LONG TuiDrawText(
  TDC dc,
  INT y,
  INT x,
  LPCSTR text,
  DWORD  attrs)
{
#ifdef __USE_CURSES__
  if (y >= LINES)
  {
    y = LINES - 1;
  }
  if (x >= COLS)
  {
    x = COLS - 1;
  }
  /* set attributes on */
  attron(attrs);
  /* print now */
  mvwprintw(dc->win, y, x, text);
  /* set attributes off */
  attroff(attrs);
#elif defined __USE_QIO__
  /* QIO for VMS implement here */
  dc->win->lType = QIO_ANY;/*QIO_INTEGER | QIO_UPPERCASE;*/
  dc->win->cColor = 'W';
  dc->win->lLength = strlen(text);
  dc->win->lRow = y;
  dc->win->lColumn = x;
  strcpy(dc->win->szHelp, "");
  strcpy(dc->win->szText, text);
  QIO_Put();
#endif
  return TUI_OK;
}

LONG TuiMoveYX(TDC dc, INT y, INT x)
{
#ifdef __USE_CURSES__
  wmove(dc->win, y, x);
#elif defined __USE_QIO__
  /* QIO for VMS implement here */
  dc->win->lRow    = y;
  dc->win->lColumn = x;
#endif
  return TUI_OK;
}


LONG TuiGetYX(TDC dc, INT* y, INT* x)
{
  int xx = 0, yy = 0;
#ifdef __USE_CURSES__
  getyx(dc->win, yy, xx);
#elif defined __USE_QIO__
  /* QIO for VMS implement here */
  yy = dc->win->lRow;
  xx = dc->win->lColumn;
#endif
  *y = yy;
  *x = xx;
  return TUI_OK;
}

/*-------------------------------------------------------------------
 * File name: tmsgbx.c
 * Author: Seree Rakwong
 * Date: 17-SEP-18
 *-----------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>

#ifdef __USE_CURSES__
#include <curses.h>
#elif (defined __USE_QIO__ && defined __VMS__)
#include <qio_init.h>
#endif

#include "tui.h"
/*-------------------------------------------------------------------
 * Message box functions
 *-----------------------------------------------------------------*/

VOID TMSGB_OnPaint(TWND wnd, TDC dc);

VOID TMSGB_OnPaint(TWND wnd, TDC dc)
{
  CHAR text[TUI_MAX_WNDTEXT+1];
  CHAR buf[TUI_MAX_WNDTEXT+1];
  RECT rc;
  
  TuiGetWndRect(wnd, &rc);
  TuiGetWndText(wnd, text, TUI_MAX_WNDTEXT);
  TuiPrintTextAlignment(buf, text, rc.cols, ALIGN_CENTER);
  TuiDrawText(dc, rc.y + 3, rc.x, buf, TuiGetWndTextAttrs(wnd));
}

LONG MSGBOXPROC(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
    case TWM_INITDIALOG:
    {
      TWND btn = TuiGetFirstActiveChildWnd(wnd);
      if (btn)
      {
        TuiSetFocus(btn);
      }
#ifdef __USE_CURSES__
      TuiSendMsg(wnd, TWM_SETTEXTATTRS, (WPARAM)(A_REVERSE), (LPARAM)0);
#endif
      return TUI_CONTINUE;
    }
    
    case TWM_PAINT:
    {
      TMSGB_OnPaint(wnd, TuiGetDC(wnd));
      return 0;
    }

    case TWM_COMMAND:
    {
      TuiEndDlg(wnd, (UINT)wparam);
      break;
    }
  }
  return TuiDefWndProc(wnd, msg, wparam, lparam);

}

/*-------------------------------------------------------------------
 * File name: tstc.c
 * Author: Seree Rakwong
 * Date: 17-SEP-18
 *-----------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>

#ifdef __USE_CURSES__
#include <curses.h>
#elif (defined __USE_QIO__ && defined __VMS__)
#include <qio_init.h>
#endif

#include "tui.h"

/*-------------------------------------------------------------------
 * STATIC functions
 *-----------------------------------------------------------------*/
VOID TSTC_OnPaint(TWND wnd, TDC dc);
LONG TSTC_OnCreate(TWND wnd);
LONG STATICPROC(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam);

VOID TSTC_OnPaint(TWND wnd, TDC dc)
{
  CHAR buf[TUI_MAX_WNDTEXT+1];
  CHAR text[TUI_MAX_WNDTEXT+1];
  RECT rc;

  if (TuiIsWndVisible(wnd))
  {
    TuiGetWndRect(wnd, &rc);
    
    TuiGetWndText(wnd, text, TUI_MAX_WNDTEXT);
    TuiPrintTextAlignment(buf, text, rc.cols, TuiGetWndStyle(wnd));
    TuiDrawText(dc, rc.y, rc.x, buf, TuiGetWndTextAttrs(wnd));
  }
}

LONG TSTC_OnCreate(TWND wnd)
{
#ifdef __USE_CURSES__
  TuiSetWndTextAttrs(wnd, COLOR_PAIR(GREEN_BLACK));
#elif defined __USE_QIO__
  TuiSetWndTextAttrs(wnd, GREEN_BLACK);
#endif
  
  return TUI_CONTINUE;
}

LONG STATICPROC(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
    case TWM_CREATE:
    {
      /* initial memory for static control */
      return TSTC_OnCreate(wnd);
    }
    case TWM_DESTROY:
    {
      /* release memory of static control */
      return 0;
    }
    case TWM_PAINT:
    {
      TSTC_OnPaint(wnd, TuiGetDC(wnd));
      return 0;
    }
  }
  return TuiDefWndProc(wnd, msg, wparam, lparam);

}

/*-------------------------------------------------------------------
 * File name: tbtn.c
 * Author: Seree Rakwong
 * Date: 17-SEP-18
 *-----------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>

#ifdef __USE_CURSES__
#include <curses.h>
#elif (defined __USE_QIO__ && defined __VMS__)
#include <qio_init.h>
#endif

#include "tui.h"

/*-------------------------------------------------------------------
 * BUTTON functions
 *-----------------------------------------------------------------*/
 struct _TBUTTONSTRUCT
{
  INT              state;
};
typedef struct _TBUTTONSTRUCT _TBUTTON;
typedef struct _TBUTTONSTRUCT *TBUTTON;

LONG TBTN_OnCreate(TWND wnd);
VOID TBTN_OnPaint(TWND wnd, TDC dc);
VOID TBTN_OnSetFocus(TWND wnd);
LONG TBTN_OnKillFocus(TWND wnd);
VOID TBTN_OnDestroy(TWND wnd);
VOID TBTN_OnKeyDown(TWND wnd, LONG ch);
LONG BUTTONPROC(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam);

VOID TBTN_OnPaint(TWND wnd, TDC dc)
{
  LONG len = 0;
  CHAR buf[TUI_MAX_WNDTEXT+1];
  CHAR text[TUI_MAX_WNDTEXT+1];
  DWORD attrs = TuiGetWndTextAttrs(wnd);
  RECT rc;
  TBUTTON btn = 0;
  
  btn = (TBUTTON)TuiGetWndParam(wnd);
  TuiGetWndRect(wnd, &rc);
  
  if (TuiIsWndVisible(wnd))
  {
    TuiGetWndText(wnd, text, TUI_MAX_WNDTEXT);
    len = TuiPrintTextAlignment(buf,
      text,
      rc.cols,
      ALIGN_CENTER);

    if (btn->state & TBS_FOCUSED)
    {
      buf[0] = '[';
      buf[1] = '[';
      buf[len-2] = ']';
      buf[len-1] = ']';
    }
    else
    {
      buf[0] = '[';
      buf[1] = ' ';
      buf[len-2] = ' ';
      buf[len-1] = ']';
    }
    
    if (!TuiIsWndEnabled(wnd))
    {
#ifdef __USE_CURSES__
      attrs = A_DIM|A_NORMAL;
#elif defined __USE_QIO__
      attrs = 0;
#endif
    }
    else if (btn->state == TBS_RELEASED)
    {
#ifdef __USE_CURSES__
      attrs = A_REVERSE;
#elif defined __USE_QIO__
      attrs = 0;
#endif
    }
    TuiDrawText(dc, rc.y, rc.x, buf, attrs);
  }
  TuiMoveYX(dc, rc.y, rc.x);  
}

LONG TBTN_OnCreate(TWND wnd)
{
  TBUTTON btn = 0;
  /* initial memory for static control */
  btn = (TBUTTON)malloc(sizeof(_TBUTTON));
  if (!btn)
  {
    return TUI_ERROR;
  }
  btn->state = TBS_RELEASED;
  
  TuiSetWndParam(wnd, (LPVOID)btn);
#ifdef __USE_CURSES__
  TuiSetWndTextAttrs(wnd, COLOR_PAIR(CYAN_BLACK));
#elif defined __USE_QIO__
  TuiSetWndTextAttrs(wnd, CYAN_BLACK);
#endif
  
  return TUI_CONTINUE;
}

VOID TBTN_OnSetFocus(TWND wnd)
{
  NMHDR nmhdr;
  TBUTTON btn = (TBUTTON)TuiGetWndParam(wnd);

  btn->state = TBS_FOCUSED; /* add state */
  TuiInvalidateWnd(wnd);
  
  /* send notification */
  nmhdr.id   = TuiGetWndID(wnd);
  nmhdr.ctl  = wnd;
  nmhdr.code = TBN_SETFOCUS;

  TuiPostMsg(TuiGetParent(wnd), TWM_NOTIFY, 0, (LPARAM)&nmhdr);
}

LONG TBTN_OnKillFocus(TWND wnd)
{
  NMHDR nmhdr;
  LONG rc = TUI_CONTINUE;
  TBUTTON btn = (TBUTTON)TuiGetWndParam(wnd);

  btn->state &= ~TBS_FOCUSED; /* remove state */
  TuiInvalidateWnd(wnd);
  
  /* send notification */
  nmhdr.id   = TuiGetWndID(wnd);
  nmhdr.ctl  = wnd;
  nmhdr.code = TBN_KILLFOCUS;
  TuiPostMsg(TuiGetParent(wnd), TWM_NOTIFY, 0, (LPARAM)&nmhdr);

  return rc;
}

VOID TBTN_OnDestroy(TWND wnd)
{
  TBUTTON btn = (TBUTTON)TuiGetWndParam(wnd);
   
  free(btn);
}

VOID TBTN_OnKeyDown(TWND wnd, LONG ch)
{
  TBUTTON btn = (TBUTTON)TuiGetWndParam(wnd);
  INT repaint = 0;
  
  switch (ch)
  {
    case TVK_SPACE:
      repaint = 1;
      break;
  }
  if (repaint)
  {
    btn->state = TBS_PRESSED;
    TuiInvalidateWnd(wnd);

    btn->state = TBS_RELEASED | TBS_FOCUSED;
    TuiInvalidateWnd(wnd);
    
    /* send notification */
    TuiPostMsg(TuiGetParent(wnd), 
      TWM_COMMAND,
      (WPARAM)TuiGetWndID(wnd),
      (LPARAM)wnd);
  }
}

LONG BUTTONPROC(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
    case TWM_CREATE:
    {
      /* initial memory for static control */
      return TBTN_OnCreate(wnd);
    }
    case TWM_DESTROY:
    {
      /* release memory of static control */
      TBTN_OnDestroy(wnd);
      return 0;
    }
    case TWM_SETFOCUS:
    {
      TBTN_OnSetFocus(wnd);
      break;
    }
    case TWM_KILLFOCUS:
    {
      return TBTN_OnKillFocus(wnd);
    }
    case TWM_KEYDOWN:
    {
      TBTN_OnKeyDown(wnd, (LONG)wparam);
      break;
    }
    case TWM_PAINT:
    {
      TBTN_OnPaint(wnd, TuiGetDC(wnd));
      return 0;
    }
  }
  return TuiDefWndProc(wnd, msg, wparam, lparam);
}


/*-------------------------------------------------------------------
 * File name: tedt.c
 * Author: Seree Rakwong
 * Date: 17-SEP-18
 *-----------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>

#ifdef __USE_CURSES__
#include <curses.h>
#elif (defined __USE_QIO__ && defined __VMS__)
#include <qio_init.h>
#endif

#include "tui.h"

/*-------------------------------------------------------------------
 * EDIT functions
 *-----------------------------------------------------------------*/

struct _TEDITSTRUCT
{
  CHAR             editbuf[TUI_MAX_WNDTEXT+1];
  INT              firstvisit;
  CHAR             passchar;
  INT              showpass;
  INT              firstchar;
  INT              limitchars;
  INT              editing;
  INT              decwidth;
};
typedef struct _TEDITSTRUCT _TEDIT;
typedef struct _TEDITSTRUCT *TEDIT;

LONG TEDT_OnCreate(TWND wnd);
VOID TEDT_OnDestroy(TWND wnd);
VOID TEDT_OnSetFocus(TWND wnd);
LONG TEDT_OnKillFocus(TWND wnd);
LONG TEDT_ValidateNumberStyle(TWND wnd, TEDIT edit, LONG ch);
LONG TEDT_ValidateDecimalStyle(TWND wnd, TEDIT edit, LONG ch);
VOID TEDT_OnChar(TWND wnd, LONG ch);
VOID TEDT_OnPaint(TWND wnd, TDC dc);
VOID TEDT_OnLimitText(TWND wnd, INT limit);
VOID TEDT_OnSetPasswdChar(TWND wnd, CHAR passchar);
VOID TEDT_OnShowPasswdChar(TWND wnd, INT show);
VOID TEDT_OnSetDecWidth(TWND wnd, INT width);
LONG EDITPROC(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam);


LONG TEDT_OnCreate(TWND wnd)
{
  TEDIT edit = 0;
  DWORD style = TuiGetWndStyle(wnd);
  RECT rc;
  
  /* initial memory for edit control */
  edit = (TEDIT)malloc(sizeof(_TEDIT));
  if (!edit)
  {
    return TUI_MEM;
  }
  
  TuiGetWndRect(wnd, &rc);
  TuiGetWndText(wnd, edit->editbuf, TUI_MAX_WNDTEXT);
  
  edit->firstvisit = 1;
  edit->firstchar  = 0;
  edit->passchar   = '*';
  edit->showpass   = TW_SHOW;
  edit->editing    = 0;
  edit->decwidth   = 6;
  edit->limitchars = (TES_AUTOHSCROLL & style ? TUI_MAX_WNDTEXT : rc.cols);
  
  TuiSetWndParam(wnd, (LPVOID)edit);
#ifdef __USE_CURSES__
  TuiSetWndTextAttrs(wnd, COLOR_PAIR(CYAN_BLACK));
#elif defined __USE_QIO__
  TuiSetWndTextAttrs(wnd, CYAN_BLACK);
#endif

  return TUI_CONTINUE;
}

VOID TEDT_OnDestroy(TWND wnd)
{
  TEDIT edit = 0;
  /* release memory of edit control */
  edit = (TEDIT)TuiGetWndParam(wnd);
  free(edit);
}

VOID TEDT_OnSetFocus(TWND wnd)
{
  TEDIT edit = 0;
  NMHDR nmhdr;
  /* save edited buffer */
  edit = (TEDIT)TuiGetWndParam(wnd);
  TuiGetWndText(wnd, edit->editbuf, TUI_MAX_WNDTEXT);
  edit->firstvisit = 1;
  edit->firstchar  = 0;
  edit->editing    = 0;
  
  /* send notification */
  nmhdr.id   = TuiGetWndID(wnd);
  nmhdr.ctl  = wnd;
  nmhdr.code = TEN_SETFOCUS;
  TuiPostMsg(TuiGetParent(wnd), TWM_NOTIFY, 0, (LPARAM)&nmhdr);
}

LONG TEDT_OnKillFocus(TWND wnd)
{
  TEDIT edit = 0;
  NMHDR nmhdr;
  LONG rc = TUI_CONTINUE;
  CHAR buf[TUI_MAX_WNDTEXT + 1];
  DOUBLE decimal = 0.0;
  DWORD style = TuiGetWndStyle(wnd);
  
  edit = (TEDIT)TuiGetWndParam(wnd);
  
  rc = TuiIsWndValidate(wnd, buf);
  if (rc != TUI_CONTINUE)
  {
    return rc;
  }  
  /* check if style is TES_DECIMAL */
  if (style & TES_DECIMAL)
  {
    decimal = atof(edit->editbuf);
    sprintf(buf, "%.*f", edit->decwidth, decimal);
    strcpy(edit->editbuf, buf);
  }
  /* update text */
  edit->firstchar = 0;
  edit->editing   = 0;
  TuiSetWndText(wnd, edit->editbuf);
  
  /* send notification */
  nmhdr.id   = TuiGetWndID(wnd);
  nmhdr.ctl  = wnd;
  nmhdr.code = TEN_KILLFOCUS;
  TuiPostMsg(TuiGetParent(wnd), TWM_NOTIFY, 0, (LPARAM)&nmhdr);
  
  TuiInvalidateWnd(wnd);
  return rc;
}

LONG TEDT_ValidateNumberStyle(TWND wnd, TEDIT edit, LONG ch)
{
  LONG rc = TUI_CONTINUE;
  if (ch < '0' || ch > '9')
  {
    rc = TUI_ERROR;
  }
  return rc;
}

LONG TEDT_ValidateDecimalStyle(TWND wnd, TEDIT edit, LONG ch)
{
  LONG rc = TUI_CONTINUE;
  CHAR* decimal = strchr(edit->editbuf, '.');
  LONG len = strlen(edit->editbuf);
  
  /* not allowed '-' in the string */
  if ((len > 0 && (edit->firstvisit == 0)) && ch == '-')
  {
    rc = TUI_ERROR;
  }
  if ((rc == TUI_CONTINUE) && (len == 0 || (edit->firstvisit == 1)) && ch == '-')
  {
    /* ok */
  }
  else if (ch == '.')
  {
    if (decimal)
    {
      /* not allowed having '.' more than one */
      rc = TUI_ERROR;
    }
  }
  else if (ch < '0' || ch > '9')
  {
    rc = TUI_ERROR;
  }
  else
  {
    
  }
  return rc;
}

VOID TEDT_OnChar(TWND wnd, LONG ch)
{
  TEDIT edit = 0;
  NMHDR nmhdr;
  INT changed = 0;
  TDC dc = TuiGetDC(wnd);
  LONG len = 0;
  CHAR buf[TUI_MAX_WNDTEXT+1];
  DWORD attrs = TuiGetWndTextAttrs(wnd);
  CHAR text[TUI_MAX_WNDTEXT+1];
  DWORD style = TuiGetWndStyle(wnd);
  RECT rc;
  LONG  ret = TUI_CONTINUE;
  
  edit = (TEDIT)TuiGetWndParam(wnd);
  
  if (TuiIsWndEnabled(wnd))
  {
#ifdef __USE_CURSES__
    attrs |= A_UNDERLINE;
#elif defined __USE_QIO__
    attrs |= 0;
#endif
  }

  TuiGetWndText(wnd, text, TUI_MAX_WNDTEXT);
  TuiGetWndRect(wnd, &rc);
  
  if (ch == TVK_ESCAPE)
  {
    if (edit->firstvisit == 0)
    {
      edit->firstvisit = 1;
      strcpy(edit->editbuf, text);
      edit->firstchar  = 0;
      edit->editing    = 0;
      TuiInvalidateWnd(wnd);
      return;
    }
  }
  if (ch >= 0x20 && ch < 0x7f)
  {
    /* add char */
    len = strlen(edit->editbuf);
    if (len + 1 <= edit->limitchars)
    {
      if (TES_NUMBER & style)
      {
        /* require only number input */
        ret = TEDT_ValidateNumberStyle(wnd, edit, ch);
        if (ret != TUI_CONTINUE)
        {
          return;
        }
      }
      else if (TES_DECIMAL & style)
      {
        /* require only decimal input */
        ret = TEDT_ValidateDecimalStyle(wnd, edit, ch);
        if (ret != TUI_CONTINUE)
        {
          return;
        }
      }
      else if (TES_UPPERCASE & style)
      {
        /* require changing from small to CAPITAL */
        if (ch >= 'a' && ch <= 'z')
        {
          ch = ch - 'a' + 'A';
        }
      }
      else if (TES_LOWERCASE & style)
      {
        if (ch >= 'A' && ch <= 'Z')
        {
          ch = ch - 'A' + 'a';
        }
      }
      if (edit->firstvisit == 1)
      {
        edit->firstvisit = 0;
        if (!(TES_APPENDMODE & style) || (TES_PASSWORD & style)) /* replace mode */
        {
          edit->editbuf[0] = 0;
          memset(buf, ' ', rc.cols);
          buf[rc.cols] = 0;
          TuiDrawText(dc, rc.y, rc.x, buf, attrs);
          TuiMoveYX(dc, rc.y, rc.x);
        }
        else if (TES_AUTOHSCROLL & style)
        {
          /* show first char at */
          if (len > rc.cols)
          {
            edit->firstchar = len - rc.cols - 1;
          }
        }
      }
      
      /* append a new char */
      len = strlen(edit->editbuf);
      if (len < edit->limitchars)
      {
        edit->editbuf[len]   = ch;
        edit->editbuf[len+1] = 0;
        if (len < rc.cols)
        {
          if (TES_PASSWORD & style)
          {
            if (edit->showpass == TW_SHOW)
            {
              TuiPutChar(dc, rc.y, rc.x + len, 
                edit->passchar,
                attrs);
            }
          }
          else
          {
            TuiPutChar(dc, rc.y, rc.x + len, 
              ch,
              attrs);
          }
        }
        changed = 1;
      } /* len < limit */
      
      if (TES_AUTOHSCROLL & style)
      {
        len = strlen(edit->editbuf);
        if (len <= edit->limitchars && len > rc.cols && changed)
        {
          ++edit->firstchar;
          if (edit->firstchar > edit->limitchars - rc.cols)
          {
            edit->firstchar = edit->limitchars - rc.cols - 1;
          }
          TuiInvalidateWnd(wnd);
          if ((TES_PASSWORD & style) && (edit->showpass == TW_SHOW))
          {
            TuiMoveYX(dc, rc.y, rc.x);
          }
          else
          {
            TuiMoveYX(dc, rc.y, rc.x + rc.cols);
          }
        }
      }

      /* editing */      
      edit->editing    = 1;

    } /*TUI_MAX_WNDTEXT*/
  }
  else if (ch == 0x7f || ch == TVK_DELETE || 
#ifdef __USE_CURSES__
  ch == KEY_BACKSPACE) /* delete char */
#elif defined __USE_QIO__
  ch == KEY_BS)
#endif
  {
    len = strlen(edit->editbuf);
    if (edit->firstvisit == 1)
    {
      edit->firstvisit = 0;
      if (!(TES_APPENDMODE & style) || TES_PASSWORD & style) /* replace mode */
      {
        edit->editbuf[0] = 0;
        sprintf(buf, "%*s", rc.cols, " ");
        TuiMoveYX(dc, rc.y, rc.x);
        TuiDrawText(dc, rc.y, rc.x, buf, attrs);
      }
      else if (TES_AUTOHSCROLL & style)
      {
        /* show first char at */
        if (len > rc.cols)
        {
          edit->firstchar = len - rc.cols - 1;
        }
      }
    }
    
    if (len > 0)
    {
      if (len > rc.cols)
      {
        TuiPutChar(dc, rc.y, rc.x + rc.cols - 1, ' ', attrs);
      }
      else
      {
        TuiPutChar(dc, rc.y, rc.x + len - 1, ' ', attrs);
      }
      TuiMoveYX(dc, rc.y, rc.x + len - 1);
      edit->editbuf[len - 1] = 0;
      changed = 1;
      
      if (TES_AUTOHSCROLL & style)
      {
        len = strlen(edit->editbuf);
        if (len >= rc.cols)
        {
          edit->firstchar = len - rc.cols;
          if (edit->firstchar < 0)
          {
            edit->firstchar = 0;
          }
          len = rc.cols;
        }
        else
        {
          edit->firstchar = 0;
        }
        TuiInvalidateWnd(wnd);
        TuiMoveYX(dc, rc.y, rc.x + len);
      }
    }      

    /* editing */      
    edit->editing    = 1;
  }
  if (changed)
  {
    nmhdr.id   = TuiGetWndID(wnd);
    nmhdr.ctl  = wnd;
    nmhdr.code = TEN_CHANGED;
    TuiPostMsg(TuiGetParent(wnd), TWM_NOTIFY, 0, (LPARAM)&nmhdr);
  }
}

VOID TEDT_OnPaint(TWND wnd, TDC dc)
{
  TEDIT edit = 0;
  LONG len = 0;
  CHAR buf[TUI_MAX_WNDTEXT+1];
  CHAR text[TUI_MAX_WNDTEXT+1];
  DWORD attrs = TuiGetWndTextAttrs(wnd);
  RECT rc;
  DWORD style = TuiGetWndStyle(wnd);
  
  edit = (TEDIT)TuiGetWndParam(wnd);
    
  if (TuiIsWndEnabled(wnd))
  {
#ifdef __USE_CURSES__
    attrs |= A_UNDERLINE;
#elif defined __USE_QIO__
    attrs |= 0;
#endif
  }
  
  TuiGetWndRect(wnd, &rc);
  len = rc.cols;
  if (TES_PASSWORD & style)
  {
    if (edit->showpass == TW_SHOW)
    {
      memset(buf, edit->passchar, len);
    }
    else
    {
      memset(buf, ' ', len);
    }
    buf[len] = 0;
    TuiDrawText(dc, rc.y, rc.x, buf, attrs);
    return;
  }

  if (TuiIsWndVisible(wnd))
  {
    len = MIN(strlen(edit->editbuf), rc.cols);
    memset(buf, 0, TUI_MAX_WNDTEXT);
    memcpy(buf, &edit->editbuf[edit->firstchar], len);
    
    if (edit->editing)
    {
      TuiPrintTextAlignment(text, buf, rc.cols, ALIGN_LEFT);
    }
    else
    {
      TuiPrintTextAlignment(text, buf, rc.cols, style);
    }
    TuiDrawText(dc, rc.y, rc.x, text, attrs);
  }
  TuiMoveYX(dc, rc.y, rc.x);
}

VOID TEDT_OnLimitText(TWND wnd, INT limit)
{
  TEDIT edit = 0;
  CHAR text[TUI_MAX_WNDTEXT + 1];
  
  if (limit > 0 || limit <= TUI_MAX_WNDTEXT)
  {
    edit = (TEDIT)TuiGetWndParam(wnd);
    edit->limitchars = limit;
    
    TuiGetWndText(wnd, text, TUI_MAX_WNDTEXT);
    if (strlen(text) > limit)
    {
      text[limit] = 0;
      TuiSetWndText(wnd, text);
      strcpy(edit->editbuf, text);
    }
  }
}

VOID TEDT_OnSetPasswdChar(TWND wnd, CHAR passchar)
{
  TEDIT edit = 0;
  
  edit = (TEDIT)TuiGetWndParam(wnd);
  edit->passchar = passchar;
}

VOID TEDT_OnShowPasswdChar(TWND wnd, INT show)
{
  TEDIT edit = 0;
  
  edit = (TEDIT)TuiGetWndParam(wnd);
  edit->showpass = (show == TW_SHOW ? TW_SHOW : TW_HIDE);
}

VOID TEDT_OnSetDecWidth(TWND wnd, INT width)
{
  TEDIT edit = 0;
  
  edit = (TEDIT)TuiGetWndParam(wnd);
  edit->decwidth = width;
}

LONG EDITPROC(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
    case TWM_CREATE:
    {
      return TEDT_OnCreate(wnd);
    }
    case TWM_DESTROY:
    {
      TEDT_OnDestroy(wnd);
      return 0;
    }
    case TWM_SETFOCUS:
    {
      TEDT_OnSetFocus(wnd);
      break;
    }
    case TWM_KILLFOCUS:
    {
      return TEDT_OnKillFocus(wnd);
    }
    case TWM_CHAR:
    {
      TEDT_OnChar(wnd, (LONG)wparam);
      break;
    }
    case TWM_PAINT:
    {
      TEDT_OnPaint(wnd, TuiGetDC(wnd));
      return 0;
    }
    case TEM_LIMITTEXT:
    {
      TEDT_OnLimitText(wnd, (INT)wparam);
      return 0;
    }
    case TEM_SETPASSWDCHAR:
    {
      TEDT_OnSetPasswdChar(wnd, (CHAR)wparam);
      return 0;
    }
    case TEM_SETDECWIDTH:
    {
      TEDT_OnSetDecWidth(wnd, (INT)wparam);
      return 0;
    }
    case TEM_SHOWPASSWDCHAR:
    {
      TEDT_OnShowPasswdChar(wnd, (INT)wparam);
      return 0;
    }
  }
  return TuiDefWndProc(wnd, msg, wparam, lparam);
}

/*-------------------------------------------------------------------
 * File name: tlb.c
 * Author: Seree Rakwong
 * Date: 17-SEP-18
 *-----------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>

#ifdef __USE_CURSES__
#include <curses.h>
#elif (defined __USE_QIO__ && defined __VMS__)
#include <qio_init.h>
#endif

#include "tui.h"

/*-------------------------------------------------------------------
 * LISTBOX functions
 *-----------------------------------------------------------------*/

struct _TLISTBOXITEMSTRUCT
{
  CHAR      itemtext[TUI_MAX_WNDTEXT+1];
  LPVOID    data;
  INT       checked;
  struct _TLISTBOXITEMSTRUCT *prev;
  struct _TLISTBOXITEMSTRUCT *next;
};
typedef struct _TLISTBOXITEMSTRUCT tlistbox_t;

struct _TUILISTBOXSTRUCT
{
  INT           firstvisible;
  INT           nitems;        /* item counts */
  INT           cursel;       /* highlight item */

  tlistbox_t*   firstitem;    /* always be item id = 0 if it is existing */
  tlistbox_t*   lastitem;

  tlistbox_t*   selitem;      /* must be the same cursel item */
  tlistbox_t*   firstvisibleitem;

  INT           checkeditems;    /* count item checked */
  tlistbox_t*   lastitemchecked; /* to identify the last item checked */
};
typedef struct _TUILISTBOXSTRUCT _TLISTBOX;
typedef struct _TUILISTBOXSTRUCT *TLISTBOX;

tlistbox_t* _TLB_FindItemByIndex(TWND wnd, INT idx);

INT TLB_OnCountItemCheck(TWND wnd);
INT TLB_OnGetItemChecked(TWND wnd, INT idx);
INT TLB_OnSetItemChecked(TWND wnd, INT idx, INT check);
LPVOID TLB_OnGetItemData(TWND wnd, INT idx);
VOID TLB_OnSetItemData(TWND wnd, INT idx, LPVOID data);
VOID TLB_OnSetCurSel(TWND wnd, INT idx);
LONG TLB_OnGetItemCount(TWND wnd);
VOID TLB_OnSetItemText(TWND wnd, INT idx, LPSTR text);
LONG TLB_OnGetItemText(TWND wnd, INT idx, LPSTR text);
INT TLB_OnGetCurSel(TWND wnd);
VOID TLB_OnDeleteAllItems(TWND wnd);
VOID TLB_OnDeleteItem(TWND wnd, LONG idx);
LONG TLB_OnAddItem(TWND wnd, LPCSTR text);
VOID TLB_OnPaint(TWND wnd, TDC dc);
VOID TLB_OnKeyDown(TWND wnd, LONG ch);
LONG TLB_OnKillFocus(TWND wnd);
VOID TLB_OnSetFocus(TWND wnd);
VOID TLB_OnDestroy(TWND wnd);
LONG TLB_OnCreate(TWND wnd);
LONG LISTBOXPROC(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam);


tlistbox_t* _TLB_FindItemByIndex(TWND wnd, INT idx)
{
  TLISTBOX lb = 0;
  INT i = 0;
  tlistbox_t* item = 0;
  
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  if (idx < 0 || idx >= lb->nitems)
  {
    return 0;
  }
  item = lb->firstitem;
  
  for (i = 0; i < lb->nitems && item; ++i, item = item->next)
  {
    if (i == idx)
    {
      return item;
    }
  }
  return 0;
}

LONG TLB_OnCreate(TWND wnd)
{
  TLISTBOX lb = 0;
  /* initial memory for static control */
  lb = (TLISTBOX)malloc(sizeof(_TLISTBOX));
  if (!lb)
  {
    return TUI_ERROR;
  }
  lb->firstvisible = -1;
  lb->cursel       = -1;
  lb->nitems        = 0;
  lb->firstitem    = 0;
  lb->lastitem     = 0;
  lb->selitem      = 0;
  lb->firstvisibleitem = 0;
  lb->checkeditems = 0;
  lb->lastitemchecked  = 0;
  
  TuiSetWndParam(wnd, (LPVOID)lb);
#ifdef __USE_CURSES__
  TuiSetWndTextAttrs(wnd, COLOR_PAIR(YELLOW_BLUE));
#elif defined __USE_QIO__
  TuiSetWndTextAttrs(wnd, YELLOW_BLUE);
#endif
  
  return TUI_CONTINUE;
}

VOID TLB_OnDestroy(TWND wnd)
{
  TLISTBOX lb = 0;
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  
  /* release memory of static control */
  TLB_DeleteAllItems(wnd);
  
  free(lb);
}

VOID TLB_OnSetFocus(TWND wnd)
{
  TLISTBOX lb = 0;
  NMHDR nmhdr;
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  if (lb->cursel >= lb->nitems)
  {
    lb->cursel = 0;
  }
  else if (lb->cursel < 0)
  {
    lb->cursel = 0;
  }
  TuiInvalidateWnd(wnd);
  
  /* send notification */
  nmhdr.id   = TuiGetWndID(wnd);
  nmhdr.ctl  = wnd;
  nmhdr.code = TLBN_SETFOCUS;
  TuiPostMsg(TuiGetParent(wnd), TWM_NOTIFY, 0, (LPARAM)&nmhdr);
}

LONG TLB_OnKillFocus(TWND wnd)
{
  NMHDR nmhdr;
  LONG rc = TUI_CONTINUE;
  
  /* send notification */
  nmhdr.id   = TuiGetWndID(wnd);
  nmhdr.ctl  = wnd;
  nmhdr.code = TLBN_KILLFOCUS;
  TuiPostMsg(TuiGetParent(wnd), TWM_NOTIFY, 0, (LPARAM)&nmhdr);
  return rc;
}

VOID TLB_OnKeyDown(TWND wnd, LONG ch)
{
  TLISTBOX lb = 0;
  NMHDR nmhdr;
  INT repaint = 0;
  DWORD style = TuiGetWndStyle(wnd);
  INT lines = 0;
  RECT rc;
  
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  TuiGetWndRect(wnd, &rc);
  switch (ch)
  {
    case TVK_SPACE:
    {
      if ((style & TLBS_CHECKBOX) ||
          (style & TLBS_RADIOBOX))
      {
        /* get current and check it */
        TLB_SetItemChecked(wnd, lb->cursel, LB_CHECKED);
      }
      ++repaint;
      break;
    }

    case KEY_DOWN:
    case KEY_RIGHT:
    {
      ++lines;
      ++repaint;
      break;
    }
  
    case KEY_UP:
    case KEY_LEFT:
    {
      --lines;
      ++repaint;
      break;
    }
      
    case TVK_PRIOR:
#ifdef __USE_CURSES__
    case KEY_PPAGE:
#elif (defined __USE_QIO__ && defined __VMS__)
    case KEY_PREV:
#endif
    {
      lines -= rc.lines;
      ++repaint;
      break;
    }
    
    case TVK_NEXT:
#ifdef __USE_CURSES__
    case KEY_NPAGE:
#elif (defined __USE_QIO__ && defined __VMS__)
    case KEY_NEXT:
#endif
    {
      lines += rc.lines;
      ++repaint;
      break;
    }
  }
  if (repaint)
  {
    lb->cursel += lines;
    if (lb->cursel >= lb->nitems)
    {
      lb->cursel = lb->nitems - 1;
    }
    else if (lb->cursel < 0)
    {
      lb->cursel = 0;
    }
    /* find the new first visible */
    if (lb->cursel >= lb->firstvisible + rc.lines)
    {
      lb->firstvisible += lines;
      if (lb->firstvisible > lb->nitems - rc.lines)
      {
        lb->firstvisible = lb->nitems - rc.lines;
      }
    }
    else if (lb->firstvisible > lb->cursel)
    {
      lb->firstvisible += lines;
      if (lb->firstvisible < 0)
      {
        lb->firstvisible = 0;
      }
    }
    
    TuiInvalidateWnd(wnd);
    /* send notification */
    nmhdr.id   = TuiGetWndID(wnd);
    nmhdr.ctl  = wnd;
    nmhdr.code = TLBN_SELCHANGED;
    TuiPostMsg(TuiGetParent(wnd), TWM_NOTIFY, 0, (LPARAM)&nmhdr);

  }
  lb->selitem = _TLB_FindItemByIndex(wnd, lb->cursel);
  lb->firstvisibleitem = _TLB_FindItemByIndex(wnd, lb->firstvisible);
}

VOID TLB_OnPaint(TWND wnd, TDC dc)
{
  TLISTBOX lb = 0;
  INT i = 0;
  tlistbox_t* item = 0;
  CHAR  buf[TUI_MAX_WNDTEXT+1];
  CHAR  text[TUI_MAX_WNDTEXT+1];
  DWORD attrs = TuiGetWndTextAttrs(wnd);
  DRAWITEM di;
  RECT rc;
  DWORD style = TuiGetWndStyle(wnd);
  
  if (!TuiIsWndVisible(wnd))
  {
    return;
  }
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  
  /* draw */
  if (lb->nitems > 0)
  {
    item = lb->firstitem;
    TuiGetWndRect(wnd, &rc);
    for (i = 0; i < lb->nitems && item; ++i, item = item->next)
    {
      if (i < lb->firstvisible)
      {
        /* do nothing */
        continue;
      }
      else if (i - lb->firstvisible < rc.lines)
      {
        if (style & TLBS_OWNERDRAW)
        {
          memset(&di, 0, sizeof(DRAWITEM));
          di.rcitem.y = rc.y + (i - lb->firstvisible);
          di.rcitem.x = rc.x;
          di.rcitem.lines = 1;
          di.rcitem.cols  = rc.cols;
          di.idx          = i;
          
          TuiSendMsg(TuiGetParent(wnd), TWM_DRAWITEM, (WPARAM)i, (LPARAM)&di);
        }
        else
        {
          strcpy(text, "");
          if (style & TLBS_CHECKBOX)
          {
            if (item->checked)
            {
              strcpy(text, "[X] ");
            }
            else
            {
              strcpy(text, "[ ] ");
            }
          }
          else if (style & TLBS_RADIOBOX)
          {
            if (item->checked)
            {
              strcpy(text, "(*) ");
            }
            else
            {
              strcpy(text, "( ) ");
            }
          }          
          
          /* copy from item text */
          strcat(text, item->itemtext);
          TuiPrintTextAlignment(buf, text, rc.cols, style);
          
          
          TuiDrawText(dc, 
            rc.y+(i-lb->firstvisible), 
            rc.x, 
            buf, 
#ifdef __USE_CURSES__
            (i == lb->cursel ? A_REVERSE|attrs : attrs));
#elif defined __USE_QIO__
            0); /* highlight */
#endif
        }
      }/* not owner draw */
    } /* for each item */
  } /* items are valid */
}

LONG TLB_OnAddItem(TWND wnd, LPCSTR text)
{
  TLISTBOX lb = 0;
  LONG len = 0;
  tlistbox_t* item = 0;
  
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  item = (tlistbox_t*)malloc(sizeof(tlistbox_t));
  if (item)
  {
    memset(item, 0, sizeof(tlistbox_t));
    len = strlen(text);
    if (len > TUI_MAX_WNDTEXT)
    {
      len = TUI_MAX_WNDTEXT;
    }
    strncpy(item->itemtext, text, len);
  
    if (lb->firstitem)
    {
      item->prev = lb->lastitem;
      lb->lastitem->next = item;
      lb->lastitem = item;
    }
    else
    {
      lb->firstitem = lb->lastitem = item;
      lb->firstvisible = 0;
    }
    ++lb->nitems;
    return lb->nitems;
  }
  return TUI_MEM;
}

VOID TLB_OnDeleteItem(TWND wnd, LONG idx)
{
  TLISTBOX lb = 0;
  tlistbox_t* item = 0;
  tlistbox_t* nextitem = 0;
  
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  item = _TLB_FindItemByIndex(wnd, idx);
  
  if (item)
  {
    /* remove the links */
    nextitem = item->next;
    if (nextitem)
    {
      nextitem->prev = item->prev;
    }
    if (item->prev)
    {
      item->prev->next = nextitem;
    }
    /* check if removed item is the first item or the last item */
    if (item == lb->firstitem)
    {
      lb->firstitem = nextitem;
    }
    else if (item == lb->lastitem)
    {
      lb->lastitem = item->prev;
    }
    
    /* free the memory */
    item->next = item->prev = 0;
    free(item);
    
    /* decrement items */
    --lb->nitems;
    if (lb->cursel >= lb->nitems)
    {
      lb->cursel = lb->nitems - 1;
    }
  }
}

VOID TLB_OnDeleteAllItems(TWND wnd)
{
  TLISTBOX lb = 0;
  INT nitems = 0;
  INT i = 0;
  
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  nitems = lb->nitems;
  for (i = 0; i < nitems; ++i)
  {
    TuiPostMsg(wnd, TLBM_DELETEITEM, 0, 0);
  }
}

INT TLB_OnGetCurSel(TWND wnd)
{
  TLISTBOX lb = 0;
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  return lb->cursel;
}

LONG TLB_OnGetItemText(TWND wnd, INT idx, LPSTR text)
{
  tlistbox_t* item = 0;
  
  item = _TLB_FindItemByIndex(wnd, idx);
  
  if (item)
  {
    strcpy(text, item->itemtext);
  }
  return strlen(text);
}

VOID TLB_OnSetItemText(TWND wnd, INT idx, LPSTR text)
{
  tlistbox_t* item = 0;
  
  item = _TLB_FindItemByIndex(wnd, idx);  
  if (item)
  {
    strcpy(item->itemtext, text);
  }
}

LONG TLB_OnGetItemCount(TWND wnd)
{
  TLISTBOX lb = 0;
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  return lb->nitems;
}

VOID TLB_OnSetCurSel(TWND wnd, INT idx)
{
  TLISTBOX lb = 0;
  NMHDR nmhdr;
  RECT rc;
  
  lb = (TLISTBOX)TuiGetWndParam(wnd);

  if (idx < 0 || idx >= lb->nitems)
  {
    idx = -1;
  }
  lb->cursel = idx;
  
  if (lb->cursel >= 0)
  {
    lb->firstvisible = lb->cursel;
    TuiGetWndRect(wnd, &rc);
    if (lb->firstvisible + rc.lines > lb->nitems)
    {
      lb->firstvisible = lb->nitems - rc.lines;
    }
  }
  else
  {
    lb->firstvisible = 0;
  }
  
  TuiInvalidateWnd(wnd);
  /* send notification */
  nmhdr.id   = TuiGetWndID(wnd);
  nmhdr.ctl  = wnd;
  nmhdr.code = TLBN_SELCHANGED;
  TuiPostMsg(TuiGetParent(wnd), TWM_NOTIFY, 0, (LPARAM)&nmhdr);

  lb->selitem = _TLB_FindItemByIndex(wnd, lb->cursel);
  lb->firstvisibleitem = _TLB_FindItemByIndex(wnd, lb->firstvisible);
}

VOID TLB_OnSetItemData(TWND wnd, INT idx, LPVOID data)
{
  tlistbox_t* item = 0;
  
  item = _TLB_FindItemByIndex(wnd, idx);  
  if (item)
  {
    item->data = data;
  }
}

LPVOID TLB_OnGetItemData(TWND wnd, INT idx)
{
  tlistbox_t* item = 0;
  
  item = _TLB_FindItemByIndex(wnd, idx);  
  if (item)
  {
    return item->data;
  }
  return 0;
}

INT TLB_OnSetItemChecked(TWND wnd, INT idx, INT check)
{
  tlistbox_t* item = 0;
  TLISTBOX lb = 0;
  DWORD style = TuiGetWndStyle(wnd);
  
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  item = _TLB_FindItemByIndex(wnd, idx);  
  /* found and do the check item state */
  if (item)
  {
    if (check == LB_UNCHECKED)
    {
      --lb->checkeditems;
      if (lb->checkeditems < 0)
      {
        lb->checkeditems    = 0;
        lb->lastitemchecked = 0;
      }
      item->checked = LB_UNCHECKED;
    }
    else
    {
      /* unselected the last if it is radio style */
      if (style & TLBS_RADIOBOX)
      {
        if (lb->lastitemchecked)
        {
          lb->lastitemchecked->checked = LB_UNCHECKED;
        }
      }
      else if (style & TLBS_CHECKBOX)
      {
        if (item->checked == LB_CHECKED)
        {
          --lb->checkeditems;
          if (lb->checkeditems < 0)
          {
            lb->checkeditems    = 0;
            lb->lastitemchecked = 0;
          }
          item->checked = LB_UNCHECKED;
          return LB_OK;
        }
      }
      /* count checked item */
      ++lb->checkeditems;
      if (lb->checkeditems > lb->nitems)
      {
        lb->checkeditems = lb->nitems;
      }
      /* checked and save the last checked item */
      item->checked = LB_CHECKED;
      lb->lastitemchecked = item;
    }
    return LB_OK;
  }
  return LB_ERROR;
}

INT TLB_OnGetItemChecked(TWND wnd, INT idx)
{
  tlistbox_t* item = 0;
  
  item = _TLB_FindItemByIndex(wnd, idx);  
  if (item)
  {
    return item->checked;
  }
  return LB_ERROR;
}

INT TLB_OnCountItemCheck(TWND wnd)
{
  TLISTBOX lb = 0;
  
  lb = (TLISTBOX)TuiGetWndParam(wnd);
  return lb->checkeditems;
}

LONG LISTBOXPROC(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
    case TLBM_COUNTITEMCHECKED:
    {
      return TLB_OnCountItemCheck(wnd);
    }
    case TLBM_GETITEMCHECKED:
    {
      return TLB_OnGetItemChecked(wnd, (INT)wparam);
    }
    case TLBM_SETITEMCHECKED:
    {
      return TLB_OnSetItemChecked(wnd, (INT)wparam, (INT)lparam);
    }
    case TWM_CREATE:
    {
      return TLB_OnCreate(wnd);
    }
    case TWM_DESTROY:
    {
      TLB_OnDestroy(wnd);
      return 0;
    }
    case TWM_SETFOCUS:
    {
      TLB_OnSetFocus(wnd);
      break;
    }
    case TWM_KILLFOCUS:
    {
      return TLB_OnKillFocus(wnd);
    }
    case TWM_KEYDOWN:
    {
      TLB_OnKeyDown(wnd, (LONG)wparam);
      break;
    }
    case TWM_PAINT:
    {
      TLB_OnPaint(wnd, TuiGetDC(wnd));
      return 0;
    }
    case TLBM_ADDITEM:
    {
      return TLB_OnAddItem(wnd, (LPCSTR)lparam);
    }
    case TLBM_DELETEITEM:
    {
      TLB_OnDeleteItem(wnd, (LONG)wparam);
      return 0;
    }
    case TLBM_GETCURSEL:
    {
      return TLB_OnGetCurSel(wnd);
    }
    case TLBM_SETCURSEL:
    {
      TLB_OnSetCurSel(wnd, (INT)wparam);
      return 0;
    }
    case TLBM_DELETEALLITEMS:
    {
      TLB_OnDeleteAllItems(wnd);
      return 0;
    }
    case TLBM_GETITEMCOUNT:
    {
      return TLB_OnGetItemCount(wnd);
    }
    case TLBM_SETITEMDATA:
    {
      TLB_OnSetItemData(wnd, (INT)wparam, (LPVOID)lparam);
      return 0;
    }
    case TLBM_GETITEMDATA:
    {
      return (LONG)TLB_OnGetItemData(wnd, (INT)wparam);
    }
    case TLBM_SETITEMTEXT:
    {
      TLB_OnSetItemText(wnd, (INT)wparam, (LPSTR)lparam);
      return 0;
    }
    case TLBM_GETITEMTEXT:
    {
      return TLB_OnGetItemText(wnd, (INT)wparam, (LPSTR)lparam);
    }
  }
  return TuiDefWndProc(wnd, msg, wparam, lparam);

}


/*-------------------------------------------------------------------
 * File name: tlctl.c
 * Author: Seree Rakwong
 * Date: 17-SEP-18
 *-----------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>

#ifdef __USE_CURSES__
#include <curses.h>
#elif (defined __USE_QIO__ && defined __VMS__)
#include <qio_init.h>
#endif

#include "tui.h"

/*-------------------------------------------------------------------
 * LISTCTRL functions
 *-----------------------------------------------------------------*/
struct _TLISTCELLSTRUCT
{
  INT         y;
  INT         x;
  CHAR        caption[TUI_MAX_WNDTEXT+1];
  DWORD       attrs;
  VOID*       data;
  
  struct _TLISTCELLSTRUCT *prev;
  struct _TLISTCELLSTRUCT *next;
};
typedef struct _TLISTCELLSTRUCT tlistcell_t;

struct _THEADERSTRUCT
{
  INT        id;
  CHAR       caption[TUI_MAX_WNDTEXT+1];
  INT        cols;      /* width */  
  INT        align;     /* left is a default */
  DWORD      attrs;
  
  tlistcell_t*    firstcell;
  tlistcell_t*    lastcell;

  struct _THEADERSTRUCT *prev;
  struct _THEADERSTRUCT *next;
};
typedef struct _THEADERSTRUCT theader_t;

struct _TLISTCTRLSTRUCT
{
  theader_t* firsthdr;
  theader_t* lasthdr;
  theader_t* firstvisiblehdr;
  theader_t* lastvisiblehdr;

  INT        nheaders;
  INT        nitems;
  INT        hdrids;      /* header id */
  INT        hdrallwidths;
  /* item control */
  INT        curselrow;
  INT        curselcol;
  INT        firstvisibleitem;
  
  TWND       editbox;
};
typedef struct _TLISTCTRLSTRUCT _TLISTCTRL;
typedef struct _TLISTCTRLSTRUCT *TLISTCTRL;

theader_t*   _TLCTL_FindHeaderByIndex(TLISTCTRL lctl, INT col);
tlistcell_t* _TLCTL_FindCellByIndex(TLISTCTRL lctl, INT col, INT idx);
tlistcell_t* _TLCTL_FindCellByHeader(TLISTCTRL lctl, theader_t* header, INT idx);
LONG         _TLCTL_GetCellRect(TLISTCTRL lctl, INT col, INT idx, RECT* rect);


LONG LISTCTRLPROC(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam);
VOID TLCTL_OnKeyDown(TWND wnd, LONG ch);
VOID TLCTL_OnPaint(TWND wnd, TDC dc);
LONG TLCTL_OnAddItem(TWND wnd, LPSTR text, INT nitems);
VOID TLCTL_OnDeleteAllItems(TWND wnd);
VOID TLCTL_OnDeleteColumn(TWND wnd, INT col);
LONG TLCTL_OnAddColumn(TWND wnd, HEADERITEM* hdritem);
VOID TLCTL_OnDestroy(TWND wnd);
LONG TLCTL_OnCreate(TWND wnd);
LONG TLCTL_OnGetItem(TWND wnd, UINT flags, SUBITEM* subitem);
LONG TLCTL_OnSetItem(TWND wnd, UINT flags, SUBITEM* subitem);


theader_t* _TLCTL_FindHeaderByIndex(TLISTCTRL lctl, INT col)
{
  theader_t* header = 0;
  INT i;
  
  if (col < 0 || col >= lctl->nheaders)
  {
    return 0; /* no header deleted */
  }
  
  header = lctl->firsthdr;
  for (i = 0; i == col && i < lctl->nheaders; ++i)
  {
    header = header->next;
  }
  return header;
}

tlistcell_t* _TLCTL_FindCellByIndex(TLISTCTRL lctl, INT col, INT idx)
{
  tlistcell_t* cell = 0;
  theader_t* header = 0;
  INT i;
  
  if (idx < 0 || idx >= lctl->nitems)
  {
    return 0;
  }
  header = _TLCTL_FindHeaderByIndex(lctl, col);
  if (header)
  {
    cell = header->firstcell;
    for (i = 0; i == idx && i < lctl->nitems; ++i)
    {
      cell = cell->next;
    }
  }
  return cell;
}

tlistcell_t* _TLCTL_FindCellByHeader(TLISTCTRL lctl, theader_t* header, INT idx)
{
  tlistcell_t* cell = 0;
  INT i;
  
  if (idx < 0 || idx >= lctl->nitems)
  {
    return 0;
  }

  if (header)
  {
    cell = header->firstcell;
    for (i = 0; i == idx && i < lctl->nitems; ++i)
    {
      cell = cell->next;
    }
  }
  return cell;
}

LONG _TLCTL_GetCellRect(TLISTCTRL lctl, INT col, INT idx, RECT* rect)
{
  /*
  tlistcell_t* cell = 0;
  TLISTCTRL lctl = 0;
  
  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  cell = TLCTL_FindCellByIndex(wnd, row, col);
*/
  return TUI_OK;
}

LONG TLCTL_OnCreate(TWND wnd)
{
  TLISTCTRL lctl = (TLISTCTRL)malloc(sizeof(_TLISTCTRL));
  if (!lctl)
  {
    return TUI_MEM;
  }
  memset(lctl, 0, sizeof(_TLISTCTRL));
  lctl->hdrids    = 1;
  lctl->editbox = TuiCreateWnd(EDIT,
                    "",
                    TWS_CHILD|TES_AUTOHSCROLL,
                    0,    /* y */
                    0,    /* x */
                    1,    /* lines  */
                    1,    /* cols   */
                    wnd,  /* parent */
                    lctl->hdrids,    /* id */
                    0);   /* no parameter */
  if (!lctl->editbox)
  {
    free(lctl);
    return TUI_MEM;
  }
  lctl->curselrow = -1;
  lctl->curselcol = -1;
  lctl->firstvisibleitem = -1;
  /* increment child ids */
  ++lctl->hdrids;
  
  /* save memory */
  TuiSetWndParam(wnd, (LPVOID)lctl);
  return TUI_CONTINUE;
}

VOID TLCTL_OnDestroy(TWND wnd)
{
  TLISTCTRL lctl = 0;
  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  
  TLC_DeleteAllColumns(wnd);
  free(lctl);
}

LONG TLCTL_OnAddColumn(TWND wnd, HEADERITEM* hdritem)
{
  TLISTCTRL lctl = 0;
  theader_t* header = 0;
  RECT rc;
  
  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  
  if (lctl->nitems > 0)
  {
    /* not allowed to add header after there are any items */
    return TUI_ERROR;
  }

  header = (theader_t*)malloc(sizeof(theader_t));
  if (!header)
  {
    return TUI_MEM;
  }
  memset(header, 0, sizeof(theader_t));
  
  TuiGetWndRect(TuiGetParent(wnd), &rc);
  /*header->caption = hdritem->caption;*/
  strncpy(header->caption,
    hdritem->caption,
    MIN(TUI_MAX_WNDTEXT, strlen(hdritem->caption)));
  header->cols    = hdritem->cols;
  header->align   = hdritem->align;
  header->attrs   = hdritem->attrs;
  header->id      = lctl->hdrids;

  /* make link */
  if (lctl->firsthdr)
  {
    header->prev = lctl->lasthdr;
    lctl->lasthdr->next = header;
    lctl->lasthdr = header;
  }
  else
  {
    lctl->firsthdr = lctl->lasthdr = header;
    lctl->firstvisiblehdr = header;
  }
  /* increment child ids */
  ++lctl->hdrids;
  ++lctl->nheaders;
  lctl->hdrallwidths += header->cols;
  
  return 0;
}

VOID TLCTL_OnDeleteColumn(TWND wnd, INT col)
{
  TLISTCTRL lctl = 0;
  theader_t* next = 0;
  tlistcell_t* cell = 0;
  tlistcell_t* nextcell = 0;
  theader_t* header = 0;
  
  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  header = _TLCTL_FindHeaderByIndex(lctl, col);
  if (!header)
  {
    return;
  }

  /* re-link */
  next = header->next;
  if (next)
  {
    next->prev = header->prev;
  }
  if (header->prev)
  {
    header->prev->next = next;
  }
  
  if (header == lctl->firsthdr)
  {
    lctl->firsthdr = next;
  }
  else if (header == lctl->lasthdr)
  {
    lctl->lasthdr = header->prev;
  }
  /* delete */
  header->next = header->prev = 0;

  cell = header->firstcell;
  while (cell)
  {
    nextcell = cell->next;
    /*free(cell->caption);*/
    cell->next = cell->prev = 0;
    free(cell);
    cell = nextcell;
  }

  free(header);

  /* done */
  --lctl->nheaders;
}

VOID TLCTL_OnDeleteAllItems(TWND wnd)
{
  INT nitems = 0;
  INT i = 0;
  TLISTCTRL lctl = 0;

  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  nitems = lctl->nheaders;
  for (i = 0; i < nitems; ++i)
  {
    TuiSendMsg(wnd, TLCM_DELETEITEM, 0, 0);
  }
}

VOID TLC_OnDeleteItem(TWND wnd, INT idx)
{
  TLISTCTRL lctl = 0;
  tlistcell_t* cell = 0;
  tlistcell_t* nextcell = 0;
  theader_t* header = 0;
  
  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  if (lctl->nitems <= 0 || idx < 0 || idx >= lctl->nitems)
  {
    return;
  }
  
  header = lctl->firsthdr;
  while (header)
  {
    cell = _TLCTL_FindCellByHeader(lctl, header, idx);
    nextcell = cell->next;
    if (nextcell)
    {
      nextcell->prev = cell->prev;
    }
    if (cell->prev)
    {
      cell->prev->next = nextcell;
    }
    if (header->firstcell == cell)
    {
      header->firstcell = cell->next;
    }
    else if (header->lastcell == cell)
    {
      header->lastcell = cell->prev;
    }
    
    cell->next = cell->prev = 0;
    /*free(cell->caption);*/
    free(cell);
    /* next header */
    header = header->next;
  }
  --lctl->nitems;
}

LONG TLCTL_OnAddItem(TWND wnd, LPSTR text, INT nitems)
{
  TLISTCTRL lctl = 0;
  INT i = 0;
  CHAR* tok;
  theader_t* header = 0;
  CHAR buf[TUI_MAX_WNDTEXT+1];
  tlistcell_t* newcell = 0;

  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  if (lctl->nheaders > 0)
  {
    /* insert into all listboxes */
    header = lctl->firsthdr;
    
    strcpy(buf, text);
    tok = strtok(buf, "\t");
    while (tok && i < nitems && i < lctl->nheaders)
    {
      newcell = (tlistcell_t*)malloc(sizeof(tlistcell_t));
      if (!newcell)
      {
        break;
      }
      memset(newcell, 0, sizeof(tlistcell_t));
      /*newcell->caption = (CHAR*)malloc(TUI_MAX_WNDTEXT+1);*/
      strncpy(newcell->caption, tok, MIN(TUI_MAX_WNDTEXT, strlen(tok)));
      
      /* add the new item */
      if (header->firstcell)
      {
        newcell->prev  = header->lastcell;
        header->lastcell->next = newcell;
        header->lastcell = newcell;
      }
      else
      {
        header->firstcell = header->lastcell = newcell;

        lctl->firstvisibleitem   = 0;
      }
      /* insert next */
      header = header->next;
      tok = strtok(0, "\t");
      ++i;
    }
    /* all items count */
    ++lctl->nitems;

  } /* have header */
  return lctl->nitems;
}

VOID TLCTL_OnPaint(TWND wnd, TDC dc)
{
  TLISTCTRL lctl = 0;
  theader_t* header = 0;
  INT width = 0;
  RECT rcitem, rc, rccell;
  CHAR buf[TUI_MAX_WNDTEXT+1];
  DWORD attrs = 0;
  LONG len = 0;
  tlistcell_t* visiblecell = 0;
  INT i = 0;

  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  
  if (lctl->nheaders <= 0 || !TuiIsWndVisible(wnd))
  {
    return;
  }

  /* draw headers */
  TuiGetWndRect(wnd, &rc);
  rcitem = rc; /* copy some values */
  
  header = lctl->firstvisiblehdr;  
  width  = header->cols;
  
  if (width > rc.cols)
  {
    width = rc.cols;
  }
  
  while (header && width < rc.cols)
  {    
    rcitem.cols = header->cols;
    rcitem.x    = rc.x + width - rcitem.cols;

    attrs = header->attrs;
#ifdef __USE_CURSES__
    attrs |= A_REVERSE;
#elif defined __USE_QIO__
    attrs = 0;
#endif
    len = TuiPrintTextAlignment(buf, 
            header->caption,
            rcitem.cols,
            ALIGN_CENTER);
    
    buf[0] = '[';
    buf[len-1] = ']';
    
    TuiDrawText(dc,
      rcitem.y,
      rcitem.x,
      buf,
      attrs);

    /* draw cells */
    rccell = rcitem;
    rccell.y += 1;
    visiblecell = header->firstcell;
    for (i = 0; i < lctl->nitems && visiblecell; ++i, visiblecell = visiblecell->next)
    {
      if (i < lctl->firstvisibleitem)
      {
        /* do nothing */
        continue;
      }
      else if (i - lctl->firstvisibleitem <= rc.lines - 2)
      {
        len = TuiPrintTextAlignment(buf,
                visiblecell->caption,
                header->cols,
                header->align);

        attrs = visiblecell->attrs;
#ifdef __USE_CURSES__
        if (i == lctl->curselrow)
        {
          attrs |= A_REVERSE;
        }
#elif defined __USE_QIO__
        
#endif
        TuiDrawText(dc,
          rccell.y,
          rccell.x,
          buf,
          attrs);
        ++rccell.y;
      }
    } /* for each drawing cell */
    
    
    /* draw next header */
    header = header->next;
    if (header)
    {
      width += header->cols;
      if (width > rc.cols)
      {
        break;
      }
    }
  } /* while header */
  
  /* print arrow controls */
#ifdef __USE_CURSES__
  attrs |= A_REVERSE;
#elif defined __USE_QIO__
  attrs = 0;
#endif
  if (lctl->firsthdr != lctl->firstvisiblehdr)
  {
    TuiPutChar(dc, rc.y, rc.x, '<', attrs);
  }
  /* save the last visible */
  if (header)
  {
    lctl->lastvisiblehdr = header->prev;
    TuiPutChar(dc, rc.y, rc.x + width - header->cols - 1, '>', attrs);
  }
  else
  {
    lctl->lastvisiblehdr = lctl->lasthdr;
  }

  TuiMoveYX(dc, rcitem.y,
      rcitem.x);
}

VOID TLCTL_OnKeyDown(TWND wnd, LONG ch)
{
  TLISTCTRL lctl = 0;
  INT repaint = 1;
  RECT rc;
  INT lines = 0;
  
  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  TuiGetWndRect(wnd, &rc);
  
  if (lctl->nheaders <= 0)
  {
    return;
  }
  
  switch (ch)
  {
    case TVK_SPACE:
    {
      /*repaint = 1;*/
      break;
    }

    case KEY_RIGHT:
    {
      if (lctl->lastvisiblehdr != lctl->lasthdr)
      {
        lctl->firstvisiblehdr = lctl->firstvisiblehdr->next;
        ++repaint;
      }
      break;
    }
    case KEY_LEFT:
    {
      if (lctl->firstvisiblehdr != lctl->firsthdr)
      {
        lctl->firstvisiblehdr = lctl->firstvisiblehdr->prev;
        ++repaint;
      }
      break;
    }
    case KEY_DOWN:
    {
      ++lines;
      ++repaint;
      break;
    }
  
    case KEY_UP:
    {
      --lines;
      ++repaint;
      break;
    }
      
    case TVK_PRIOR:
#ifdef __USE_CURSES__
    case KEY_PPAGE:
#elif (defined __USE_QIO__ && defined __VMS__)
    case KEY_PREV:
#endif
    {
      lines -= rc.lines - 1;
      ++repaint;
      break;
    }
    
    case TVK_NEXT:
#ifdef __USE_CURSES__
    case KEY_NPAGE:
#elif (defined __USE_QIO__ && defined __VMS__)
    case KEY_NEXT:
#endif
    {
      lines += rc.lines - 1;
      ++repaint;
      break;
    }
  }
  if (repaint)
  {
    lctl->curselrow += lines;
    if (lctl->curselrow >= lctl->nitems)
    {
      lctl->curselrow = lctl->nitems - 1;
    }
    else if (lctl->curselrow < 0)
    {
      lctl->curselrow = 0;
    }
    
    if (lctl->curselrow >= lctl->firstvisibleitem + rc.lines - 1)
    {
      lctl->firstvisibleitem += lines;
      if (lctl->firstvisibleitem - 1 > lctl->nitems - rc.lines)
      {
        lctl->firstvisibleitem = lctl->nitems - rc.lines;
      }
    }
    else if (lctl->firstvisibleitem > lctl->curselrow)
    {
      lctl->firstvisibleitem += lines;
      if (lctl->firstvisibleitem < 0)
      {
        lctl->firstvisibleitem = 0;
      }
    }
    TuiInvalidateWnd(wnd);
    /* send notification */
    /*
    nmhdr.id   = TuiGetWndID(wnd);
    nmhdr.ctl  = wnd;
    nmhdr.code = TLBN_SELCHANGED;
    TuiPostMsg(TuiGetParent(wnd), TWM_NOTIFY, 0, (LPARAM)&nmhdr);
    */
  }
}

LONG TLCM_OnGetItemCount(TWND wnd)
{
  TLISTCTRL lctl = 0;
  
  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  return lctl->nitems;
}

LONG TLCTL_OnSetItem(TWND wnd, UINT flags, SUBITEM* subitem)
{
  TLISTCTRL lctl = 0;
  tlistcell_t* cell = 0;
  LONG rc = TUI_ERROR;

  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  cell = _TLCTL_FindCellByIndex(lctl, subitem->col, subitem->idx);
  if (cell)
  {
    if (flags & LCFM_TEXT)  { strcpy(cell->caption, subitem->text);  }
    if (flags & LCFM_DATA)  { cell->data  = subitem->data;  }
    if (flags & LCFM_ATTRS) { cell->attrs = subitem->attrs; }
    rc = TUI_OK;
  }
  return rc;
}

LONG TLCTL_OnGetItem(TWND wnd, UINT flags, SUBITEM* subitem)
{
  TLISTCTRL lctl = 0;
  tlistcell_t* cell = 0;
  LONG rc = TUI_ERROR;

  lctl = (TLISTCTRL)TuiGetWndParam(wnd);
  cell = _TLCTL_FindCellByIndex(lctl, subitem->col, subitem->idx);
  if (cell)
  {
    if (flags & LCFM_TEXT)  { subitem->text  = cell->caption;  }
    if (flags & LCFM_DATA)  { subitem->data  = cell->data;  }
    if (flags & LCFM_ATTRS) { subitem->attrs = cell->attrs; }
    rc = TUI_OK;
  }
  return rc;
}

LONG LISTCTRLPROC(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
    case TWM_CREATE:
    {
      /* initial memory for static control */
      return TLCTL_OnCreate(wnd);
    }
    case TWM_DESTROY:
    {
      /* release memory of static control */
      TLCTL_OnDestroy(wnd);
      return 0;
    }
/*
    case TWM_SETFOCUS:
    {
      TLCTL_OnSetFocus(wnd);
      break;
    }
    case TWM_KILLFOCUS:
    {
      return TLCTL_OnKillFocus(wnd);
    }
*/
    case TWM_KEYDOWN:
    {
      TLCTL_OnKeyDown(wnd, (LONG)wparam);
      break;
    }

    case TWM_PAINT:
    {
      TLCTL_OnPaint(wnd, TuiGetDC(wnd));
      return 0;
    }
    
    case TLCM_ADDCOLUMN:
    {
      return TLCTL_OnAddColumn(wnd, (HEADERITEM*)lparam);
    }
    case TLCM_DELETECOLUMN:
    {
      TLCTL_OnDeleteColumn(wnd, (INT)wparam);
      return 0;
    }
    case TLCM_DELETEALLCOLUMNS:
    {
      TLCTL_OnDeleteAllItems(wnd);
      return 0;
    }
    case TLCM_ADDITEM:
    {
      return TLCTL_OnAddItem(wnd, (LPSTR)lparam, (INT)wparam);
    }
    case TLCM_DELETEITEM:
    {
      TLC_OnDeleteItem(wnd, (INT)wparam);
      return 0;
    }
    case TLCM_DELETEALLITEMS:
    {
      TLCTL_OnDeleteAllItems(wnd);
      return 0;
    }
    case TLCM_SETITEM:
    {
      return TLCTL_OnSetItem(wnd, (UINT)wparam, (SUBITEM*)lparam);
    }
    case TLCM_GETITEM:
    {
      return TLCTL_OnGetItem(wnd, (UINT)wparam, (SUBITEM*)lparam);
    }
    case TLCM_GETITEMCOUNT:
    {
      return TLCM_OnGetItemCount(wnd);
    }
  }
  return TuiDefWndProc(wnd, msg, wparam, lparam);

}


/*-------------------------------------------------------------------
 * File name: tui_test.c
 * Author: Seree Rakwong
 * Date: 17-SEP-18
 *-----------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curses.h>
#include "tui.h"

#define IDC_STATUSBAR    199
#define IDC_NAME         200
#define IDC_LISTBOX1     204
#define IDC_PASSWORD     205
#define IDC_OK           206
#define IDC_CANCEL       207
#define IDC_LISTBOX2     208
#define IDC_LISTBOX3     209
#define IDC_CLOSE        210
#define IDC_SAVE         211
#define IDC_OPENDLG2     212
#define IDC_OPENDLG3     213
#define IDC_MSG          214
#define IDC_PRICE        215

WNDTEMPL dlg1[] =
{
  /* 1st object is always dialog */
  { "mywndproc", "Dialog1", 1,  0,  0, 25, 80, TWS_WINDOW, 0 },
  /* 2nd and others are controls */
  { STATIC, "Name:",    100,  1,  1,  1,  16, TWS_CHILD|TWS_VISIBLE|TSS_RIGHT, 0 },
  { EDIT,   "12345678901234567890",       IDC_NAME,  1, 18,  1, 16, 
    TWS_CHILD|TWS_VISIBLE|
      TES_LEFT|TES_APPENDMODE|TES_UPPERCASE|TES_AUTOHSCROLL, 0 },
  { STATIC, "ID:",    101,  2,  1,  1,  16, TWS_CHILD|TWS_VISIBLE|TSS_RIGHT, 0 },
  { EDIT,   "DT66234",     201,  2, 18,  1, 16, 
    TWS_CHILD|TWS_VISIBLE|
      TES_CENTER|TES_AUTOHSCROLL, 0 },
  { STATIC, "Tel:",    102,  3,  1,  1,  16, TWS_CHILD|TWS_VISIBLE|TSS_RIGHT, 0 },
  { EDIT,   "1234",   202,  3, 18,  1, 16, 
    TWS_CHILD|TWS_VISIBLE|
      TES_NUMBER|TES_RIGHT|TES_AUTOHSCROLL, 0 },
  { STATIC, "Email:",    103,  4,  1,  1,  16, TWS_CHILD|TWS_VISIBLE|TSS_RIGHT, 0 },
  { EDIT,   "abc@abc.com", 203,  4, 18,  1, 16, 
    TWS_CHILD|TWS_VISIBLE|TWS_DISABLED|
      TES_AUTOHSCROLL, 0 },
  { STATIC, "Password:",    104,  5,  1,  1,  16, TWS_CHILD|TWS_VISIBLE|TSS_RIGHT, 0 },
  { EDIT,   "welcome1!", IDC_PASSWORD,  5, 18,  1, 16, 
    TWS_CHILD|TWS_VISIBLE|
      TES_LEFT|TES_PASSWORD|TES_AUTOHSCROLL, 0 },
  { STATIC, "Price:",    105,  6,  1,  1,  16, TWS_CHILD|TWS_VISIBLE|TSS_RIGHT, 0 },
  { EDIT,   "399.50", IDC_PRICE,  6, 18,  1, 16, 
    TWS_CHILD|TWS_VISIBLE|
      TES_RIGHT|TES_DECIMAL|TES_AUTOHSCROLL, 0 },
  { LISTBOX,"",               IDC_LISTBOX1,  8,  1,  5, 16, TWS_CHILD|TWS_VISIBLE|TLBS_CENTER, 0 },
  { LISTBOX,"",               IDC_LISTBOX2,  8, 21,  5, 16, 
    TWS_CHILD|TWS_VISIBLE|TLBS_CHECKBOX, 0 },
  { LISTBOX,"",               IDC_LISTBOX3,  8, 41,  5, 16, 
    TWS_CHILD|TWS_VISIBLE|TLBS_RADIOBOX|TLBS_RIGHT, 0 },
  { BUTTON, "Dlg2",    IDC_OPENDLG2,  14,  1,  1,   10, TWS_CHILD|TWS_VISIBLE|TWS_DISABLED, 0 },
  { BUTTON, "Dlg3",    IDC_OPENDLG3,  14,  21,  1,   10, TWS_CHILD|TWS_VISIBLE, 0 },
  { BUTTON, "Close",    IDC_CLOSE,    14,  41,  1,  11, TWS_CHILD|TWS_VISIBLE, 0 },
  { STATIC, "Esc to exit: ", IDC_STATUSBAR, 24,  0,  1, 80, TWS_CHILD|TWS_VISIBLE|TWS_DISABLED|TSS_LEFT, 0 },
  /* the last is the end-of-controls */
  { 0, 0, 0, 0, 0, 0, 0, 0 }
};

WNDTEMPL dlg3[] =
{
  /* 1st object is always dialog */
  { "mylistctlproc", "Dialog3", 2,  0,  0, 25, 80, TWS_WINDOW, 0 },
  /* 2nd and others are controls */
  { LISTCTRL, "",    IDC_OK,  1,  1,  16,  79, TWS_CHILD|TWS_VISIBLE, 0 },
  { BUTTON, "Message",    IDC_MSG,  20,  20,  1,  15, TWS_CHILD|TWS_VISIBLE, 0 },
  { BUTTON, "Close",    IDC_CLOSE,  20,  40,  1,  11, TWS_CHILD|TWS_VISIBLE, 0 },
  /* the last is the end-of-controls */
  { 0, 0, 0, 0, 0, 0, 0, 0 }
};

WNDTEMPL msgbox[] =
{
  /* 1st object is always dialog */
  { "msgbox", "msgbox", 3,  10,  10, 7, 40, TWS_WINDOW, 0 },
  /* 2nd and others are controls */
  { STATIC, "This is a message box",        IDC_MSG,      1,  1,  1,  38, 
      TWS_CHILD|TWS_VISIBLE|TSS_CENTER, 0 },
  { BUTTON, "OK",        IDC_OK,      3,  5,  1,  10, TWS_CHILD|TWS_VISIBLE, 0 },
  { BUTTON, "Cancel",    IDC_CANCEL,  3,  20,  1,  10, TWS_CHILD|TWS_VISIBLE, 0 },
  /* the last is the end-of-controls */
  { 0, 0, 0, 0, 0, 0, 0, 0 }
};


VOID mywndproc_onselchanged(TWND wnd, TWND ctl)
{
  TWND statusbar = 0;
  TWND listbox = 0;
  INT i = 0;
  CHAR buf[TUI_MAX_WNDTEXT+1];
  
  statusbar = TuiGetWndItem(wnd, IDC_STATUSBAR);
  listbox = ctl;
  i = TLB_GetCurSel(listbox);
  if (i >= 0)
  {
    TLB_GetItemText(listbox, i, buf);
    TuiSetWndText(statusbar, buf);
  }
}

LONG mywndproc(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  CHAR buf[TUI_MAX_WNDTEXT+1];
  INT i;
  TWND statusbar = 0;
  TWND listbox = 0;
  TWND edit = 0;
  NMHDR* nmhdr = 0;

  switch (msg)
  {
    case TWM_INITDIALOG:
    {
      listbox = TuiGetWndItem(wnd, IDC_LISTBOX1);
      for (i = 0; i < 20; ++i)
      {
        sprintf(buf, "Item %d", (i));
        TLB_AddItem(listbox, buf);
      }
      
      TLB_DeleteItem(listbox, 5);
      TLB_SetCurSel(listbox, 7);

      listbox = TuiGetWndItem(wnd, IDC_LISTBOX2);
      for (i = 0; i < 10; ++i)
      {
        sprintf(buf, "Item %d", (i));
        TLB_AddItem(listbox, buf);
      }
      
      listbox = TuiGetWndItem(wnd, IDC_LISTBOX3);
      for (i = 0; i < 10; ++i)
      {
        sprintf(buf, "Item %d", (i));
        TLB_AddItem(listbox, buf);
      }

      edit = TuiGetWndItem(wnd, IDC_NAME);
      TuiSendMsg(edit, TEM_LIMITTEXT, (WPARAM)20, (LPARAM)0);
      
      statusbar = TuiGetWndItem(wnd, IDC_STATUSBAR);
#ifdef __USE_CURSES__
      TuiSendMsg(statusbar, TWM_SETTEXTATTRS, (WPARAM)(A_REVERSE), (LPARAM)0);
#endif      
      edit = TuiGetWndItem(wnd, IDC_PRICE);
      TuiSendMsg(edit, TEM_SETDECWIDTH, (WPARAM)2, (LPARAM)0);
     /* 
      edit = TuiGetWndItem(wnd, IDC_PASSWORD);
      TEDT_ShowPasswdChar(edit, TW_HIDE);*/
      return TUI_CONTINUE;
    }

    case TWM_NOTIFY:
    {
      nmhdr = (NMHDR*)lparam;
      switch (nmhdr->code)
      {
        case TLBN_SELCHANGED:
        {
          mywndproc_onselchanged(wnd, nmhdr->ctl);
          break;
        }
      }
      break;
    }
    case TWM_COMMAND:
    {
      sprintf(buf, "Pressed: %s", (IDC_OK == wparam ? "OK" : "Cancel"));
      statusbar = TuiGetWndItem(wnd, IDC_STATUSBAR);
      TuiSetWndText(statusbar, buf);

      if (wparam == IDC_OPENDLG2)
      {
      }
      else if (wparam == IDC_OPENDLG3)
      {
        TWND dlg = TuiCreateWndTempl(dlg3, 0);
        TuiShowWnd(dlg, 1);        
      }
      else if (wparam == IDC_CLOSE)
      {
        TuiPostQuitMsg(0);
      }
      
      break;
    }
  }
  return TuiDefWndProc(wnd, msg, wparam, lparam);
}


LONG mylistctlproc(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  TWND list = 0;

  switch (msg)
  {
    case TWM_INITDIALOG:
    {
      list = TuiGetWndItem(wnd, IDC_OK);
#ifdef __USE_CURSES__

      TLC_AddColumn(list, "STOCK", 20, ALIGN_LEFT,   COLOR_PAIR(BLUE_YELLOW));
      TLC_AddColumn(list, "DATE",  16, ALIGN_CENTER, COLOR_PAIR(BLUE_YELLOW));
      TLC_AddColumn(list, "OPEN",  16, ALIGN_RIGHT,  COLOR_PAIR(BLUE_YELLOW));
      TLC_AddColumn(list, "HIGH",  16, ALIGN_RIGHT,  COLOR_PAIR(BLUE_YELLOW));
      TLC_AddColumn(list, "LOW",   16, ALIGN_RIGHT,  COLOR_PAIR(BLUE_YELLOW));
      TLC_AddColumn(list, "CLOSE", 16, ALIGN_RIGHT,  COLOR_PAIR(BLUE_YELLOW));
#else
      TLC_AddColumn(list, "STOCK", 20, ALIGN_LEFT,   BLUE_YELLOW);
      TLC_AddColumn(list, "DATE",  16, ALIGN_CENTER, BLUE_YELLOW);
      TLC_AddColumn(list, "OPEN",  16, ALIGN_RIGHT,  BLUE_YELLOW);
      TLC_AddColumn(list, "HIGH",  16, ALIGN_RIGHT,  BLUE_YELLOW);
      TLC_AddColumn(list, "LOW",   16, ALIGN_RIGHT,  BLUE_YELLOW);
      TLC_AddColumn(list, "CLOSE", 16, ALIGN_RIGHT,  BLUE_YELLOW);
#endif  
      TLC_AddItem(list, "PTT\t01-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t02-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t03-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t04-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t05-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t06-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t07-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t08-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t09-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t10-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t11-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t12-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t13-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t14-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t15-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t16-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t17-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t18-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t19-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t20-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t21-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t22-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t23-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t24-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t25-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t26-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t27-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t28-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t29-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      TLC_AddItem(list, "PTT\t30-01-2018\t60.00\t63.50\t60.00\t62.00\t", 6);
      return TUI_CONTINUE;
    }

    case TWM_COMMAND:
    {
      if (wparam == IDC_CLOSE)
      {
        TuiDestroyWnd(wnd);
      }
      else if (wparam == IDC_MSG)
      {
        UINT rc = TuiMsgBox(wnd,
                    "Hello world",
                    "Welcome to the real world.",
                    MB_YESNOCANCEL);
        switch (rc)
        {
          case MB_YES: TuiMsgBox(wnd, "Hello TUI", "Clicked YES", MB_OK); break;
          case MB_NO: TuiMsgBox(wnd, "Hello TUI", "Clicked NO", MB_OK); break;
          case MB_CANCEL: TuiMsgBox(wnd, "Hello TUI", "Clicked CANCEL", MB_OK); break;
        }
      
      }
      break;
    }
  }
  return TuiDefWndProc(wnd, msg, wparam, lparam);
}

LONG msgboxproc(TWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
    case TWM_INITDIALOG:
    {
#ifdef __USE_CURSES__
      TuiSendMsg(wnd, TWM_SETTEXTATTRS, (WPARAM)(A_REVERSE), (LPARAM)0);
#endif
      return TUI_CONTINUE;
    }

    case TWM_COMMAND:
    {
      if (wparam == IDC_CANCEL)
      {
        TuiDestroyWnd(wnd);
      }
      break;
    }
  }
  return TuiDefWndProc(wnd, msg, wparam, lparam);
}


int main(int argc, char* argv[])
{
  MSG msg;
  TWND wnd;

  TuiStartup();

  TuiSetNextMove(TVK_TAB);

  TuiRegisterCls("mywndproc", mywndproc);
  TuiRegisterCls("mylistctlproc", mylistctlproc);
  TuiRegisterCls("msgbox", msgboxproc);

  wnd = TuiCreateWndTempl(dlg1, 0);
  if (!wnd)
  {
    TuiShutdown();
    return -1;
  }
  TuiShowWnd(wnd, 1);

  while (TuiGetMsg(&msg))
  {
    TuiDispatchMsg(&msg);
    TuiTranslateMsg(&msg);
  }
  TuiShutdown();
  return 0;
}