/* Next available MSG number is 21 */
/*

   ADS Programmable Dialog Box Test Program for Rx

   DLGTEST.CC

   Copyright (C) 1994,1996 by Autodesk, Inc.
 
   Permission to use, copy, modify, and distribute this software in 
   object code form for any purpose and without fee is hereby granted, 
   provided that the above copyright notice appears in all copies and 
   that both that copyright notice and the limited warranty and 
   restricted rights notice below appear in all supporting 
   documentation.
 
   AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.  
   AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF 
   MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.  AUTODESK, INC.
   DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE 
   UNINTERRUPTED OR ERROR FREE.
 
   Use, duplication, or disclosure by the U.S. Government is subject to 
   restrictions set forth in FAR 52.227-19 (Commercial Computer 
   Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) 
   (Rights in Technical Data and Computer Software), as applicable.
    
   .

    Programmable Dialog Box Test Program

    CREATED BY:  Ed Becnel, January 1994

    Function Entry Points: 
      AcRx::AppRetCode
        acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt);

    Exported ADS Functions
        RXSLIDE
	RXDIMEN
	RXSETCOLOR

    Modification History:
        Jan 15 1994 - eab - original creation

    Notes:

	 This program is the ADS/Arx counterpart to the LISP test
    program, dlgtest.lsp, and the ADS counterpart, dlgtest.c.  It 
    provides a simple dimensioning dialog invoked with the command 
    "dimen" and a simple color dialog invoked with the command "setcolor".

	 The purposes of providing this program:
    1) Demonstrate Programmable Dialog Box use with minimum of code
	to sort through
    2) Demonstrate differences between LISP and ADS dialog 
	programming
    3) Use as a starting point to try out new dialog code.

     dlgtest uses the file dlgtest.dcl as the DCL (Dialog 
     Control Language) file.

    ADS functions are associated with dialog buttons with 
    the ads_action_tile functions.  These functions are called 
    when the user presses buttons during the ads_start_dialog 
    function.

    Special tile names (keys): 
	"accept" - Ok button
	"cancel" - Cancel button


*/


/**************************************************************************/
/*  INCLUDES                                                              */
/**************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "rxdefs.h"
#include "adslib.h"
#include "adsdlg.h"


extern "C" {
/****************************************************************************/
/*  LOCALLY DEFINED ENTRY POINT INVOKED BY Arx                               */
/****************************************************************************/
AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt);
}

/****************************************************************************/
/*  LOCAL FUNCTION FORWARD DECLARATIONS                                     */
/****************************************************************************/
int viewslide();
int setdimen();
int setcolor();

void get_dimvars (ads_hdlg hdlg);
static void set_dimvars (ads_hdlg hdlg);
static void CALLB dimen_ok   (ads_callback_packet *cpkt);
static void CALLB editcol_cb (ads_callback_packet *cpkt);
static void CALLB listcol_cb (ads_callback_packet *cpkt);
static void CALLB help_cb    (ads_callback_packet *cpkt);
static void CALLB cancelcol_cb (ads_callback_packet *cpkt);
static void CALLB viewslide_OK(ads_callback_packet *cpkt);
static void CALLB subdlg_OK(ads_callback_packet *cpkt);
static void CALLB subdlg_handler(ads_callback_packet *);
static void CALLB subdlg_terminate(ads_callback_packet *);
static void CALLB subdlg_term_on_off(ads_callback_packet *cpkt);
void dlg_colortile (ads_hdlg hdlg, char *key, short color, int borderflag);
void dlg_rect (short x, short y, short width, short height, short color);

int funcload   (void);
int funcunload (void);
int dofun      (void);

/**************************************************************************/
/*  TYPEDEFS                                                              */
/**************************************************************************/
/* ADS Function Table */
typedef struct {
    char    *name;
    int     (*fptr)();
} ftblent;

/****************************************************************************/
/*  DEFINES                                                                 */
/****************************************************************************/
#define ELEMENTS(array) (sizeof(array)/sizeof((array)[0]))

