/*****************************************************************************
*   General routines common to graphics driver.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.1, June 1993.  *
*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "irit_sm.h"
#include "genmat.h"
#include "getarg.h"
#include "config.h"
#include "iritprsr.h"
#include "allocate.h"
#include "attribut.h"
#include "iritgrap.h"
#include "irit_soc.h"

#ifdef OS2GCC
#define INCL_DOSPROCESS
#include <os2.h>
#endif /* OS2GCC */

#define MAX_ROTATE_ANGLE	45.0 /* Max. rates used by interactive mode. */
#define MAX_TRANSLATE_FACTOR	2.0
#define MAX_SCALE_FACTOR	2.0
#define MAX_CLIP_FACTOR		0.5

typedef struct StateNameNumType {
   IGGlblStateType Num;
   char *Name;
} StateNameNumType;

static StateNameNumType StateNameNum[] =
{
    { IG_STATE_MORE_SENSITIVE,		"MoreSense" },
    { IG_STATE_LESS_SENSITIVE,		"LessSense" },
    { IG_STATE_SCR_OBJ_TGL,		"ScrnObjct" },
    { IG_STATE_PERS_ORTHO_TGL,		"PerpsOrtho" },
    { IG_STATE_DEPTH_CUE,		"DepthCue" },
    { IG_STATE_DRAW_SOLID,		"DrawSolid" },
    { IG_STATE_DOUBLE_BUFFER,		"DblBuffer" },
    { IG_STATE_ANTI_ALIASING,		"AntiAlias" },
    { IG_STATE_DRAW_INTERNAL,		"DrawIntrnl" },
    { IG_STATE_DRAW_VNORMAL,		"DrawVNrml" },
    { IG_STATE_DRAW_PNORMAL,		"DrawPNrml" },
    { IG_STATE_DRAW_SRF_MESH,		"DSrfMesh" },
    { IG_STATE_DRAW_SRF_POLY,		"DSrfPoly" },
    { IG_STATE_FOUR_PER_FLAT,		"4PerFlat" },
    { IG_STATE_MORE_ISOLINES,		"MoreIso" },
    { IG_STATE_LESS_ISOLINES,		"LessIso" },
    { IG_STATE_FINER_APPROX,		"FinrAprx" },
    { IG_STATE_COARSER_APPROX,		"CrsrAprx" },
    { IG_STATE_LONGER_VECTORS,		"LngrVecs" },
    { IG_STATE_SHORTER_VECTORS,		"ShrtrVecs" },
    { IG_STATE_WIDER_LINES,		"WiderLns" },
    { IG_STATE_NARROW_LINES,		"NarrwLns" },
    { IG_STATE_FINER_ADAP_ISO,		"FinrAdapIso" },
    { IG_STATE_COARSER_ADAP_ISO,	"CRsrAdapIso" },
    { IG_STATE_FINER_RULED_SRF,		"FinerRld" },
    { IG_STATE_COARSER_RULED_SRF,	"CrsrRld" },
    { IG_STATE_RULED_SRF_APPROX,	"RuledSrfApx" },
    { IG_STATE_ADAP_ISO_DIR,		"AdapIsoDir" },
    { IG_STATE_VIEW_FRONT,		"Front" },
    { IG_STATE_VIEW_SIDE,		"Side" },
    { IG_STATE_VIEW_TOP,		"Top" },
    { IG_STATE_VIEW_ISOMETRY,		"Isometry" },
    { 0,				NULL }
};

static int
    GlblDelayedClear = FALSE,
    GlblProcessCommandMesssages = TRUE,
    GlblNormalLenAux = 200;

static MatrixType PushViewMat, PushPrspMat;
static MatrixType IsometryViewMat = {	      /* Isometric view, by default. */
    { -0.707107, -0.408248, 0.577350, 0.000000 },
    {  0.707107, -0.408248, 0.577350, 0.000000 },
    {  0.000000,  0.816496, 0.577350, 0.000000 },
    {  0.000000,  0.000000, 0.000000, 1.000000 }
};