#ifndef BLACK
#define    BLACK    0
#define    RED      1
#define    YELLOW   2
#define    GREEN    3
#define    CYAN     4
#define    BLUE     5
#define    MAGENTA  6
#define    WHITE    7
#endif

#define MAXHEIGHTLEN 4

/**************************************************************************/
/*  GLOBAL VARIABLES                                                      */
/**************************************************************************/
/* Table of ADS functions */
ftblent exfun[] = {
            {/*MSG0*/"C:RXSLIDE", viewslide},
	    {/*MSG0*/"C:RXDIMEN", setdimen},
	    {/*MSG0*/"C:RXSETCOLOR", setcolor}
        };

static char *dimbools[] = {
    /*MSG0*/"dimse1", 
    /*MSG0*/"dimse2", 
    /*MSG0*/"dimtih", 
    /*MSG0*/"dimtoh",
    /*MSG0*/"dimtad",
    /*MSG0*/"dimtol", 
    /*MSG0*/"dimlim", 
    /*MSG0*/"dimalt", 
    /*MSG0*/"dimaso",
    /*MSG0*/"dimsho",
    NULL
   };

static char *dimreals[] = {
    /*MSG0*/"dimasz", 
    /*MSG0*/"dimtsz", 
    /*MSG0*/"dimtxt", 
    /*MSG0*/"dimcen",
    /*MSG0*/"dimexo",
    /*MSG0*/"dimexe", 
    /*MSG0*/"dimdle", 
    NULL
   };

static short color;
static short color_save;
#define CLEN 32
static char colorstr[CLEN+1] = "0";
static char colorstr_save[CLEN+1] = "0";
static int allow_terminate = 0;

static char *colorlist[] = {
    /*MSG1*/"black", 
    /*MSG2*/"red", 
    /*MSG3*/"yellow", 
    /*MSG4*/"green", 
    /*MSG5*/"cyan", 
    /*MSG6*/"blue",
    /*MSG7*/"magenta", 
    /*MSG8*/"white"
   };

static int dcl_id;

/******************************************************************************/
/*.doc funcload(internal) */
/*+
    This function is called to define all function names in the ADS
    function table.  Each named function will be callable from lisp or
    invokable from another ADS application.
-*/
/******************************************************************************/
int
/*FCN*/funcload()
{
    int i;

    for (i = 0; i < ELEMENTS(exfun); i++) {
        if (!ads_defun(exfun[i].name, i))
            return RTERROR;
    }

    return RTNORM;
}

/******************************************************************************/
/*.doc funclunoad(internal) */
/*+
    This function is called to undefine all function names in the ADS
    function table.  Each named function will be removed from the
    AutoLISP hash table.
-*/
/******************************************************************************/
int
/*FCN*/funcunload()
{
    int i;

    /* Undefine each function we defined */

    for (i = 0; i < ELEMENTS(exfun); i++) {
        ads_undef(exfun[i].name,i);
    }

    return RTNORM;
}

/******************************************************************************/
/*.doc dofun(internal) */
/*+
    This function is called to invoke the function which has the
    registerd function code that is obtained from  ads_getfuncode.  The
    function will return RTERROR if the function code is invalid, or
    RSERR if the invoked function fails to return RTNORM.  The value
    RSRSLT will be returned if the function code is valid and the
    invoked subroutine returns RTNORM.
-*/
/******************************************************************************/
int
/*FCN*/dofun()
{
    int    val;
    int    rc;

    ads_retvoid();
        
    if ((val = ads_getfuncode()) < 0 || val > ELEMENTS(exfun))
        return RTERROR;
 
    rc = (*exfun[val].fptr)();
 
    return ((rc == RTNORM) ? RSRSLT:RSERR);
}


/* 

		VIEWLIDE -- Slide Viewer Dialog

*/