#ifdef __UNIX__
static char
    *GlblBackGroundStr = "20 20 20";

int IGGlblBackGroundColor[3] = { 20, 20, 20 },
#else
static char
    *GlblBackGroundStr = "63 63 63";

int IGGlblBackGroundColor[3] = { 63, 63, 63 },
#endif /* __UNIX__ */
    IGGlblDrawInternal = FALSE,
    IGGlblDrawVNormal = FALSE,
    IGGlblDrawPNormal = FALSE,
    IGGlblMore = FALSE,
    IGGlblForceUnitMat = FALSE,
    IGGlblDrawSolid = FALSE,
    IGGlblDoDoubleBuffer = TRUE,
    IGGlblNumOfIsolines = 8,
    IGGlblSamplesPerCurve = 6,
    IGGlblLineWidth = 1,
    IGGlblAdapIsoDir = CAGD_CONST_U_DIR,
    IGGlblFineNess = 5,
    IGGlblDepthCue = TRUE,
    IGGlblFourPerFlat = TRUE,
    IGGlblAntiAliasing = TRUE,
    IGGlblDrawSurfaceMesh = FALSE,
    IGGlblDrawSurfacePoly = FALSE,
    IGGlblStandAlone = TRUE,
    IGGlblTransformMode = IG_TRANS_SCREEN,
    IGGlblViewMode = IG_VIEW_PERSPECTIVE,
    IGGlblDebugObjectsFlag = FALSE,
    IGGlblDebugEchoInputFlag = FALSE,
    IGGlblIntensityHighState = TRUE,
    IGGlblDisplayListIsUsed = FALSE,
    IGGlblAbortKeyPressed = FALSE,
    IGGlblNumFiles;

char
    *IGGlblTransPrefPos = "455, 640, 520, 965",
    *IGGlblViewPrefPos = "1,   450, 520, 965",
    **IGGlblFileNames;

RealType
    IGGlblChangeFactor = 1.0,
    IGGlblZMinClip = -2.0,
    IGGlblZMaxClip = 2.0,
    IGGlblNormalLen = 0.2;

MatrixType IGGlblCrntViewMat;

IPObjectStruct
    *IGGlblDisplayList = NULL;

static ConfigStruct SetUp[] =
{
  { "TransPrefPos",	(VoidPtr) &IGGlblTransPrefPos,	SU_STRING_TYPE },
  { "ViewPrefPos",	(VoidPtr) &IGGlblViewPrefPos,	SU_STRING_TYPE },
  { "BackGround",	(VoidPtr) &GlblBackGroundStr,	SU_STRING_TYPE },
  { "Internal",		(VoidPtr) &IGGlblDrawInternal,	SU_BOOLEAN_TYPE },
  { "DrawVNormal",	(VoidPtr) &IGGlblDrawVNormal,	SU_BOOLEAN_TYPE },
  { "DrawPNormal",	(VoidPtr) &IGGlblDrawPNormal,	SU_BOOLEAN_TYPE },
  { "MoreVerbose",	(VoidPtr) &IGGlblMore,		SU_BOOLEAN_TYPE },
  { "UnitMatrix",	(VoidPtr) &IGGlblForceUnitMat,	SU_BOOLEAN_TYPE },
  { "DrawSolid",	(VoidPtr) &IGGlblDrawSolid,	SU_BOOLEAN_TYPE },
  { "DoubleBuffer",	(VoidPtr) &IGGlblDoDoubleBuffer,SU_BOOLEAN_TYPE },
  { "NumOfIsolines",	(VoidPtr) &IGGlblNumOfIsolines,	SU_INTEGER_TYPE },
  { "SamplesPerCurve",	(VoidPtr) &IGGlblSamplesPerCurve, SU_INTEGER_TYPE },
  { "LineWidth",	(VoidPtr) &IGGlblLineWidth,	SU_INTEGER_TYPE },
  { "AdapIsoDir",	(VoidPtr) &IGGlblAdapIsoDir,	SU_INTEGER_TYPE },
  { "FineNess",		(VoidPtr) &IGGlblFineNess,	SU_INTEGER_TYPE },
  { "DepthCue",		(VoidPtr) &IGGlblDepthCue,	SU_BOOLEAN_TYPE },
  { "FourPerFlat",	(VoidPtr) &IGGlblFourPerFlat,	SU_BOOLEAN_TYPE },
  { "AntiAlias",	(VoidPtr) &IGGlblAntiAliasing,	SU_BOOLEAN_TYPE },
  { "DrawSurfaceMesh",	(VoidPtr) &IGGlblDrawSurfaceMesh, SU_BOOLEAN_TYPE },
  { "DrawSurfacePoly",	(VoidPtr) &IGGlblDrawSurfacePoly, SU_BOOLEAN_TYPE },
  { "StandAlone",	(VoidPtr) &IGGlblStandAlone,	SU_BOOLEAN_TYPE },
  { "TransMode",	(VoidPtr) &IGGlblTransformMode,	SU_INTEGER_TYPE },
  { "ViewMode",		(VoidPtr) &IGGlblViewMode,	SU_INTEGER_TYPE },
  { "NormalLength",	(VoidPtr) &GlblNormalLenAux,	SU_INTEGER_TYPE },
  { "DebugObjects",	(VoidPtr) &IGGlblDebugObjectsFlag, SU_BOOLEAN_TYPE },
  { "DebugEchoInput",	(VoidPtr) &IGGlblDebugEchoInputFlag, SU_BOOLEAN_TYPE },
};
#define NUM_SET_UP	(sizeof(SetUp) / sizeof(ConfigStruct))