int
/*FCN*/viewslide()
{
    ads_hdlg hdlg;
    int dlg_status;
    short x, y;
    char height_val[MAXHEIGHTLEN];


    /* Load the dialog file */
    ads_load_dialog(/*MSG0*/"viewslid.dcl", &dcl_id);
    if (dcl_id < 0) {
	ads_printf(/*MSG19*/"Error loading \"viewslid.dcl\"\n");
	return RTERROR;
    }
    /* initialize the viewslide dialog, no default callback function */
    ads_new_dialog(/*MSG0*/"viewslide", dcl_id, (CLIENTFUNC)0, &hdlg);
    if (hdlg == NULL) {
	ads_printf(/*MSG20*/"new_dialog for viewslide failed\n");
	ads_unload_dialog(dcl_id);
	return RTERROR;
    }

    /* Update other tiles when one is changed by using callback
       functions */
    ads_action_tile(hdlg, "accept", (CLIENTFUNC)viewslide_OK);
    ads_action_tile(hdlg, "subdlgtest", (CLIENTFUNC)subdlg_handler); 

    ads_dimensions_tile(hdlg, "color_whl", &x, &y);
    ads_start_image(hdlg, "color_whl");
    ads_slide_image(0, 0, x, y, "colorwh");
    ads_end_image();

    /* Get key */
    ads_get_attr(hdlg, "color_whl", "height", height_val, MAXHEIGHTLEN);
    if (strcmp(height_val, "7") != 0)
	ads_printf("\nValue for color_whl tile's height attribute is incorrect: %s != 7\n", height_val);
    
    /* Hand control over to the dialog until OK is pressed */
    ads_start_dialog(hdlg, &dlg_status);

    /* free all memory for dialog */
    ads_unload_dialog(dcl_id);

    return RTNORM;
}

static void CALLB viewslide_OK(ads_callback_packet *cpkt)
{
    ads_done_dialog(cpkt->dialog, DLGOK);
}

static void CALLB subdlg_handler(ads_callback_packet *) 
{
    int dlg_status;
    ads_hdlg sdlg;
 
    /* initialize the viewslide dialog, no default callback function */
    ads_new_dialog(/*MSG0*/"subdlg", dcl_id, (CLIENTFUNC)0, &sdlg);
    if (sdlg == NULL) {
	ads_printf(/*MSG20*/"new_dialog for sub-dialog failed\n");
	return;
    }
       
    /* Update other tiles when one is changed by using callback
       functions */
    ads_action_tile(sdlg, "accept", (CLIENTFUNC)subdlg_OK);
    ads_action_tile(sdlg, "terminate", (CLIENTFUNC)subdlg_terminate);
    ads_action_tile(sdlg, "term_on_off", (CLIENTFUNC)subdlg_term_on_off);
    if (allow_terminate) {
	ads_mode_tile(sdlg, "terminate", MODE_ENABLE);
	ads_set_tile(sdlg, "term_on_off", "1");
    }
    else {
	ads_mode_tile(sdlg, "terminate", MODE_DISABLE);
	ads_set_tile(sdlg, "term_on_off", "0");
    }
	   
    /* Hand control over to the dialog until OK is pressed */
    ads_start_dialog(sdlg, &dlg_status);
       
    return;
}
 
static void CALLB subdlg_OK(ads_callback_packet *cpkt)
{
    ads_done_dialog(cpkt->dialog, DLGOK);
}

static void CALLB subdlg_terminate(ads_callback_packet *)
{
    ads_term_dialog();
}

static void CALLB subdlg_term_on_off(ads_callback_packet *cpkt)
{
    ads_hdlg sdlg = cpkt->dialog;
    char value[TILE_STR_LIMIT];
    
    strcpy(value, cpkt->value);
    if (strcmp(value, "0") == 0) {      /* Disabled */
	ads_mode_tile(sdlg, "terminate", MODE_DISABLE);
	allow_terminate = 0;
    }
    else {
	ads_mode_tile(sdlg, "terminate", MODE_ENABLE);
	allow_terminate = 1;
    }
}


/* 

		DIMEN -- Dimensioning Dialog

*/

/* Position of dialog, centered to start */
static int dimx = -1, 
	   dimy = -1; 