#ifdef NO_CONCAT_STR
static char
    *GenVersionStr = "	 Version 4.0,	Gershon Elber,\n\
	(C) Copyright 1989/90/91/92/93 Gershon Elber, Non commercial use only.";
#else
static char
    *GenVersionStr = "	" VERSION ",	Gershon Elber,	"
    __DATE__ ",   " __TIME__ "\n"
    "(C) Copyright 1989/90/91/92/93 Gershon Elber, Non commercial use only.";
#endif /* NO_CONCAT_STR */

static char
    *GenCtrlStr = " s%- u%- n%- N%- i%- c%- m%- I%-#IsoLines!d S%-#SampPerCrv!d f%-FineNess!d l%-LineWidth!d d%- D%- L%-NormalLen!d 4%- b%-BackGround!d!d!d M%- P%- z%- DFiles!*s";

static void AddReplaceObjDisplayList(IPObjectStruct **DisplayList,
				     IPObjectStruct *NewObj, char *ObjName);

/****************************************************************************
* Set up configuration of global variables.				    *
****************************************************************************/
void IGConfigureGlobals(char *PrgmName, int argc, char **argv)
{
    int Error,
	IsoLinesFlag = FALSE,
	SamplesPerCurveFlag = FALSE,
	FinenessFlag = FALSE,
	LineWidthFlag = FALSE,
	NormalLenFlag = FALSE,
	BackGroundFlag = FALSE,
	VerFlag = FALSE;
    char Line[LINE_LEN_LONG];

    Config(PrgmName, SetUp, NUM_SET_UP);   /* Read config. file if exists. */

    sscanf(GlblBackGroundStr, "%d %d %d",
	   &IGGlblBackGroundColor[0],
	   &IGGlblBackGroundColor[1],
	   &IGGlblBackGroundColor[2]);

    strcpy(Line, PrgmName);
    strcat(Line, GenCtrlStr);
    if ((Error = GAGetArgs(argc, argv, Line,
			   &IGGlblStandAlone, &IGGlblForceUnitMat,
			   &IGGlblDrawVNormal, &IGGlblDrawPNormal,
			   &IGGlblDrawInternal, &IGGlblDepthCue,
			   &IGGlblMore, &IsoLinesFlag, &IGGlblNumOfIsolines,
			   &SamplesPerCurveFlag, &IGGlblSamplesPerCurve,
			   &FinenessFlag, &IGGlblFineNess,
			   &LineWidthFlag, &IGGlblLineWidth,
			   &IGGlblDebugObjectsFlag, &IGGlblDebugEchoInputFlag,
			   &NormalLenFlag, &GlblNormalLenAux,
			   &IGGlblFourPerFlat, &BackGroundFlag,
			   &IGGlblBackGroundColor[0], &IGGlblBackGroundColor[1],
			   &IGGlblBackGroundColor[2],
			   &IGGlblDrawSurfaceMesh, &IGGlblDrawSurfacePoly,
			   &VerFlag, &IGGlblNumFiles, &IGGlblFileNames)) != 0) {
	GAPrintErrMsg(Error);
	GAPrintHowTo(Line);
	exit(1);
    }

    if (VerFlag) {
	fprintf(stderr, "\n%s%s\n\n", PrgmName, GenVersionStr);
	GAPrintHowTo(Line);
	ConfigPrint(SetUp, NUM_SET_UP);
	exit(0);
    }

    if (IGGlblDebugEchoInputFlag)
	SocClientEchoInput(TRUE);

    IGGlblNormalLen = GlblNormalLenAux / 1000.0;

    if (IGGlblAdapIsoDir == 1)
	IGGlblAdapIsoDir = CAGD_CONST_U_DIR;
    else if (IGGlblAdapIsoDir == 2)
	IGGlblAdapIsoDir = CAGD_CONST_V_DIR;

    /* Get the data files: */
    if (IGGlblNumFiles > 0) {
	IGGlblDisplayList = IritPrsrGetDataFiles(IGGlblFileNames,
						 IGGlblNumFiles, TRUE, TRUE);
	IGGlblViewMode = IritPrsrWasPrspMat ? IG_VIEW_PERSPECTIVE
					    : IG_VIEW_ORTHOGRAPHIC;
	if (IGGlblForceUnitMat) {
	    MatGenUnitMat(IritPrsrViewMat);
	    IGGlblViewMode = IG_VIEW_ORTHOGRAPHIC;
	}
    }

    GEN_COPY(PushViewMat, IritPrsrViewMat, sizeof(MatrixType));
    GEN_COPY(PushPrspMat, IritPrsrPrspMat, sizeof(MatrixType));
}