int
/*FCN*/setdimen()
{
    ads_hdlg hdlg;
    int dlg_status;
    int dcl_id;

    ads_load_dialog(/*MSG0*/"dlgtest.dcl", &dcl_id);
    if (dcl_id < 0) {
	ads_printf(/*MSG13*/"Error loading \"dlgtest.dcl\"\n");
	return RTERROR;
    }
    /* Display the "dimensions" dialog */
    ads_new_positioned_dialog(/*MSG14*/"dimensions", dcl_id, 
			(CLIENTFUNC)0, dimx, dimy, &hdlg);
    if (hdlg == NULL) {
	ads_printf(/*MSG15*/"The ads_new_dialog function failed\n");
	ads_unload_dialog(dcl_id);
	return RTERROR;
    }

    /* Register dimen_ok function with the OK button */
    ads_action_tile(hdlg, /*MSG0*/"accept", (CLIENTFUNC)dimen_ok);

    /* show current values in dialog */
    get_dimvars(hdlg);

    /* run the dialog */
    ads_start_dialog(hdlg, &dlg_status);

    /* free all memory for dialog */
    ads_unload_dialog(dcl_id);

    return RTNORM;
}


/* DIMEN_OK -- callback function for OK button of dimension dialog.  */

static void CALLB
/*FCN*/dimen_ok(ads_callback_packet *cpkt)
{
    /* User pressed OK button to end dialog.  Check modified data
       and send to AutoCAD. */
    set_dimvars(cpkt->dialog);
    ads_done_positioned_dialog(cpkt->dialog, 1, &dimx, &dimy);
}


/* Show current values in dialog */

void
/*FCN*/get_dimvars(ads_hdlg hdlg)
{
    char *bitem;
    char *ritem;
    char **blist = dimbools;
    char **rlist = dimreals;
    char value[80];
    struct resbuf rb;

    for ( ; (bitem = *blist) != NULL; ++blist) {
        ads_getvar(bitem, &rb);
        if (rb.restype != RTSHORT) {
            ads_printf(/*MSG17*/"No such AutoCAD variable: %s\n", bitem);
            if (rb.restype == RTSTR)
                free(rb.resval.rstring);
            continue;
        }
        sprintf(value, "%d", rb.resval.rint);
        ads_set_tile(hdlg, bitem, value);
    }
    for ( ; (ritem = *rlist) != NULL; ++rlist) {
        ads_getvar(ritem, &rb);
        if (rb.restype != RTREAL) {
            ads_printf(/*MSG18*/"No such AutoCAD variable: %s\n", ritem);
            if (rb.restype == RTSTR)
                free(rb.resval.rstring);
            continue;
        }
        ads_rtos(rb.resval.rreal, -1, -1, value);
        ads_set_tile(hdlg, ritem, value);
    }
    ads_set_tile(hdlg, /*MSG0*/"test_item", /*MSG0*/"test_value");
}


/* set modified dimension variables in AutoCAD */

static void
/*FCN*/set_dimvars(ads_hdlg hdlg)
{
    char *bitem;
    char *ritem;
    char **blist = dimbools;
    char **rlist = dimreals;
    char val[MAX_TILE_STR+1];
    struct resbuf rb;

    /* Check all the checkbox tiles for new values */
    for ( ; (bitem = *blist) != NULL; ++blist) {
        /* Get the new value of tiles */
        ads_get_tile(hdlg, bitem, val, MAX_TILE_STR);

        rb.restype = RTSHORT;
        rb.resval.rint = atoi(val);
        ads_setvar(bitem, &rb);
    }
    /* Check all the edit box tiles for new values */
    for ( ; (ritem = *rlist) != NULL; ++rlist) {
        /* Get the new value of tiles */
        ads_get_tile(hdlg, ritem, val, MAX_TILE_STR);
        rb.restype = RTREAL;
        ads_distof(val, -1, &rb.resval.rreal);
        ads_setvar(ritem, &rb);
    }
    ads_get_tile(hdlg, /*MSG0*/"test_item", /*MSG0*/"test_value", 50);
}