/****************************************************************************
* Set up configuration of global variables.				    *
****************************************************************************/
void IGProcessCommandMessages(int ProcessCommandMessages)
{
    GlblProcessCommandMesssages = ProcessCommandMessages;
}

/*****************************************************************************
* Routine to attempt and read an object from communication socket.	     *
*   Returns TRUE if screen needs to be redrawn in the case that the	     *
* DisplayList was modified.						     *
*   This function DOES NOT BLOCK if no input is available.		     *
*   Handles commands via a string object with name "COMMAND_".		     *
*****************************************************************************/
int IGReadObjectsFromSocket(int ViewMode, IPObjectStruct **DisplayList)
{
    int Redraw = FALSE;

    IPObjectStruct *PObjs;

    if ((PObjs = SocClientReadOneObject()) != NULL) {
	IPObjectStruct *PObj;

#       ifdef __WINNT__
	    /* Slim chance that a race will oocur if the following test and */
	    /* set is done simultanuously. Should use semaphors probably.   */
	    while (IGGlblDisplayListIsUsed)
	        IritSleep(10);
	    IGGlblDisplayListIsUsed = TRUE;
#	endif /* __WINNT__ */
#	ifdef OS2GCC
	while (IGGlblDisplayListIsUsed) {
	    IritSleep(10);
	    DosEnterCritSec();
	    if (!IGGlblDisplayListIsUsed) {
		IGGlblDisplayListIsUsed = TRUE;
		DosExitCritSec();
		break;
	    }
	    DosExitCritSec();
	}
#	endif /* OS2GCC */

	Redraw = TRUE;

	if (GlblDelayedClear) {
#	    ifdef OS2GCC
	        /* For some reason without this delay, this  */
	        /* driver crashes on command toggles.	     */
		IritSleep(100);
#	    endif /* OS2GCC */
	    GlblDelayedClear = FALSE;
	    IPFreeObjectList(*DisplayList);
	    *DisplayList = NULL;
	}

	while (PObjs != NULL) {
	    PObj = PObjs;
	    PObjs = PObjs -> Pnext;
	    PObj -> Pnext = NULL;

	    if (IGGlblDebugObjectsFlag)
		IritPrsrPutObject(stdout, PObj);

	    if (GlblProcessCommandMesssages) {
		switch (PObj -> ObjType) {
		    case IP_OBJ_STRING:
		        if (stricmp(PObj -> Name, "COMMAND_") == 0) {
			    char
				*Str = PObj -> U.Str;

#			    ifdef OS2GCC
			        /* For some reason without this delay, this  */
			        /* driver crashes on command toggles.	     */
				IritSleep(300);
#			    endif /* OS2GCC */
			    if (stricmp(Str, "CLEAR") == 0) {
				IPFreeObjectList(*DisplayList);
				*DisplayList = NULL;
			    }
			    if (stricmp(Str, "DCLEAR") == 0) {
				GlblDelayedClear = TRUE;
				Redraw = FALSE;
			    }
			    else if (strnicmp(Str, "MSAVE", 5) == 0) {
				IGSaveCurrentMat(ViewMode, &PObj -> U.Str[6]);
				Redraw = FALSE;
			    }
			    else if (strnicmp(Str, "REMOVE", 6) == 0) {
				AddReplaceObjDisplayList(DisplayList, NULL,
							&PObj -> U.Str[7]);
				Redraw = TRUE;
			    }
			    else if (stricmp(Str, "DISCONNECT") == 0) {
				SocClientCloseSocket();
				Redraw = FALSE;
			    }
			    else if (stricmp(Str, "EXIT") == 0) {
				SocClientCloseSocket();
				exit(0);
			    }
			    else if (stricmp(Str, "BEEP") == 0) {
				IGIritBeep();
				Redraw = FALSE;
			    }
			    else if (strnicmp(Str, "STATE", 5) == 0) {
				int i;

				Str = &Str[6];

				for (i = 0;
				     StateNameNum[i].Name != NULL;
				     i++) {
				    if (stricmp(Str,
						StateNameNum[i].Name) == 0) {
					IGHandleState(StateNameNum[i].Num,
						      TRUE);
					break;
				    }
				}
			    }
			}
			IPFreeObject(PObj);
			break;
		    case IP_OBJ_MATRIX:
			if (stricmp(PObj -> Name, "VIEW_MAT") == 0) {
			    GEN_COPY(IritPrsrViewMat, *PObj -> U.Mat,
				     sizeof(MatrixType));
			}
			else if (stricmp(PObj -> Name, "PRSP_MAT") == 0) {
			    GEN_COPY(IritPrsrPrspMat, *PObj -> U.Mat,
				     sizeof(MatrixType));
			}
			IPFreeObject(PObj);
			break;
		    default:
			AddReplaceObjDisplayList(DisplayList, PObj, NULL);
			break;
		}
	    }
	    else {
		AddReplaceObjDisplayList(DisplayList, PObj, NULL);
	    }
	}

	IGGlblDisplayListIsUsed = FALSE;

	return Redraw;
    }
    else
	return Redraw;
}

/****************************************************************************
* Add or replace an object on the display list.				    *
*   If ObjName is not NULL, that object is removed from the display list.   *
*   Otherwise NewObj is added to the display list, possibly replacing an    *
* object on the display list with the same name.			    *
****************************************************************************/
static void AddReplaceObjDisplayList(IPObjectStruct **DisplayList,
				     IPObjectStruct *NewObj, char *ObjName)
{
    int Remove = ObjName != NULL;
    char
	*Name = ObjName != NULL ? ObjName : NewObj -> Name;
    IPObjectStruct *PObj;

    /* If object has no name or display list is empty, add it. */
    if (!Remove &&
	NewObj != NULL &&
	(stricmp(NewObj -> Name, "none") == 0 ||
	 strlen(NewObj -> Name) == 0)) {
	NewObj -> Pnext = *DisplayList;
	*DisplayList = NewObj;
	return;
    }
    if (*DisplayList == NULL) {
	if (NewObj != NULL) {
	    *DisplayList = NewObj;
	}
	return;
    }

    if (stricmp(Name, (*DisplayList) -> Name) == 0) {
	if (Remove) {
	    PObj = *DisplayList;
	    *DisplayList = (*DisplayList) -> Pnext;
	    IPFreeObject(PObj);
	}
	else {
	    NewObj -> Pnext = (*DisplayList) -> Pnext;
	    IPFreeObject(*DisplayList);
	    *DisplayList = NewObj;
	}
    }
    else {
	for (PObj = *DisplayList; PObj -> Pnext != NULL; PObj = PObj -> Pnext) {
	    if (stricmp(Name, PObj -> Pnext -> Name) == 0) {
		IPObjectStruct
		    *PObjTmp = PObj -> Pnext;

		if (Remove) {
		    PObj -> Pnext = PObjTmp -> Pnext;
		}
		else {
		    PObj -> Pnext = NewObj;
		    NewObj -> Pnext = PObjTmp -> Pnext;
		}
		IPFreeObject(PObjTmp);
		return;
	    }
	}

	/* Name was not found. */
	if (!Remove) {
	    NewObj -> Pnext = *DisplayList;
	    *DisplayList = NewObj;
	}
    }
}