/* 

		SETCOLOR -- Color Dialog

*/

int
/*FCN*/setcolor()
{
    int idx;
    ads_hdlg hdlg;
    int dlg_status;
    struct resbuf rb;
    char *cptr, *ptr;
    char cname[10];
    int dcl_id;

    /* Load the dialog file */
    ads_load_dialog(/*MSG0*/"dlgtest.dcl", &dcl_id);
    if (dcl_id < 0) {
	ads_printf(/*MSG19*/"Error loading \"dlgtest.dcl\"\n");
	return RTERROR;
    }
    /* initialize the setcolor dialog, no default callback function */
    ads_new_dialog(/*MSG0*/"setcolor", dcl_id, (CLIENTFUNC)0, &hdlg);
    if (hdlg == NULL) {
	ads_printf(/*MSG20*/"new_dialog for setcolor failed\n");
	ads_unload_dialog(dcl_id);
	return RTERROR;
    }
    /* Get the current color from AutoCAD */
    ads_getvar(/*MSG0*/"CECOLOR", &rb);
    /* AutoCAD  currently returns  "human readable" colour strings
       like "1 (red)" for the standard colours.  Trim  the  string
       at  the  first space to guarantee we have a valid string to
       restore the colour later.  */
    cptr = rb.resval.rstring;
    ptr = strchr(cptr, ' ');
    if (ptr != NULL)
	*ptr = EOS;
    strcpy(colorstr, cptr);
    /* free(cptr); */	/* Un-comment for production build */
    color = color_save = atoi(colorstr);
    strcpy(colorstr_save, colorstr);

    /* Update other tiles when one is changed by using callback
       functions */
    ads_action_tile(hdlg, /*MSG0*/"edit_col", (CLIENTFUNC)editcol_cb);
    ads_action_tile(hdlg, /*MSG0*/"list_col", (CLIENTFUNC)listcol_cb);
    ads_action_tile(hdlg, /*MSG0*/"cancel", (CLIENTFUNC)cancelcol_cb);

    ads_action_tile(hdlg, /*MSG0*/"help", (CLIENTFUNC)help_cb);

    /* Use the client data pointer to store the key of each tile,
       for convenient access during callbacks.   We could use
       get_attr_string during the callbacks instead. */
    ads_client_data_tile(hdlg, /*MSG0*/"edit_col", /*MSG0*/"edit_col");
    ads_client_data_tile(hdlg, /*MSG0*/"list_col", /*MSG0*/"list_col");

    /* Fill list box */
    ads_start_list(hdlg, /*MSG0*/"list_col", LIST_NEW, 0);
    for (idx = 0; idx < 8; ++idx)
	ads_add_list(colorlist[idx]);
    for (idx = 8; idx < 256; ++idx) {
	sprintf(cname, "%d", idx);
	ads_add_list(cname);
    }
    ads_end_list();

    /* Show initial values in edit box, list box, and image tile */
    ads_set_tile(hdlg, /*MSG0*/"edit_col", colorstr);
    ads_set_tile(hdlg, /*MSG0*/"list_col", colorstr);
    dlg_colortile(hdlg, /*MSG0*/"show_image", color, TRUE);

    /* Hand control over to the dialog until OK or CANCEL is pressed */
    ads_start_dialog(hdlg, &dlg_status);

    /* Dialog ended with OK button, "accept"? */
    if (dlg_status == DLGOK) {
	struct resbuf rb;
	rb.restype = RTSTR;
	if (color == 0) {
	    rb.resval.rstring = "BYLAYER";
	    ads_setvar("cecolor", &rb);
	} else {
	    rb.resval.rstring = colorstr;
	    ads_setvar("cecolor", &rb);
	}
    }
    /* free all memory for dialog */
    ads_unload_dialog(dcl_id);

    return RTNORM;
}


/* EDITCOL_CB -- ADS callback for color edit box.  */