/****************************************************************************
* Save the current viewing matrices.					    *
****************************************************************************/
void IGSaveCurrentMat(int ViewMode, char *Name)
{
    int	i, j;
    FILE *f;

    if (Name == NULL)
	Name = IG_DEFAULT_IRIT_MAT;

    if (strlen(Name) == 0 || (f = fopen(Name, "wt")) == NULL) {
	IGIritBeep();
	return;
    }

    fprintf(f, "[OBJECT MATRICES\n    [OBJECT VIEW_MAT\n\t[MATRIX");
    for (i = 0; i < 4; i++) {
	fprintf(f, "\n\t    ");
	for (j = 0; j < 4; j++)
	    fprintf(f, "%12.9lf ", IritPrsrViewMat[i][j]);
    }
    fprintf(f, "\n\t]\n    ]\n");

    if (ViewMode == IG_VIEW_PERSPECTIVE) {
	fprintf(f, "    [OBJECT PRSP_MAT\n\t[MATRIX");
	for (i = 0; i < 4; i++) {
	    fprintf(f, "\n\t    ");
	    for (j = 0; j < 4; j++)
		fprintf(f, "%12.9lf ", IritPrsrPrspMat[i][j]);
	}
	fprintf(f, "\n\t]\n    ]\n");
    }

    fprintf(f, "]\n");

    fclose(f);
}

/******************************************************************************
* Process the given event. Returns TRUE if redraw of view window is needed    *
******************************************************************************/
int IGProcessEvent(IGGraphicEventType Event, RealType ChangeFactor)
{
    int UpdateView = TRUE;
    MatrixType Mat;

    MatGenUnitMat(Mat);

    switch (Event) {
	case IG_EVENT_SCR_OBJ_TGL:    /* Its Coordinate system - toggle it. */
	    UpdateView = FALSE;
	    break;
	case IG_EVENT_PERS_ORTHO_TGL:	      /* Its View mode - toggle it. */
	    break;
	case IG_EVENT_PERS_ORTHO_Z: /* Its Perspective Z focal point modif. */
	    if (IGGlblViewMode != IG_VIEW_PERSPECTIVE) {
		IGIritBeep();
		UpdateView = FALSE;
		break;
	    }
	    /* Make it between 0.5 and 1.5: */
	    ChangeFactor = ChangeFactor / 2.0 + 1.0;
	    IritPrsrPrspMat[2][2] *= ChangeFactor;
	    IritPrsrPrspMat[2][3] *= ChangeFactor;
	    IritPrsrPrspMat[3][2] *= ChangeFactor;
	    break;
	case IG_EVENT_ROTATE_X:		   /* Its rotation along the X axis. */
	    MatGenMatRotX1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
	    break;
	case IG_EVENT_ROTATE_Y:		   /* Its rotation along the Y axis. */
	    MatGenMatRotY1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
	    break;
	case IG_EVENT_ROTATE_Z:		   /* Its rotation along the Z axis. */
	    MatGenMatRotZ1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
	    break;
	case IG_EVENT_TRANSLATE_X:	/* Its translation along the X axis. */
	    MatGenMatTrans(ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0, 0.0, Mat);
	    break;
	case IG_EVENT_TRANSLATE_Y:	/* Its translation along the Y axis. */
	    MatGenMatTrans(0.0, ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0, Mat);
	    break;
	case IG_EVENT_TRANSLATE_Z:	/* Its translation along the Z axis. */
	    MatGenMatTrans(0.0, 0.0, ChangeFactor * MAX_TRANSLATE_FACTOR, Mat);
	    break;
	case IG_EVENT_SCALE:		      /* Its scaling along all axes. */
	    if (ChangeFactor > 0.0)		      /* Make it around 1... */
	        ChangeFactor = ChangeFactor * MAX_SCALE_FACTOR + 1.0;
	    else
	        ChangeFactor = 1.0 / (-ChangeFactor * MAX_SCALE_FACTOR + 1.0);
	    MatGenMatScale(ChangeFactor, ChangeFactor, ChangeFactor, Mat);
	    break;
	case IG_EVENT_NEAR_CLIP:		     /* Near plane clipping. */
	    IGGlblZMinClip += ChangeFactor * MAX_CLIP_FACTOR;
	    break;
	case IG_EVENT_FAR_CLIP:		              /* Far plane clipping. */
	    IGGlblZMaxClip += ChangeFactor * MAX_CLIP_FACTOR;
	    break;
	case IG_EVENT_DEPTH_CUE:
	    break;
	case IG_EVENT_SAVE_MATRIX:
	    IGSaveCurrentMat(IGGlblViewMode, NULL);
	    UpdateView = FALSE;
	    break;
	case IG_EVENT_PUSH_MATRIX:
	    GEN_COPY(PushViewMat, IritPrsrViewMat, sizeof(MatrixType));
	    GEN_COPY(PushPrspMat, IritPrsrPrspMat, sizeof(MatrixType));
	    break;
	case IG_EVENT_POP_MATRIX:
	    GEN_COPY(IritPrsrViewMat, PushViewMat, sizeof(MatrixType));
	    GEN_COPY(IritPrsrPrspMat, PushPrspMat, sizeof(MatrixType));
	    break;
	case IG_EVENT_STATE:
	    IGCreateStateMenu();
	    break;
	default:
	    IGIritBeep();
	    UpdateView = FALSE;
    }

    if (UpdateView) {
	switch (IGGlblTransformMode) {/* Udpate the global viewing matrix. */
	    case IG_TRANS_SCREEN:
	        MatMultTwo4by4(IritPrsrViewMat, IritPrsrViewMat, Mat);
		break;
	    case IG_TRANS_OBJECT:
		MatMultTwo4by4(IritPrsrViewMat, Mat, IritPrsrViewMat);
		break;
	}
    }
    return UpdateView;
}

/******************************************************************************
* Free the attribute named Name from all objects in the IGGlblDisplayList.    *
******************************************************************************/
void IGActiveListFreeNamedAttribute(char *Name)
{
    IPObjectStruct
	*PObj = IGGlblDisplayList;

    for (; PObj != NULL; PObj = PObj -> Pnext) {
	AttrFreeOneAttribute(&PObj -> Attrs, Name);
    }
}