static void CALLB
/*FCN*/editcol_cb(ads_callback_packet *cpkt)
{
    if (cpkt->value == NULL || cpkt->value[0] == EOS) 
	return;

    ads_set_tile(cpkt->dialog, /*MSG0*/"list_col", cpkt->value);
    color = atoi(cpkt->value);
    strcpy(colorstr, cpkt->value);
    dlg_colortile(cpkt->dialog, /*MSG0*/"show_image", color, TRUE);
}

#define MAXKEYLEN 32

/* LISTCOL_CB -- ADS callback for color edit box.  */

static void CALLB
/*FCN*/listcol_cb(ads_callback_packet *cpkt)
{
    char akey[MAXKEYLEN];

    if (cpkt->value == NULL || cpkt->value[0] == EOS)
	return;

    /* Get key */
    ads_get_attr_string(cpkt->tile, /*MSG0*/"key", akey, MAXKEYLEN);
    if (strcmp(akey, "list_col") != 0)
	ads_printf("\nlist_col key is incorrect: %s != list_col\n", akey);

    ads_set_tile(cpkt->dialog, /*MSG0*/"edit_col", cpkt->value);
    color = atoi(cpkt->value);
    strcpy(colorstr, cpkt->value);
    dlg_colortile(cpkt->dialog, /*MSG0*/"show_image", color, TRUE);
}

/*HELP_CB -- ADS callback for help button.  */

static void CALLB
/*FCN*/help_cb(ads_callback_packet *cpkt)
{
    /* request main Acad help, just to show you can do it. */
    ads_help(NULL, NULL, 0);
}

/* CANCELCOL_CB -- ADS callback for color edit box - cancel pressed.  */

static void CALLB
/*FCN*/cancelcol_cb(ads_callback_packet *cpkt)
{
    color = color_save;
    strcpy(colorstr, colorstr_save);
    dlg_colortile(cpkt->dialog, /*MSG0*/"show_image", color, TRUE);
}

/* DLG_COLORTILE -- Color a tile.  "dialog" can be NULL to use the
		    current dialog.  Values for "color" may be 0 to
		    255.  "color" may be one of the defines in
		    colours.h, such as RED.   Draws border if
		    borderflag is TRUE. */

void
/*FCN*/dlg_colortile(ads_hdlg hdlg, char *key, short color, int borderflag)
{
    short width, height;

    ads_dimensions_tile(hdlg, key, &width, &height);
    ads_start_image(hdlg, key);
    ads_fill_image(0, 0, width, height, color);

    if (borderflag) {
	/* Put border around color */
	dlg_rect(0, 0, width, height, WHITE);
    }
    ads_end_image();
}



/* DLG_RECT -- Draw a rectangle in an image tile.  Use tile dimensions
	       to draw border around tile.  Assumes start_image
	       has been called. */

void
/*FCN*/dlg_rect(short x, short y, short width, short height, short color)
{
    short x2, y2;

    x2 = x + width - 1;
    y2 = y + height - 1;
    ads_vector_image(x,  y,  x,  y2, color);
    ads_vector_image(x,  y2, x2, y2, color);
    ads_vector_image(x2, y2, x2, y,  color);
    ads_vector_image(x2, y,  x,  y,  color);
}


/* =================== Arx Module Interface Functions ================ */

AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* ptr) {

    if (ptr != NULL) {
        // We have been handed some kind of object
        // but we aren't going to do anything with it.
    }

    switch(msg) {
	case AcRx::kInitAppMsg:
            acrxUnlockApplication(ptr);
	    ads_printf(/*MSG10*/"Functions: 1-rxdimen 2-rxsetcolor 3-rxslide.\n");
	    break;
        case AcRx::kInvkSubrMsg:
            dofun();
            break;
        case AcRx::kLoadDwgMsg:
            funcload();
            break;
        case AcRx::kUnloadDwgMsg:
            funcunload();
            ads_printf(/*MSG2*/"Unloading.\n");
            break;
	case AcRx::kUnloadAppMsg:
        default:
	    break;
    }
    return AcRx::kRetOK;
}