/******************************************************************************
* Handle the event of a pop up window.					      *
******************************************************************************/
int IGDefaultStateHandler(int State, int Refresh)
{
    int UpdateView = TRUE;

    switch (State) {
	case IG_STATE_MORE_SENSITIVE:
	    IGGlblChangeFactor *= 2.0;
	    break;
	case IG_STATE_LESS_SENSITIVE:
	    IGGlblChangeFactor *= 0.5;
	    break;
	case IG_STATE_SCR_OBJ_TGL:
	    IGGlblTransformMode = IGGlblTransformMode == IG_TRANS_OBJECT ?
							 IG_TRANS_SCREEN :
							 IG_TRANS_OBJECT;
	    UpdateView = FALSE;
	    break;
	case IG_STATE_PERS_ORTHO_TGL:
	    IGGlblViewMode = IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
					       IG_VIEW_ORTHOGRAPHIC :
					       IG_VIEW_PERSPECTIVE;
	    break;
	case IG_STATE_DEPTH_CUE:
	    IGGlblDepthCue = !IGGlblDepthCue;
	    break;
	case IG_STATE_DRAW_INTERNAL:
	    IGGlblDrawInternal = !IGGlblDrawInternal;
	    break;
	case IG_STATE_DRAW_VNORMAL:
	    IGGlblDrawVNormal = !IGGlblDrawVNormal;
	    break;
	case IG_STATE_DRAW_PNORMAL:
	    IGGlblDrawPNormal = !IGGlblDrawPNormal;
	    break;
	case IG_STATE_DRAW_SRF_MESH:
	    IGGlblDrawSurfaceMesh = !IGGlblDrawSurfaceMesh;
	    break;
	case IG_STATE_DRAW_SRF_POLY:
	    IGGlblDrawSurfacePoly = !IGGlblDrawSurfacePoly;
	    break;
	case IG_STATE_FOUR_PER_FLAT:
	    IGGlblFourPerFlat = !IGGlblFourPerFlat;
	    IGActiveListFreeNamedAttribute("_polygons");
	    break;
	case IG_STATE_MORE_ISOLINES:
	    IGGlblNumOfIsolines *= 2;
	    IGActiveListFreeNamedAttribute("_isoline");
	    break;
	case IG_STATE_LESS_ISOLINES:
	    IGGlblNumOfIsolines /= 2;
	    if (IGGlblNumOfIsolines < 2)
		IGGlblNumOfIsolines = 2;
	    IGActiveListFreeNamedAttribute("_isoline");
	    break;
	case IG_STATE_LONGER_VECTORS:
	    IGGlblNormalLen *= 2.0;
	    break;
	case IG_STATE_SHORTER_VECTORS:
	    IGGlblNormalLen /= 2.0;
	    break;
	case IG_STATE_FINER_APPROX:
	    IGGlblSamplesPerCurve++;
	    IGGlblFineNess++;
	    IGActiveListFreeNamedAttribute("_polygons");
	    IGActiveListFreeNamedAttribute("_isoline");
	    break;
	case IG_STATE_COARSER_APPROX:
	    IGGlblFineNess--;
	    if (IGGlblFineNess < 2)
		IGGlblFineNess = 2;
	    IGGlblSamplesPerCurve--;
	    if (IGGlblSamplesPerCurve < 2)
		IGGlblSamplesPerCurve = 2;
	    IGActiveListFreeNamedAttribute("_polygons");
	    IGActiveListFreeNamedAttribute("_isoline");
	    break;
	case IG_STATE_VIEW_FRONT:
	    IGGlblViewMode = IG_VIEW_ORTHOGRAPHIC;
	    MatGenUnitMat(IritPrsrViewMat);
	    break;
	case IG_STATE_VIEW_SIDE:
	    IGGlblViewMode = IG_VIEW_ORTHOGRAPHIC;
	    MatGenMatRotY1(DEG2RAD(90.0), IritPrsrViewMat);
	    break;
	case IG_STATE_VIEW_TOP:
	    IGGlblViewMode = IG_VIEW_ORTHOGRAPHIC;
	    MatGenMatRotX1(DEG2RAD(90.0), IritPrsrViewMat);
	    break;
	case IG_STATE_VIEW_ISOMETRY:
	    IGGlblViewMode = IG_VIEW_ORTHOGRAPHIC;
	    GEN_COPY(IritPrsrViewMat, IsometryViewMat, sizeof(MatrixType));
	    break;
	default:
	    UpdateView = FALSE;
	    break;
    }

    return UpdateView;
}
