/******************************************************************************/
/* Copyright 1995 MBARI                                                       */
/******************************************************************************/
/* Summary  : CTD Data Display GUI Callbacks Module                           */
/* Filename : guiCallbacks.c                                                  */
/* Author   : Janice Tarrant                                                  */
/* Project  : Tiburon                                                         */
/* Version  : Version 1.0                                                     */
/* Created  : 02/13/95                                                        */
/* Modified : 03/21/96                                                        */
/* Archived :                                                                 */
/******************************************************************************/
/* Modification History :                                                     */
/* $Header: /usr/tiburon/unix/gui/ctd/RCS/guiCallbacks.c,v 1.1 1998/03/12 17:52:41 oreilly Exp $
 * $Log: guiCallbacks.c,v $
 * Revision 1.1  1998/03/12 17:52:41  oreilly
 * Initial revision
 *
 *
 */
/******************************************************************************/
/*
 * Generated by the ICS builderXcessory (BX).
 *
 *
 * Builder Xcessory 2.5.
 *
 */
#include <Xm/Xm.h>
#include <Xm/List.h>
#include <Xm/TextF.h>
#include <Xm/Text.h>
#include <Xm/MessageB.h>
#include <Xm/FileSB.h>
#include <Xm/SelectioB.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <mbari/types.h>        /* MBARI style guide type declarations        */
#include <dmUnix.h>             /* unix data manager definitions              */
#include <semLib.h>             /* semaphore library                          */
#include <time.h>               /* MBARI time declarations                    */
#include <datamgr.h>            /* data manager declarations                  */
#include "ctdDisplay.h"

#define X_LIST_MESSAGE "Cannot select more than four x axis variables"
#define Y_LIST_MESSAGE "Must have one y axis variable selected"
#define STATUS_LIST_MESSAGE "Cannot select more than five status line variables"
#define STATUS_RATE_MESSAGE "Value entered is out of range"
#define MIN_MESSAGE "Value entered is greater than or equal to max"
#define MAX_MESSAGE "Value entered is less than or equal to min"
#define Y_MAX_MESSAGE "Value entered is before start of data acquisition"
#define COLOUR_MESSAGE "Selected colour is not available"
#define DIVS_MESSAGE "Value entered can't be less than one"
#define TIME_MESSAGE "Time range specified is before start of data acquisition"
#define MARK_MESSAGE1 "Select mark file name"
#define MARK_MESSAGE2 "Number of marks exceeds maximum"
#define MARK_MESSAGE3 "Enter a comment for the mark file:"

MBool               xItemsListed = TRUE;
MBool               yItemListed = FALSE;
MBool               statusItemsListed = FALSE;
XtIntervalId        displayTimeoutId;
XtIntervalId        statusLineTimeoutId;
parametersAltered   paramsAltered;

extern Widget       appShell;
extern Widget       xShell[NUM_X_VARS];
extern Widget       xForm[NUM_X_VARS];
extern Widget       yShell;
extern Widget       yForm;
extern Widget       canvas;
extern Widget       colourOptionMenu;
extern Widget       xItemsToggleButton;
extern Widget       yItemToggleButton;
extern Widget       statusItemToggleButton;
extern Widget       displayRateTextField;
extern Widget       statusRateTextField;
extern Widget       linesPlotToggleButton;
extern Widget       dotsPlotToggleButton;
extern Widget       showToggleButton;
extern Widget       scrollToggleButton;
extern GC           gc;

extern configurationParameters configParams;

extern MBool        plotScrolling;
extern MBool        autoStart;
extern MBool        startDisplay;
extern MBool        restartDisplay;
extern MBool        freezeDisplay;
extern MBool        stopAcquisition;
extern MBool        resetYTickLabels;
extern MBool        rescale;
extern Int16        loopNumber;
extern Int32        pointCount;
extern Int32        numDataPoints;
extern Int32        plotStart;
extern Int32        plotEnd;
extern Int32        plotMax;
extern Int32        plotSpan;
extern Flt32        minInSecs;                  /* min time value in seconds  */
extern Flt32        maxInSecs;                  /* max time value in seconds  */
extern Flt32        yTimeUnitsScale;		/* y time units scale         */
extern Flt32        *dataValues[NUM_DATA_POINTS];
extern itemListData listData;

void        updateDisplay(XtPointer clientData, XtIntervalId *id);
void        updateStatusLine(XtPointer clientData, XtIntervalId *id);
void        plotDataPoints(displayUpdateData *displayData, Display *display,
                           Drawable drawable, MBool erase);
void        plotMarks(plotParameters *plotParams, Display *display,
		      Drawable drawable, MBool erase);
Int16       reDrawAxes(plotParameters *plotParams, Display *display,
		       Drawable drawable);
void showXYPosition(Widget w, XtPointer clientData, XEvent *event,
			   Boolean *flag);
void trackXYPosition(Widget w, XtPointer clientData, XEvent *event,
			    Boolean *flag);
void clearXYPosition(Widget w, XtPointer clientData, XEvent *event,
			    Boolean *flag);
Widget      CreatexForm(Widget w, Int16 var, plotParameters *plotParams);
Widget      CreateyForm(Widget w, yItemParameters *yVar,
			plotParameters *plotParams);
Int16       readInParams(char *filename, displayUpdateData *data);
void	    markFilenameCallback(Widget w, XtPointer clientData,
				 XtPointer callData);
void        writeMarkData(Widget w, Flt32 *markData);
void        markOkCallback(Widget w, XtPointer clientData, XtPointer callData);
void        markCancelCallback(Widget w, XtPointer clientData,
			       XtPointer callData);
void        saveFileOkCallback(Widget w, XtPointer clientData,
			       XtPointer callData);
void        saveFileCancelCallback(Widget w, XtPointer clientData,
				   XtPointer callData);
char        *toItemName(itemVariables item);
char        *toUnitsName(unitsOfTime timeUnits);
char        *toColourName(displayColours colour);
void        toColour(xItemParameters *xVar, char *colourName);


/******************************************************************************/
/* Function : itemsListCallback                                               */
/* Purpose  : Responds to an item selection or deselection in the x, y and    */
/*            status line item lists.                                         */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
itemListCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    plotParameters       *plotParams = (plotParameters *) clientData;
    XmListCallbackStruct *list = (XmListCallbackStruct *) callData;
    static Widget        dialog = NULL;
    Arg		         args[256];
    Cardinal             argcnt;
    char                 *text;
    Int16                largestYLabel;
    Int16                var, var2;		/* loop counters              */
    Int16                word;
    Int32                count;
    Int16                lastVar;		/* last x variable index      */

#if DEBUG_ON
    XmStringGetLtoR(list->item, XmFONTLIST_DEFAULT_TAG, &text);
    printf("item selected %s  position %d\n", text, list->item_position);
#endif

    if (xItemsListed)
    {
/* check that number of items selected does not exceed maximum                */
	if (list->selected_item_count > NUM_X_VARS)	/* deselect item and  */
	{						/* display warning    */
	    XmListDeselectItem(w, list->item);		/* message            */
	    if (!dialog)
	    {
	        dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    	XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			      X_LIST_MESSAGE, strlen(X_LIST_MESSAGE)+1, NULL);
	    }
	    XtManageChild(dialog);
	}
/* determine if an item was selected or deselected                            */
	else
	{
						/*item deselected             */
	    if (list->selected_item_count < configParams.numXVariables)
	    {
						/* last item deselected       */
		if (configParams.items[configParams.numXVariables-1] ==
		    (list->item_position - 1))
		{
		    configParams.numXVariables--;
		    XtDestroyWidget(xForm[configParams.numXVariables]);
		    XtDestroyWidget(xShell[configParams.numXVariables]);
		}
		else			
		{
					/* find item deselected               */
		    for (var = 0; var < configParams.numXVariables-1; var++)
			if (configParams.items[var] ==
			    (list->item_position - 1))
			    break;
					/* transfer last item parameters to   */
					/* the vacant deselected slot         */
		    configParams.numXVariables--;
		    lastVar = configParams.numXVariables;
		    configParams.items[var] =
			configParams.items[configParams.numXVariables];

	    	    strcpy(configParams.xVariables[var].axisLabel,
		           configParams.xVariables[lastVar].axisLabel);
	    	    configParams.xVariables[var].minValue = 
		        configParams.xVariables[lastVar].minValue;
	    	    configParams.xVariables[var].maxValue = 
			configParams.xVariables[lastVar].maxValue;
	    	    configParams.xVariables[var].majorDivisions = 
			configParams.xVariables[lastVar].majorDivisions;
	    	    configParams.xVariables[var].colour =
			configParams.xVariables[lastVar].colour;
	    	    strcpy(configParams.xVariables[var].colourName,
			   configParams.xVariables[lastVar].colourName);

		    strcpy(plotParams->axisLabel[var],
			   plotParams->axisLabel[lastVar]);
		    plotParams->labelLength[var] =
			plotParams->labelLength[lastVar];
		    plotParams->displayData.minValues[var] =
		        plotParams->displayData.minValues[lastVar];
		    plotParams->displayData.maxValues[var] =
		        plotParams->displayData.maxValues[lastVar];
		    plotParams->majorDivisions[var] =
			plotParams->majorDivisions[lastVar];
		    plotParams->numTicks[var] = plotParams->numTicks[lastVar];
		    plotParams->xColours[var] = plotParams->xColours[lastVar];
		    plotParams->displayData.colours[var] =
		        plotParams->displayData.colours[lastVar];
		    makeTickLabel(var, plotParams);
					/* destroy last parameter input shell */
		    XtDestroyWidget(xForm[lastVar]);
		    XtDestroyWidget(xShell[lastVar]);
					/* destroy vacant parameter input form*/
					/* and create a new one               */
		    XtDestroyWidget(xForm[var]);
		    xForm[var] = CreatexForm(xShell[var], var, plotParams);
		    XtManageChild(xForm[var]);
		}
					/* reconfigure the display graphics   */
		initAxesGeom(plotParams);
		rescale = TRUE;
	    }
	    else			/* item selected                      */
	    {
		var = configParams.numXVariables;
		configParams.numXVariables++;
		configParams.items[var] = list->item_position - 1;
					/* set default item parameters        */
	    	strcpy(configParams.xVariables[var].axisLabel,
		       listData.displayItems[configParams.items[var]]); 
	    	configParams.xVariables[var].minValue = 0.0;
	    	configParams.xVariables[var].maxValue = 10.0;
	    	configParams.xVariables[var].majorDivisions = 2.0;
	    	configParams.xVariables[var].colour = WHITE;
	    	strcpy(configParams.xVariables[var].colourName, "white");

		strcpy(plotParams->axisLabel[var],
	    	       configParams.xVariables[var].axisLabel);
		plotParams->labelLength[var] =
		    strlen(configParams.xVariables[var].axisLabel);
		plotParams->displayData.minValues[var] =
		    configParams.xVariables[var].minValue;
		plotParams->displayData.maxValues[var] = 
		    configParams.xVariables[var].maxValue;
		plotParams->majorDivisions[var] = 
		    configParams.xVariables[var].majorDivisions;
		plotParams->numTicks[var] = plotParams->majorDivisions[var] + 1;
		plotParams->xColours[var] = configParams.xVariables[var].colour;
		plotParams->displayData.colours[var] =
		    plotParams->displayData.colours[Y];
		makeTickLabel(var, plotParams);
					/* create a pop-up shell for the item */
					/* parameters                         */
	        argcnt = 0;
		XtSetArg(args[argcnt], XmNallowShellResize, False); argcnt++;
		XtSetArg(args[argcnt], XmNwidth, 500); argcnt++;
		XtSetArg(args[argcnt], XmNheight, 350); argcnt++;
		xShell[var] =
		    XtCreatePopupShell("x parameters", topLevelShellWidgetClass,
		                       appShell, args, argcnt);
		xForm[var] = CreatexForm(xShell[var], var, plotParams);
		XtManageChild(xForm[var]);
	        XtPopup(XtParent(xForm[var]), XtGrabNone);
					/* reconfigure the display graphics   */
		initAxesGeom(plotParams);
		rescale = TRUE;
	    }

	    if (XtIsRealized(plotParams->displayData.display))
	        XClearArea(XtDisplay(plotParams->displayData.display),
		           XtWindow(plotParams->displayData.display), 0, 0, 0,
		 	   0, TRUE);
	}
    }

    else if (yItemListed)
    {
						/* must have one item selected*/
	if (list->selected_item_count == 0)	/* reselect item and display  */
	{					/* warning message            */
	    XmListSelectItem(w, list->item, FALSE);
	    if (!dialog)
	    {
	        dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    	XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			      Y_LIST_MESSAGE, strlen(Y_LIST_MESSAGE)+1, NULL);
	    }
	    XtManageChild(dialog);
	}
	else
	{					/* deselect previous item, its*/
						/* location alternates between*/
						/* list position 0 and 1      */
	    XmListDeselectItem(w, list->selected_items[0]);
	    XmListDeselectItem(w, list->selected_items[1]);
	    XmListSelectItem(w, list->item, FALSE);
	    configParams.items[Y] = list->item_position - 1;
					/* set default item parameters        */
	    strcpy(configParams.yVariable.axisLabel,
		   listData.displayItems[configParams.items[Y]]); 
	    configParams.yVariable.minValue = 0.0;
	    configParams.yVariable.maxValue = 10.0;
	    configParams.yVariable.majorDivisions = 2.0;
	    configParams.yVariable.timeUnits = SECONDS;
	    strcpy(plotParams->axisLabel[Y], configParams.yVariable.axisLabel); 
	    plotParams->labelLength[Y] =
		strlen(configParams.yVariable.axisLabel);
	    plotParams->displayData.minValues[Y] =
		configParams.yVariable.minValue;
	    plotParams->displayData.maxValues[Y] =
		configParams.yVariable.maxValue;
	    plotParams->majorDivisions[Y] =
		configParams.yVariable.majorDivisions;
	    plotParams->numTicks[Y] = configParams.yVariable.majorDivisions - 1;
	    plotParams->numYWords = 1;
	    plotParams->yLabelSpace[0] =
		strlen(configParams.yVariable.axisLabel) + 1;
	    for (word = 1; word < NUM_Y_WORDS; word++)
	        plotParams->yLabelSpace[word] = 0;
	    makeTickLabel(Y, plotParams);
	    largestYLabel = plotParams->yLabelSpace[0];
	    largestYLabel = MAX(largestYLabel,
				plotParams->firstTickLabelLength[Y]);
	    largestYLabel = MAX(largestYLabel,
				plotParams->lastTickLabelLength[Y]);
	    plotParams->displayData.xAxisStart = AXES_OFFSET3 + largestYLabel *
				                 OFFSET_PER_CHAR;
	    plotParams->numMarks = 0;
						/* destroy parameter input    */
						/* form and create a new one  */
	    XtDestroyWidget(yForm);
	    yForm = CreateyForm(yShell, &configParams.yVariable, plotParams);
	    XtManageChild(yForm);
	    XtPopup(XtParent(yForm), XtGrabNone);
					/* reconfigure the display graphics   */
	    if (configParams.items[Y] == SCAN_NUMBER)
	    {
                plotStart = configParams.yVariable.minValue;
        	plotSpan = configParams.yVariable.maxValue -
			   configParams.yVariable.minValue;
                plotMax = configParams.yVariable.minValue + plotSpan;
                plotEnd = pointCount;
                if (plotEnd < plotStart)
            	    plotEnd = plotStart;
                else if (plotEnd > plotMax)
            	    plotEnd = plotMax;
                if (plotEnd < plotMax)
            	    plotScrolling = FALSE;
	    }
	    else if (configParams.items[Y] == TIME)
	    {
            	if (configParams.yVariable.timeUnits == SECONDS)
		{
                    minInSecs = configParams.yVariable.minValue;
                    maxInSecs = configParams.yVariable.maxValue;
		}
            	else if (configParams.yVariable.timeUnits == MINUTES)
		{
                    minInSecs = configParams.yVariable.minValue *
				(Flt64) SECONDS_PER_MINUTE;
                    maxInSecs = configParams.yVariable.maxValue *
				(Flt64) SECONDS_PER_MINUTE;
		}
            	else    /* configParams.yVariable.timeUnits == HOURS */
		{
                    minInSecs = configParams.yVariable.minValue *
				(Flt64) SECONDS_PER_HOUR;
                    maxInSecs = configParams.yVariable.maxValue *
				(Flt64) SECONDS_PER_HOUR;
		}
            	plotSpan = (maxInSecs - minInSecs) * DATA_UPDATE_RATE;
            	if (dataValues[0][TIME] >= minInSecs)
            	{
                    plotStart = 0;
                    plotMax = (maxInSecs - dataValues[0][TIME]) *
                              DATA_UPDATE_RATE;
            	}
            	else
            	{
                    plotStart = (minInSecs - dataValues[0][TIME]) *
                                DATA_UPDATE_RATE;
                    plotMax = plotStart + plotSpan;
            	}
            	if (pointCount <= plotStart)
                    plotEnd = plotStart;
            	else if (pointCount <= plotMax)
                    plotEnd = pointCount;
            	else
                    plotEnd = plotMax;
            	if (plotEnd < plotMax)
                    plotScrolling = FALSE;
	    }
	    else				/* !SCAN_NUMBER and !TIME     */
	    {					/* reset start to min value   */
                plotStart = -1;
                for (count = 0; count <= pointCount; count++)
                    if ( (dataValues[count][configParams.items[Y]] >=
			  configParams.yVariable.minValue) &&
                         (dataValues[count][configParams.items[Y]] <=
			  configParams.yVariable.maxValue) )
                    {
                        plotStart = count;
                        break;
                    }
                                        	/* set end to latest data     */
            	plotEnd = plotStart;        	/* value between min and max  */
            	for (count = pointCount; count >= 0; count--)
                    if ( (dataValues[count][configParams.items[Y]] >=
			  configParams.yVariable.minValue) &&
                         (dataValues[count][configParams.items[Y]] <=
			  configParams.yVariable.maxValue) )
                    {
                    	plotEnd = count;
                    	break;
                    }
            	if ((plotStart == -1) ||    /* min value not found in data    */
                    (plotEnd == plotStart)) /* array, or max value not found  */
            	{
                    plotSpan = 0;
                    plotMax = plotEnd;
            	}
            	else                        /* end value found, max set to end*/
            	{                           /* update span                    */
                    plotMax = plotEnd;
                    plotSpan = plotMax - plotStart;
            	}
	    }
	    initAxesGeom(plotParams);
	    rescale = TRUE;

	    if (XtIsRealized(plotParams->displayData.display))
	        XClearArea(XtDisplay(plotParams->displayData.display),
		           XtWindow(plotParams->displayData.display), 0, 0, 0,
			   0, TRUE);
	}
    }

    else
    {	/* statusItemsListed */
/* check that number of items selected does not exceed maximum                */
	if (list->selected_item_count > NUM_STATUS_VARS)/* deselect item and  */
	{						/* display warning    */
	    XmListDeselectItem(w, list->item);		/* message            */
	    if (!dialog)
	    {
	        dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    	XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
		    STATUS_LIST_MESSAGE, strlen(STATUS_LIST_MESSAGE)+1, NULL);
	    }
	    XtManageChild(dialog);
	}
/* determine if an item was selected or deselected                            */
	else
	{
					/*item deselected                     */
	    if (list->selected_item_count < configParams.numStatusLineVariables)
	    {
					/* last item deselected               */
		if ( configParams.statusLineItems[configParams.numXVariables-1]
		     == (list->item_position - 1))
		    configParams.numStatusLineVariables--;
		else			
		{
					/* find item deselected               */
		    for (var = 0; var < configParams.numStatusLineVariables-1;
			 var++)
			if ( configParams.statusLineItems[var] ==
			     (list->item_position - 1))
			    break;
		    configParams.numStatusLineVariables--;					
					/* move items to replace deselected   */
					/* item                               */
		    for (var2 = var; var2 < configParams.numStatusLineVariables;
			 var2++)
		        configParams.statusLineItems[var2] =
			    configParams.statusLineItems[var2+1];
		}
	    }
	    else			/* item selected                      */
	    {
		var = configParams.numStatusLineVariables;
		configParams.numStatusLineVariables++;
		configParams.statusLineItems[var] = list->item_position - 1;
	    }
   	}
    }

}  /* itemListCallback */


/******************************************************************************/
/* Function : displayRateCallback                                             */
/* Purpose  : Changes the graphics display rate.                              */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
displayRateCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    static Widget dialog = NULL;
    char          value[STRING_LENGTH];
    Flt32         rate;

    rate = atof(XmTextFieldGetString(w));

/* check for out of range values                                              */
    if ( (rate <= EPSILON) || (rate > 1.0) )
    {

/* replace new value with previous value                                      */
	sprintf(value, "%6.4f", configParams.graphicsDisplayRate);
	XmTextReplace(w, 0, STRING_LENGTH, value);

/* display warning message                                                    */
	if (!dialog)
	{
	    dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			  STATUS_RATE_MESSAGE, strlen(STATUS_RATE_MESSAGE)+1,
			  NULL);
	}
	XtManageChild(dialog);
    }
    else					/* set new rate               */
    	configParams.graphicsDisplayRate = rate;

}  /* displayRateCallback */


/******************************************************************************/
/* Function : displayRateLostFoucsCallback                                    */
/* Purpose  : Changes the graphics display rate.                              */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
displayRateLostFocusCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    Flt32         rate;

    rate = atof(XmTextFieldGetString(w));

    printf("lost focus  rate %f\n", rate);

}  /* displayRateLostFocusCallback */


/******************************************************************************/
/* Function : exitCallback                                                    */
/* Purpose  : Exits the CTD data display application.                         */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
exitCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    Int32 point;

    for (point = 0; point < numDataPoints; point++)
	free(dataValues[point]);

    if (configParams.markFilePtr != NULL)
	fclose(configParams.markFilePtr);

    dm_task_exit(0);
    exit(0);

}


/******************************************************************************/
/* Function : linePlotCallback                                                */
/* Purpose  : Sets line drawing mode.                                         */
/* Inputs   : Widget, display data and call data.                             */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
linePlotCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    displayUpdateData            *data = (displayUpdateData *) clientData;
    XmToggleButtonCallbackStruct *toggle =
	(XmToggleButtonCallbackStruct *) callData;
    Display                      *display = XtDisplay(data->display);
    Drawable                     drawable = XtWindow(data->display);

    if (toggle->set)
	configParams.lineDrawing = TRUE;

    if (stopAcquisition)
	XClearArea(display, drawable, 0, 0, 0, 0, TRUE);
    else
        restartDisplay = TRUE;

}  /* linePlotCallback */


/******************************************************************************/
/* Function : markCallback                                                    */
/* Purpose  : Sets mark data values and writes them to a file.                */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
markCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    plotParameters *plotParams =		/* display plotting parameters*/
		   (plotParameters *) clientData;
    Widget         dialog = NULL;		/* dialogue widget            */
    char           dateString[STRING_LENGTH];	/* Julian date                */
    char           markFilePath[2*STRING_LENGTH];/* mark file name and dir    */
    char           markFileMsg[2*STRING_LENGTH];/* mark file message          */
    Int16          var;				/* loop counter               */
    static Flt32   markData[NUM_DISPLAY_ITEMS];	/* mark data values           */
    time_t         currentTime;                 /* current time               */
    struct tm      timeData, *timeDataPtr;      /* time data                  */
    XmString       xmString[2];			/* mark file name choices     */

    plotParams->numMarks++;

/* on first mark, save the data, display the mark, then confirm the mark file */
/* name, open the file, and write the data to the file                        */
    if (configParams.markFilePtr == NULL)
    {
						/* save the mark data values  */
	for (var = 0; var < NUM_DISPLAY_ITEMS; var++)
	    markData[var] = dataValues[pointCount][var];
						/* display the mark           */
	plotParams->markValue[plotParams->numMarks-1] =
	    dataValues[pointCount][configParams.items[Y]];
	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0, 0,
		       TRUE);
						/* create dated mark file name*/
    	currentTime = time(NULL);
    	timeDataPtr = localtime(&currentTime);
    	timeData = *timeDataPtr;
	if (timeData.tm_yday < 99)		/* only 2 digits in the day   */
    	    sprintf(dateString, "%2d%1d%2d", timeData.tm_year, 0,
		    timeData.tm_yday+1);
	else
    	    sprintf(dateString, "%2d%3d", timeData.tm_year, timeData.tm_yday+1);
    	strcat(dateString, ".mrk");
						/* confirm mark file name     */
						/* and write mark data        */
    	if (strcmp(configParams.markFilename, dateString))
    	{
	    xmString[0] = XmStringCreateLtoR(configParams.markFilename,
					     XmFONTLIST_DEFAULT_TAG);
	    xmString[1] = XmStringCreateLtoR(dateString,
					     XmFONTLIST_DEFAULT_TAG);
	    if (!dialog)
	    {
	        dialog = XmCreateSelectionDialog(w, "dialog", NULL, 0);
	        XtVaSetValues(dialog, XtVaTypedArg, XmNlistLabelString,
			      XmRString, MARK_MESSAGE1, strlen(MARK_MESSAGE1)+1,
			      XmNlistItems, xmString, XmNlistItemCount, 2,
			      NULL);
    	    	XtAddCallback(dialog, XmNapplyCallback, markFilenameCallback,
			      (XtPointer) markData);
    	    	XtAddCallback(dialog, XmNokCallback, markFilenameCallback,
			      (XtPointer) markData);
	    }
	    XtManageChild(dialog);

	    XmStringFree(xmString[0]);
	    XmStringFree(xmString[1]);
    	}
	else
	{
	    strcpy(markFilePath, MARK_FILE_DIR);
	    strcat(markFilePath, configParams.markFilename);
    	    if ( (configParams.markFilePtr = fopen(markFilePath, "w")) == NULL)
    	    {
        	sprintf(markFileMsg, "Could not open file %s\n", markFilePath);
		if (!dialog)
		{
	    	    dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    	    XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString,
				  XmRString, markFileMsg, strlen(markFileMsg)+1,
				  NULL);
		}
		XtManageChild(dialog);
    	    }
    	    writeMarkData(w, markData);
	}

    }

/* else display the mark and write the data to the file                       */
    else
    {
    	if (plotParams->numMarks > NUM_MARKS) 	/* display warning message    */
    	{
	    if (!dialog)
	    {
	        dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	        XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			      MARK_MESSAGE2, strlen(MARK_MESSAGE2)+1, NULL);
	    }
	    XtManageChild(dialog);
    	}
    	else
    	{
	    plotParams->markValue[plotParams->numMarks-1] =
	        dataValues[pointCount][configParams.items[Y]];
	    if (XtIsRealized(plotParams->displayData.display))
	        XClearArea(XtDisplay(plotParams->displayData.display),
		           XtWindow(plotParams->displayData.display), 0, 0, 0,
			   0, TRUE);
    	}

	writeMarkData(w, dataValues[pointCount]);
    }

}  /* markCallback */


/******************************************************************************/
/* Function : markFilenameCallback                                            */
/* Purpose  : Gets the mark file name, opens the file and writes the data.    */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
markFilenameCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    XmSelectionBoxCallbackStruct *markSelect = 	
	(XmSelectionBoxCallbackStruct *) callData;
    Widget                       dialog = NULL;	/* dialogue widget            */
    Flt32                        *markData =	/* mark data values           */
	(Flt32 *) clientData;
    char                         *markFilename;	/* mark file name             */
						/* mark file name and dir    */
    char                         markFilePath[2*STRING_LENGTH];
    						/* mark file name message     */
    char                         markFileMsg[2*STRING_LENGTH];
    Int16 var;

    XmStringGetLtoR(markSelect->value, XmFONTLIST_DEFAULT_TAG, &markFilename);
    strcpy(configParams.markFilename, markFilename);

    strcpy(markFilePath, MARK_FILE_DIR);
    strcat(markFilePath, configParams.markFilename);
    if ( (configParams.markFilePtr = fopen(markFilePath, "w"))
	  == NULL)
    {
        sprintf(markFileMsg, "Could not open file %s\n", markFilePath);
	if (!dialog)
	{
	    dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			  markFileMsg, strlen(markFileMsg)+1, NULL);
	}
	XtManageChild(dialog);
    }
    else
    	writeMarkData(w, markData);

    XtUnmanageChild(w);

}  /* markFilenameCallback */


/******************************************************************************/
/* Function : writeMarkData                                                   */
/* Purpose  : Opens the mark data file and writes the data.                   */
/* Inputs   : Mark data values.                                               */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
writeMarkData(Widget w, Flt32 *markData)
{
    Widget dialog = NULL;	/* dialogue widget            */

    if (configParams.markFilePtr != NULL)
    {
	if (!dialog)			/* prompt for a comment       */
	{
	    dialog = XmCreatePromptDialog(w, "dialog", NULL, 0);
	    XtVaSetValues(dialog, XtVaTypedArg, XmNselectionLabelString,
			  XmRString, MARK_MESSAGE3, strlen(MARK_MESSAGE3)+1,
			  NULL);
    	    XtAddCallback(dialog, XmNokCallback, markOkCallback, NULL);
    	    XtAddCallback(dialog, XmNcancelCallback, markCancelCallback, NULL);
	}
	XtManageChild(dialog);

	fprintf(configParams.markFilePtr, "Beam Attenuation    %f\n",
		markData[BEAM_ATTENUATION]);
	fprintf(configParams.markFilePtr, "Beam Percent        %f\n",
		markData[BEAM_PERCENT]);
	fprintf(configParams.markFilePtr, "Conductivity        %f\n",
		markData[CONDUCTIVITY]);
	fprintf(configParams.markFilePtr, "Depth               %f\n",
		markData[DEPTH]);
	fprintf(configParams.markFilePtr, "Oxygen              %f\n",
		markData[OXYGEN]);
	fprintf(configParams.markFilePtr, "Oxygen Current      %f\n",
		markData[OXYGEN_CURRENT]);
	fprintf(configParams.markFilePtr, "Oxygen Temperature  %f\n",
		markData[OXYGEN_TEMP]);
	fprintf(configParams.markFilePtr, "Pressure            %f\n",
		markData[PRESSURE]);
	fprintf(configParams.markFilePtr, "Salinity            %f\n",
		markData[SALINITY]);
	fprintf(configParams.markFilePtr, "Scan Number         %f\n",
		markData[SCAN_NUMBER]);
	fprintf(configParams.markFilePtr, "Sigma T             %f\n",
		markData[SIGMA_T]);
	fprintf(configParams.markFilePtr, "Sigma Theta         %f\n",
		markData[SIGMA_THETA]);
	fprintf(configParams.markFilePtr, "Sound Velocity      %f\n",
		markData[SOUND_VELOCITY]);
	fprintf(configParams.markFilePtr, "Temperature         %f\n",
		markData[TEMPERATURE]);
	fprintf(configParams.markFilePtr, "Theta               %f\n",
		markData[THETA]);
	fprintf(configParams.markFilePtr, "Time                %f\n",
		markData[TIME]);
	fprintf(configParams.markFilePtr, "Transmission        %f\n",
		markData[TRANSMISSION]);
    }

}  /* writeMarkData */


/******************************************************************************/
/* Function : markOkCallback                                                  */
/* Purpose  : Writes a comment to the mark data file.                         */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
markOkCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    XmSelectionBoxCallbackStruct *markSelect = 
	(XmSelectionBoxCallbackStruct *) callData;
    char   *markComment;			/* mark comment               */

    XmStringGetLtoR(markSelect->value, XmFONTLIST_DEFAULT_TAG, &markComment);

    if (configParams.markFilePtr != NULL)
    	fprintf(configParams.markFilePtr, "%s\n\n", markComment);

    XtUnmanageChild(w);

}  /* markOkCallback */


/******************************************************************************/
/* Function : markCancelCallback                                              */
/* Purpose  : Removes the mark file comment dialog from the screen.           */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
markCancelCallback(Widget w, XtPointer clientData, XtPointer callData)
{

    XtUnmanageChild(w);

}  /* markCancelCallback */


/******************************************************************************/
/* Function : parametersCallback                                              */
/* Purpose  : Pops up the x or y items parameter configuration forms.         */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
parametersCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    Int16 var;

    if (xItemsListed)
        for (var = 0; var < configParams.numXVariables; var++)
	    XtPopup(XtParent(xForm[var]), XtGrabNone);
    else if (yItemListed)
	XtPopup(XtParent(yForm), XtGrabNone);

}  /* parametersCallback */


/******************************************************************************/
/* Function : scrollCallback                                                  */
/* Purpose  : Sets the scrolling graphics display mode.                       */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
scrollCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    XmToggleButtonCallbackStruct *toggle =
	(XmToggleButtonCallbackStruct *) callData;

    if (toggle->set)
    	configParams.scrolling = TRUE;
    else
    {
    	configParams.scrolling = FALSE;
	plotScrolling = FALSE;
    }

}  /* scrollCallback */


/******************************************************************************/
/* Function : showXYCallback                                                  */
/* Purpose  : Sets the show xy coordinates graphics display mode.             */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
showXYCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    XmToggleButtonCallbackStruct *toggle =
	(XmToggleButtonCallbackStruct *) callData;
    plotParameters               *plotParams = (plotParameters *) clientData;
    Int16                        var;			/* loop counter       */

    if (toggle->set)
    {
	XtAddEventHandler(plotParams->displayData.display, ButtonPressMask,
			  FALSE, showXYPosition, (XtPointer) plotParams);
	XtAddEventHandler(plotParams->displayData.display, ButtonMotionMask,
			  FALSE, trackXYPosition, (XtPointer) plotParams);
	XtAddEventHandler(plotParams->displayData.display,
			  ButtonReleaseMask | LeaveWindowMask, FALSE,
			  clearXYPosition, (XtPointer) plotParams);
    	configParams.showXY = TRUE;
    }
    else
    {
	XtRemoveEventHandler(plotParams->displayData.display, ButtonPressMask,
			     FALSE, showXYPosition, (XtPointer) plotParams);
	XtRemoveEventHandler(plotParams->displayData.display, ButtonMotionMask,
			     FALSE, trackXYPosition, (XtPointer) plotParams);
	XtRemoveEventHandler(plotParams->displayData.display,
			     ButtonReleaseMask | LeaveWindowMask, FALSE,
			     clearXYPosition, (XtPointer) plotParams);
    	configParams.showXY = FALSE;
    	for (var = 0; var < configParams.numXVariables; var++)
	    strcpy(plotParams->coordString[var], "");

	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0,
		       plotParams->displayData.yStatusLinePos - AXES_OFFSET4,
		       TRUE);
    }

}  /* showXYCallback */


/******************************************************************************/
/* Function : showXYPosition                                                  */
/* Purpose  : Displays the xy coordinates when a mouse button is pressed.     */
/* Inputs   : Widget, plotting data, event and flag.                          */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
showXYPosition(Widget w, XtPointer clientData, XEvent *event, Boolean *flag)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    Int16 var;					/* loop counter               */
    Flt32 x[NUM_X_VARS], y;			/* xy coordinates             */

						/* determine if mouse is      */
						/* within plot axes           */
    if ((event->xbutton.x > plotParams->displayData.xAxisStart) &&
	(event->xbutton.x < plotParams->displayData.xAxisEnd))
    {
	if ((event->xbutton.y > plotParams->displayData.yAxisStart) &&
	    (event->xbutton.y < plotParams->displayData.yAxisEnd))
	{
	    plotParams->inDisplayWindow = TRUE;

    	    plotParams->xCoordPosition = event->xbutton.x;
    	    for (var = 0; var < configParams.numXVariables; var++)
	    {
    		plotParams->yCoordPositions[var] = event->xbutton.y +
					           var * COORD_CHAR_OFFSET;

	 	x[var] = plotParams->displayData.minValues[var] +
		    (event->xbutton.x - plotParams->displayData.xAxisStart) /
		     plotParams->displayData.scales[var]; 
	    }
	    if (configParams.items[Y] == TIME)
	    	y = ( (event->xbutton.y - plotParams->displayData.yAxisStart) /
		      plotParams->displayData.scales[Y] +
		      plotParams->displayData.minValues[Y] ) / yTimeUnitsScale;
	    else
	    	y = (event->xbutton.y - plotParams->displayData.yAxisStart) /
		    plotParams->displayData.scales[Y] +
		    plotParams->displayData.minValues[Y];

	    strcpy(plotParams->coordString[X1], "");
	    if (configParams.items[Y] == SCAN_NUMBER)
    	        sprintf(plotParams->coordString[X1], "    %6.2f  %d", x[X1],
			(Int16) y);
	    else
    	        sprintf(plotParams->coordString[X1], "    %6.2f  %6.2f", x[X1],
			y);
    	    for (var = 1; var < configParams.numXVariables; var++)
	    {
	        strcpy(plotParams->coordString[var], "");
    	    	sprintf(plotParams->coordString[var], "    %6.2f", x[var]);
	    }

	    if (XtIsRealized(plotParams->displayData.display))
	    	XClearArea(XtDisplay(plotParams->displayData.display),
		           XtWindow(plotParams->displayData.display), 0, 0, 0,
		           plotParams->displayData.yStatusLinePos -
			   AXES_OFFSET4, TRUE);

	}
	else
	    plotParams->inDisplayWindow = FALSE;
    }
    else 
	plotParams->inDisplayWindow = FALSE;

}  /* showXYPosition */


/******************************************************************************/
/* Function : trackXYPosition                                                 */
/* Purpose  : Displays the xy coordinates when the mouse is moved.            */
/* Inputs   : Widget, plotting data, event and flag.                          */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
trackXYPosition(Widget w, XtPointer clientData, XEvent *event, Boolean *flag)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    Int16 var;					/* loop counter               */
    Flt32 x[NUM_X_VARS], y;			/* xy coordinates             */

						/* determine if mouse is      */
						/* within plot axes           */
    if ((event->xmotion.x > plotParams->displayData.xAxisStart) &&
	(event->xmotion.x < plotParams->displayData.xAxisEnd))
    {
	if ((event->xmotion.y > plotParams->displayData.yAxisStart) &&
	    (event->xmotion.y < plotParams->displayData.yAxisEnd))
	{
	    plotParams->inDisplayWindow = TRUE;

    	    plotParams->xCoordPosition = event->xmotion.x;
    	    for (var = 0; var < configParams.numXVariables; var++)
	    {
    		plotParams->yCoordPositions[var] = event->xmotion.y +
					           var * COORD_CHAR_OFFSET;

	 	x[var] = plotParams->displayData.minValues[var] +
		    (event->xmotion.x - plotParams->displayData.xAxisStart) /
		     plotParams->displayData.scales[var]; 
	    }
	    if (configParams.items[Y] == TIME)
	    	y = ( (event->xmotion.y - plotParams->displayData.yAxisStart) /
		      plotParams->displayData.scales[Y] +
		      plotParams->displayData.minValues[Y] ) / yTimeUnitsScale;
	    else
	    	y = (event->xmotion.y - plotParams->displayData.yAxisStart) /
		    plotParams->displayData.scales[Y] +
		    plotParams->displayData.minValues[Y];

	    strcpy(plotParams->coordString[X1], "");
	    if (configParams.items[Y] == SCAN_NUMBER)
    	        sprintf(plotParams->coordString[X1], "    %6.2f  %d", x[X1],
			(Int16) y);
	    else
    	        sprintf(plotParams->coordString[X1], "    %6.2f  %6.2f", x[X1],
			y);
    	    for (var = 1; var < configParams.numXVariables; var++)
	    {
	        strcpy(plotParams->coordString[var], "");
    	    	sprintf(plotParams->coordString[var], "    %6.2f", x[var]);
	    }

	}
	else
	{
	    plotParams->inDisplayWindow = FALSE;
    	    for (var = 0; var < configParams.numXVariables; var++)
	        strcpy(plotParams->coordString[var], "");
	}
    }
    else 
    {
	plotParams->inDisplayWindow = FALSE;
    	for (var = 0; var < configParams.numXVariables; var++)
	    strcpy(plotParams->coordString[var], "");
    }

    if (XtIsRealized(plotParams->displayData.display))
    	XClearArea(XtDisplay(plotParams->displayData.display),
		   XtWindow(plotParams->displayData.display), 0, 0, 0,
		   plotParams->displayData.yStatusLinePos - AXES_OFFSET4, TRUE);

}  /* trackXYPosition */


/******************************************************************************/
/* Function : clearXYPosition                                                 */
/* Purpose  : Clears the xy coordinates from the graphics display.            */
/* Inputs   : Widget, plotting data, event and flag.                          */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
clearXYPosition(Widget w, XtPointer clientData, XEvent *event, Boolean *flag)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    Int16          var;					/* loop counter       */

    plotParams->inDisplayWindow = FALSE;
    for (var = 0; var < configParams.numXVariables; var++)
        strcpy(plotParams->coordString[var], "");

    if (XtIsRealized(plotParams->displayData.display))
    	XClearArea(XtDisplay(plotParams->displayData.display),
		   XtWindow(plotParams->displayData.display), 0, 0, 0,
		   plotParams->displayData.yStatusLinePos - AXES_OFFSET4, TRUE);

}  /* clearXYPosition */


/******************************************************************************/
/* Function : startCallback                                                   */
/* Purpose  : Starts the graphics for the data display.                       */
/* Inputs   : Widget, display data and and call data.                         */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
startCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    plotParameters     *plotParams = (plotParameters *) clientData;
    displayUpdateData  *data = &plotParams->displayData;
    Display            *display = XtDisplay(plotParams->displayData.display);
    Drawable           drawable = XtWindow(plotParams->displayData.display);
    Arg		       args[256];
    Cardinal           argcnt;
    static MBool       first = TRUE;
    char               value[STRING_LENGTH];
    Int16              var, item;			/* loop counters      */
    Int32              count;
    XmString           xmString[NUM_DISPLAY_ITEMS];

    if ((first) && (!autoStart))
    {
        updateDisplay((XtPointer) plotParams, NULL);
        updateStatusLine((XtPointer) data, NULL);
	restartDisplay = FALSE;
	resetYTickLabels = FALSE;
    	rescale = FALSE;
    	if (configParams.showXY == TRUE)
    	{
            XtAddEventHandler(plotParams->displayData.display, ButtonPressMask,
                              FALSE, showXYPosition, (XtPointer) plotParams);
            XtAddEventHandler(plotParams->displayData.display, ButtonMotionMask,
                              FALSE, trackXYPosition, (XtPointer) plotParams);
            XtAddEventHandler(plotParams->displayData.display,
                              ButtonReleaseMask | LeaveWindowMask, FALSE,
                              clearXYPosition, (XtPointer) plotParams);
    	}
    }
    else
    {
					/* destroy the x and y shell widgets  */
	for (var = 0; var < configParams.numXVariables; var++)
	{
	    XtDestroyWidget(xForm[var]);
	    XtDestroyWidget(xShell[var]);
	}
	XtDestroyWidget(yForm);
					/* deselect x item list               */
    	for (item = 0; item < NUM_DISPLAY_ITEMS; item++)
            xmString[item] = XmStringCreateLtoR(listData.displayItems[item],
					        XmFONTLIST_DEFAULT_TAG);
	if (xItemsListed)
	{
	    for (var = 0; var < configParams.numXVariables; var++)
		XmListDeselectItem(listData.w,
				   xmString[configParams.items[var]]);
	}
					/* read the original config parameters*/
	readInParams(configParams.configFilename, &plotParams->displayData);
					/* reset the item list                */
	if (xItemsListed)
	{
	    for (var = 0; var < configParams.numXVariables; var++)
		XmListSelectItem(listData.w, xmString[configParams.items[var]],
				 FALSE);
	}
	else if (yItemListed)
	{
    	    XmListDeselectItem(listData.w, xmString[configParams.items[Y]]);
	    XtVaSetValues(yItemToggleButton, XmNset, FALSE, NULL);
	    yItemListed = FALSE;

	    for (var = 0; var < configParams.numXVariables; var++)
		XmListSelectItem(listData.w, xmString[configParams.items[var]],
			         FALSE);
	    XtVaSetValues(xItemsToggleButton, XmNset, TRUE, NULL);
	    xItemsListed = TRUE;
	}
	else if (statusItemsListed)
	{
	    for (var = 0; var < configParams.numStatusLineVariables; var++)
		XmListDeselectItem(listData.w,
				   xmString[configParams.statusLineItems[var]]);
	    XtVaSetValues(statusItemToggleButton, XmNset, FALSE, NULL);
	    statusItemsListed = FALSE;

	    for (var = 0; var < configParams.numXVariables; var++)
		XmListSelectItem(listData.w, xmString[configParams.items[var]],
			         FALSE);
	    XtVaSetValues(xItemsToggleButton, XmNset, TRUE, NULL);
	    xItemsListed = TRUE;
	}
	for (item = 0; item < NUM_DISPLAY_ITEMS; item++)
	    XmStringFree(xmString[item]);
					/* set the display rates              */
    	sprintf(value, "%4.2f", configParams.graphicsDisplayRate);
    	XmTextSetString(displayRateTextField, value);
    	sprintf(value, "%4.2f", configParams.statusLineRate);
    	XmTextSetString(statusRateTextField, value);
					/* set the plot style toggle buttons  */
    	if (configParams.lineDrawing)
    	{
            XtVaSetValues(linesPlotToggleButton, XmNset, TRUE, NULL);
            XtVaSetValues(dotsPlotToggleButton, XmNset, FALSE, NULL);
    	}
    	else
    	{
            XtVaSetValues(linesPlotToggleButton, XmNset, FALSE, NULL);
            XtVaSetValues(dotsPlotToggleButton, XmNset, TRUE, NULL);
    	}
					/* set the show xy coordinates toggle */
					/* buttons                            */
    	if (configParams.showXY)
            XtVaSetValues(showToggleButton, XmNset, TRUE, NULL);
    	else
            XtVaSetValues(showToggleButton, XmNset, FALSE, NULL);
					/* set the scroll toggle buttons      */
    	if (configParams.scrolling)
            XtVaSetValues(scrollToggleButton, XmNset, TRUE, NULL);
    	else
            XtVaSetValues(scrollToggleButton, XmNset, FALSE, NULL);

	initGraphics(canvas, plotParams);
					/* create a new shell and form for the*/
					/* x and y items parameters           */
	for (var = 0; var < configParams.numXVariables; var++)
	{
	    argcnt = 0;
	    XtSetArg(args[argcnt], XmNallowShellResize, False); argcnt++;
	    XtSetArg(args[argcnt], XmNwidth, 500); argcnt++;
	    XtSetArg(args[argcnt], XmNheight, 350); argcnt++;
	    xShell[var] =
		XtCreatePopupShell("x parameters", topLevelShellWidgetClass,
	                           appShell, args, argcnt);
	    xForm[var] = CreatexForm(xShell[var], var, plotParams);
	    XtManageChild(xForm[var]);
	}
	yForm = CreateyForm(yShell, &configParams.yVariable, plotParams);
	XtManageChild(yForm);

	plotParams->numMarks = 0;
	restartDisplay = TRUE;
	resetYTickLabels = TRUE;
	plotScrolling = FALSE;
	rescale = TRUE;
	if (stopAcquisition)
	{
            updateDisplay((XtPointer) plotParams, NULL);
            updateStatusLine((XtPointer) data, NULL);
	}

/* reset plot start and end to original max and min values                   */
    	if (configParams.items[Y] == SCAN_NUMBER)
    	{
    	    plotStart = configParams.yVariable.minValue;
	    if (plotMax <= pointCount)
	    	plotEnd = plotMax;
	    else
	    	plotEnd = pointCount;
    	}

    	else if (configParams.items[Y] == TIME)
    	{
	    if (dataValues[0][TIME] >= minInSecs) /* min value at array start */
		plotStart = 0;
            else
                plotStart = (minInSecs - dataValues[0][TIME]) *
                             DATA_UPDATE_RATE;
            if (pointCount <= plotStart)
                plotEnd = plotStart;
            else if (pointCount <= plotMax)
                plotEnd = pointCount;
            else
                plotEnd = plotMax;
					/* reset to original scale            */
	    data->scales[Y] = data->scales[Y] *
			      (data->maxValues[Y] - data->minValues[Y]) /
		              (configParams.yVariable.maxValue -
		               configParams.yVariable.minValue);
    	}

	else				/* !SCAN_NUMBER and !TIME             */
	{				/* set start to first data value      */
	    plotStart = -1;		/* between min and max                */
	    for (count = 0; count <= pointCount; count++)
	        if ( (dataValues[count][configParams.items[Y]] >=
		      configParams.yVariable.minValue) &&
	             (dataValues[count][configParams.items[Y]] <=
		      configParams.yVariable.maxValue) )
	        {
		    plotStart = count;
		    break;
	        }
	    if (plotStart == -1)	/* min value not found in data array  */
   	    {
	        plotEnd = plotStart;
		plotSpan = 0;
	        plotMax = plotEnd;
	    }
	    else	
	    {
					/* set end to latest data value       */
	        plotEnd = plotStart;	/* between min and max                */
	        for (count = pointCount; count >= 0; count--)
	            if ( (dataValues[count][configParams.items[Y]] >=
			  configParams.yVariable.minValue) &&
	                 (dataValues[count][configParams.items[Y]] <=
			  configParams.yVariable.maxValue) )
	            {
		        plotEnd = count;
		        break;
	            }
		if (plotEnd == plotStart)	/* max value not found        */
   	        {
		    plotSpan = 0;
	            plotMax = plotEnd;
	        }
	        else			/* end value found, max set to end,   */
	        {			/* update span                        */
		    plotMax = plotEnd;
	            plotSpan = plotMax - plotStart;
	        }
	    }
					/* reset to original scale            */
	    data->scales[Y] = data->scales[Y] *
		              (data->maxValues[Y] - data->minValues[Y]) /
	                      (configParams.yVariable.maxValue -
		               configParams.yVariable.minValue);
	}			
    }

    XClearArea(display, drawable, 0, 0, 0, 0, TRUE);

    startDisplay = TRUE;
    freezeDisplay = FALSE;
    stopAcquisition = FALSE;
    first = FALSE;

}  /* startCallback */


/******************************************************************************/
/* Function : stopCallback                                                    */
/* Purpose  : Stops the graphics for the data display.                        */
/* Inputs   : Widget, client data and interval id.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
stopCallback(Widget w, XtPointer clientData, XtPointer callData)
{

    XtRemoveTimeOut(displayTimeoutId);
    XtRemoveTimeOut(statusLineTimeoutId);
/*    startDisplay = FALSE; */
    stopAcquisition = TRUE;

}  /* stopCallback */


/******************************************************************************/
/* Function : updateDisplay                                                   */
/* Purpose  : Updates the data plot on the display.                           */
/* Inputs   : Display data and interval id.                                   */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
updateDisplay(XtPointer clientData, XtIntervalId *id)
{
    plotParameters    *plotParams = 		/* display axes parameters    */
		      (plotParameters *) clientData;
    displayUpdateData *data = 			/* display update data        */
		      &plotParams->displayData;
    Display           *display = XtDisplay(data->display);
    Drawable          drawable = XtWindow(data->display);
    Widget            dialog[NUM_VARS] = 	/* dialogue widget            */
		      { NULL, NULL, NULL, NULL, NULL };
    static MBool      badValue[NUM_VARS] = 	/* bad data value flag        */
		      { FALSE, FALSE, FALSE, FALSE, FALSE };
    static MBool      badWarningUp[NUM_VARS] =	/* bad warning popped-up flag */
		      { FALSE, FALSE, FALSE, FALSE, FALSE };
    static Int16      lastX[NUM_X_VARS], lastY;	/* last x and y values        */
    static Flt32      prevScaleX, prevScaleY;	/* previous scale             */
    char              badMessage[STRING_LENGTH]	/* bad data value message     */
		      = "";
    Int16             var;			/* variable counter           */
    Int16             x[NUM_X_VARS], y;		/* scaled data values         */
    Int32             count;			/* point count                */
    Int32             savedPlotStart;		/* saved plot start           */
    Nat32             timeout = 1000.0 / configParams.graphicsDisplayRate;
    Flt32             minValueY;		/* min y value in scrolling   */

/* restart timeout                                                            */
    displayTimeoutId =
	XtAppAddTimeOut(XtWidgetToApplicationContext(data->display), timeout,
			updateDisplay, (XtPointer) plotParams);

/* check for bad x data values and scale the data                             */
    for (var = 0; var < configParams.numXVariables; var++)
    {
	if (fabs(dataValues[pointCount][configParams.items[var]] -
		 BAD_DATA_VALUE) <= EPSILON)
	{
	    x[var] = NO_PLOT_VALUE;
	    if (!badValue[var])
	        badValue[var] = TRUE;
	}
	else
	{
	    if (dataValues[pointCount][configParams.items[var]] <=
	        data->minValues[var])
	        x[var] = data->xAxisStart;
	    else
	    {
	        if (dataValues[pointCount][configParams.items[var]] >=
		    data->maxValues[var])
                    x[var] = data->xAxisStart + data->scales[var] *
                             (data->maxValues[var] - data->minValues[var]);
	        else
                    x[var] = data->xAxisStart + data->scales[var] *
                             (dataValues[pointCount][configParams.items[var]] -
		              data->minValues[var]);
	    }	
	    if (badValue[var])
	    {
	        badValue[var] = FALSE;
		badWarningUp[var] = FALSE;
	    }
	}
    	if (badValue[var] && (!badWarningUp[var]))/* popup warning message    */
    	{
	    sprintf(badMessage, "Bad %s data values",
		    listData.displayItems[configParams.items[var]]);
	    dialog[var] = XmCreateWarningDialog(data->display, "dialog", NULL,
						0);
	    XtVaSetValues(dialog[var], XtVaTypedArg, XmNmessageString,
			  XmRString, badMessage, strlen(badMessage)+1, NULL);
	    XtManageChild(dialog[var]);
	    badWarningUp[var] = TRUE;
    	}
    }

/* check for bad y data values and scale the data                             */
    if (fabs(dataValues[pointCount][configParams.items[Y]] - BAD_DATA_VALUE)
	<= EPSILON)
    {
    	y = NO_PLOT_VALUE;
	if (!badValue[Y])
	    badValue[Y] = TRUE;
    }
    else
    {
        if (configParams.items[Y] == TIME)
        {
    	    if (dataValues[pointCount][configParams.items[Y]] <=
	        data->minValues[Y] * yTimeUnitsScale)
    	        y = data->yAxisStart;
    	    else
	    {
    	        if (dataValues[pointCount][configParams.items[Y]] >=
	            data->maxValues[Y] * yTimeUnitsScale)
    	            y = data->yAxisStart + data->scales[Y] * yTimeUnitsScale *
	                (data->maxValues[Y] - data->minValues[Y]);
	        else
    	            y = data->yAxisStart + data->scales[Y] *
	                (dataValues[pointCount][configParams.items[Y]] -
	                 data->minValues[Y] * yTimeUnitsScale);
	    }
        }
        else
        {
    	    if (dataValues[pointCount][configParams.items[Y]] <=
		data->minValues[Y])
    	        y = data->yAxisStart;
    	    else
	    {
    	        if (dataValues[pointCount][configParams.items[Y]] >=
		    data->maxValues[Y])
    	            y = data->yAxisStart + data->scales[Y] *
	                (data->maxValues[Y] - data->minValues[Y]);
	        else
    	            y = data->yAxisStart + data->scales[Y] *
	                (dataValues[pointCount][configParams.items[Y]] -
	                 data->minValues[Y]);
	    }
        }
	if (badValue[Y])
	{
	    badValue[Y] = FALSE;
	    badWarningUp[Y] = FALSE;
	}
    }
    if (badValue[Y] && (!badWarningUp[Y]))	/* popup warning message      */
    {
	sprintf(badMessage, "Bad %s data values",
		listData.displayItems[configParams.items[Y]]);
	dialog[Y] = XmCreateWarningDialog(data->display, "dialog", NULL, 0);
	XtVaSetValues(dialog[Y], XtVaTypedArg, XmNmessageString, XmRString,
		      badMessage, strlen(badMessage)+1, NULL);
	XtManageChild(dialog[Y]);
	badWarningUp[Y] = TRUE;
    }

/* set initial values at start of data display                                */
    if ( (pointCount == 0) && (loopNumber == 1) )
    {
        for (var = 0; var < configParams.numXVariables; var++)
            lastX[var] = x[var];
        lastY = y;
        prevScaleX = data->scales[X1];
        prevScaleY = data->scales[Y];
    }

/* reset values on window resize or axes rescale                              */
    if ( (fabs(prevScaleX - data->scales[X1]) > EPSILON) ||
	 (fabs(prevScaleY - data->scales[Y]) > EPSILON) || rescale )
    {
	if ( (pointCount == 0) && (loopNumber == 1) )
    	{
            for (var = 0; var < configParams.numXVariables; var++)
                lastX[var] = x[var];
            lastY = y;
    	}
	else
	{
	    if (pointCount == 0)
		count = numDataPoints-1;
	    else
		count = pointCount-1;
            for (var = 0; var < configParams.numXVariables; var++)
	    {
		if (fabs(dataValues[pointCount][configParams.items[var]] -
		 	 BAD_DATA_VALUE) <= EPSILON)
	    	    lastX[var] = NO_PLOT_VALUE;
		else
		{
		    if (dataValues[pointCount][configParams.items[var]] <=
	    	        data->minValues[var])
	    	        lastX[var] = data->xAxisStart;
		    else
		    {
	    	    if (dataValues[pointCount][configParams.items[var]] >=
		        data->maxValues[var])
                        lastX[var] = data->xAxisStart + data->scales[var] *
                                 (data->maxValues[var] - data->minValues[var]);
	    	    else
                        lastX[var] = data->xAxisStart + data->scales[var] *
                                 (dataValues[count][configParams.items[var]] -
			          data->minValues[var]);
		    }
	        }
	    }
    	    if (fabs(dataValues[pointCount][configParams.items[Y]] -
		     BAD_DATA_VALUE) <= EPSILON)
    		lastY = NO_PLOT_VALUE;
    	    else
    	    {
	        if (configParams.items[Y] == TIME)
	        {
    		    if (dataValues[pointCount][configParams.items[Y]] <=
	    	        data->minValues[Y] * yTimeUnitsScale)
    	    	        lastY = data->yAxisStart;
    		    else
		    {
    	    	        if (dataValues[pointCount][configParams.items[Y]] >=
	                    data->maxValues[Y] * yTimeUnitsScale)
    	        	    lastY = data->yAxisStart +
				    data->scales[Y] * yTimeUnitsScale *
				    (data->maxValues[Y] - data->minValues[Y]);
	                else
            	            lastY = data->yAxisStart + data->scales[Y] *
	                            (dataValues[count][configParams.items[Y]] -
			             data->minValues[Y] * yTimeUnitsScale);
		    }
	        }
	        else
	        {
    		    if (dataValues[pointCount][configParams.items[Y]] <=
	    	        data->minValues[Y])
    	    	        lastY = data->yAxisStart;
    		    else
		    {
    	    	        if (dataValues[pointCount][configParams.items[Y]] >=
	                    data->maxValues[Y])
    	        	    lastY = data->yAxisStart + data->scales[Y] *
				    (data->maxValues[Y] - data->minValues[Y]);
	                else
            		    lastY = data->yAxisStart + data->scales[Y] *
	                	    (dataValues[count][configParams.items[Y]] -
			 	    data->minValues[Y]);
		    }
	        }
	    }
	}
        prevScaleX = data->scales[X1];
        prevScaleY = data->scales[Y];
	rescale = FALSE;
    }

    if (restartDisplay)				 /* clear and redraw display  */
    {
	XClearArea(display, drawable, 0, 0, 0, 0, TRUE);
	restartDisplay = FALSE;
    }

    if ( (!freezeDisplay) && (plotStart >= 0) )	/* update display             */
    {					
	if (!configParams.scrolling)		/* scrolling mode not selected*/
	{
	    if ( (pointCount > plotStart) && (pointCount <= plotMax) )
	    {
		if (configParams.graphicsDisplayRate < 1.0)
		{
		    savedPlotStart = plotStart;
		    plotStart = pointCount -
				(1.0 / configParams.graphicsDisplayRate);
		    plotDataPoints(data, display, drawable, ERASE_OFF);
		    plotStart = savedPlotStart;
		}
		else
		{
    	    	    for (var = 0; var < configParams.numXVariables; var++)
    	    	    {
		        if ( (x[var] != NO_PLOT_VALUE) && (y != NO_PLOT_VALUE) )
		        {
                            XSetForeground(display, gc, data->colours[var]);
	                    if (configParams.lineDrawing)
			    {
		    	        if ( (lastX[var] != NO_PLOT_VALUE) &&
				     (lastY != NO_PLOT_VALUE) )
        		    	    XDrawLine(display, drawable, gc, lastX[var],
					      lastY, x[var], y);
			    }
	                    else
                	        XDrawPoint(display, drawable, gc, x[var], y);
		        }				
    	    	    }
		}
#if TRACE_ON
    printf("start %d  end %d y values %d %f\n", plotStart, plotEnd, y,
           dataValues[plotEnd][configParams.items[Y]]);
#endif
	    }
	}
	else					/* scrolling mode selected    */
	{
	    if (pointCount > plotStart)
	    {
		if (pointCount <= plotMax)	/* not enough points to scroll*/
		{
		    if (configParams.graphicsDisplayRate < 1.0)
		    {
		        savedPlotStart = plotStart;
		        plotStart = pointCount -
				    (1.0 / configParams.graphicsDisplayRate);
		        plotDataPoints(data, display, drawable, ERASE_OFF);
			plotStart = savedPlotStart;
		    }
		    else
		    {
    	    	        for (var = 0; var < configParams.numXVariables; var++)
    	    	        {
		    	    if ( (x[var] != NO_PLOT_VALUE) &&
				 (y != NO_PLOT_VALUE) )
		    	    {
                                XSetForeground(display, gc, data->colours[var]);
	                        if (configParams.lineDrawing)
			        {
		    	            if ( (lastX[var] != NO_PLOT_VALUE) &&
				         (lastY != NO_PLOT_VALUE) )
        		        	    XDrawLine(display, drawable, gc,
						      lastX[var], lastY, x[var],
						      y);
			        }
	                        else
                	            XDrawPoint(display, drawable, gc, x[var],
					       y);
			    }
    	    	        }
		    }
#if TRACE_ON
    printf("start %d  end %d y values %d %f\n", plotStart, plotEnd, y,
           dataValues[plotEnd][configParams.items[Y]]);
#endif
		}
		else		       /* scrolling, erase and redraw display */
		{
		    plotDataPoints(data, display, drawable, ERASE_ON);
		    plotMarks(plotParams, display, drawable, ERASE_ON);
	    	    plotEnd = pointCount;
		    if (configParams.items[Y] == TIME)	/* max and min are in */
		    {					/* the selected units */
		    	data->maxValues[Y] =
			    dataValues[plotEnd][configParams.items[Y]] / 
			    yTimeUnitsScale;
		    	data->minValues[Y] = data->maxValues[Y] -
			                     (configParams.yVariable.maxValue -
			                      configParams.yVariable.minValue);
					/* minValueY is in seconds            */
			minValueY = data->minValues[Y] * yTimeUnitsScale;
		    }
		    else
		    {
		    	data->maxValues[Y] =
			    dataValues[plotEnd][configParams.items[Y]];
		    	data->minValues[Y] = data->maxValues[Y] -
			    (configParams.yVariable.maxValue -
			     configParams.yVariable.minValue);
			minValueY = data->minValues[Y];
		    }
		    plotStart = plotEnd;
		    while ( (dataValues[plotStart][configParams.items[Y]] >
			     minValueY) && (plotStart > 0) )
			plotStart--;
		    plotSpan = plotEnd - plotStart;
		    plotScrolling = TRUE;
		    if (reDrawAxes(plotParams, display, drawable) == OK)
		    {
		        plotDataPoints(data, display, drawable, ERASE_OFF);
		    	plotMarks(plotParams, display, drawable, ERASE_OFF);
		    }
		    else
	    	        XClearArea(display, drawable, 0, 0, 0,
		       	           data->yStatusLinePos - AXES_OFFSET4, TRUE);

#if TRACE_ON
    printf("start %d  end %d y values %f %f\n", plotStart, plotEnd,
           dataValues[plotStart][configParams.items[Y]],
           dataValues[plotEnd][configParams.items[Y]]);
#endif
		}
    	    }
	}
    }

/* update previous values for next loop                                       */
    for (var = 0; var < configParams.numXVariables; var++)
        lastX[var] = x[var];
    lastY = y;

}  /* updateDisplay */


/******************************************************************************/
/* Function : reDrawAxes                                                      */
/* Purpose  : Redraws the data display axes obscured by an erased data plot.  */
/* Inputs   : Plotting data, display and drawable.                            */
/* Outputs  : Returns OK or ERROR.                                            */
/******************************************************************************/
    Int16
reDrawAxes(plotParameters *plotParams, Display *display, Drawable drawable)
{
    Pixel background;                           /* background colour          */

/* get background colour                                                      */
    XtVaGetValues(plotParams->displayData.display, XmNbackground, &background,
		  NULL);

/* erase the y axis tick labels                                               */
    XSetForeground(display, gc, background);
    XDrawString(display, drawable, gc, plotParams->xFirstTickLabelPos[Y],
		plotParams->yFirstTickLabelPos[Y],
		plotParams->firstTickLabel[Y],
		plotParams->firstTickLabelLength[Y]);
    XDrawString(display, drawable, gc, plotParams->xLastTickLabelPos[Y],
		plotParams->yLastTickLabelPos[Y],
		plotParams->lastTickLabel[Y],
		plotParams->lastTickLabelLength[Y]);

/* set y axis tick labels to update with scrolling                            */
    makeTickLabel(Y, plotParams);
    plotParams->xFirstTickLabelPos[Y] = plotParams->displayData.xAxisStart -
    	plotParams->firstTickLabelLength[Y] * OFFSET_PER_CHAR;
    plotParams->xLastTickLabelPos[Y] = plotParams->displayData.xAxisStart -
        plotParams->lastTickLabelLength[Y] * OFFSET_PER_CHAR;

/* if tick labels too long, y axis must be repositioned, return ERROR         */
    if ( (plotParams->xFirstTickLabelPos[Y] < 0) ||
         (plotParams->xLastTickLabelPos[Y] < 0) )
	return(ERROR);

/* draw the new y axis tick labels                                            */
    XSetForeground(display, gc, plotParams->displayData.colours[Y]);
    XDrawString(display, drawable, gc, plotParams->xFirstTickLabelPos[Y],
		plotParams->yFirstTickLabelPos[Y],
		plotParams->firstTickLabel[Y],
		plotParams->firstTickLabelLength[Y]);
    XDrawString(display, drawable, gc, plotParams->xLastTickLabelPos[Y],
		plotParams->yLastTickLabelPos[Y],
		plotParams->lastTickLabel[Y],
		plotParams->lastTickLabelLength[Y]);

/* redraw x and y axes                                                        */
    if (configParams.numXVariables > 1)
    {
    	XDrawSegments(display, drawable, gc, plotParams->segments, 2);
    	XDrawSegments(display, drawable, gc, plotParams->tickMarks[Y],
		      plotParams->numTicks[Y]);
    	XSetForeground(display, gc, plotParams->displayData.colours[X1]);
    	XDrawSegments(display, drawable, gc, plotParams->segments+2, 1);
    	XDrawSegments(display, drawable, gc, plotParams->tickMarks[X1],
		      plotParams->numTicks[X1]);
    	XSetForeground(display, gc, plotParams->displayData.colours[X2]);
    	XDrawSegments(display, drawable, gc, plotParams->segments+3, 1);
    	XDrawSegments(display, drawable, gc, plotParams->tickMarks[X2],
		      plotParams->numTicks[X2]);
    }
    else if (configParams.numXVariables == 1)
    {
    	XDrawSegments(display, drawable, gc, plotParams->segments, 4);
    	XDrawSegments(display, drawable, gc, plotParams->tickMarks[Y],
		      plotParams->numTicks[Y]);
    	XSetForeground(display, gc, plotParams->displayData.colours[X1]);
    	XDrawSegments(display, drawable, gc, plotParams->segments+2, 1);
    	XDrawSegments(display, drawable, gc, plotParams->tickMarks[X1],
		      plotParams->numTicks[X1]);
    }
    else if (configParams.numXVariables == 0)
    {
    	XDrawSegments(display, drawable, gc, plotParams->segments, 4);
    	XDrawSegments(display, drawable, gc, plotParams->tickMarks[Y],
		      plotParams->numTicks[Y]);
    }

    return(OK);

} /* reDrawAxes */


/******************************************************************************/
/* Function : updateStatusLine                                                */
/* Purpose  : Updates the status line on the display.                         */
/* Inputs   : Display data and interval id.                                   */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
updateStatusLine(XtPointer clientData, XtIntervalId *id)
{
    displayUpdateData *data = (displayUpdateData *) clientData;
    Display           *display = XtDisplay(data->display);
    Drawable          drawable = XtWindow(data->display);
    char              statusLineLabel[NUM_DISPLAY_ITEMS][STRING_LENGTH] =
		      { "BA:", "B%:", "CD:", "DP:",
			"OX:", "OC:", "OT:", "PR:",
			"SL:", "S#:", "ST:", "STH:",
			"SV:", "TP:", "TH:", "TM:",
			"TR:" };
    char              tmpString[STRING_LENGTH];
    char              statusLine[AXIS_LABEL_LENGTH] = "";
    Int16             var;				/* loop counter       */
    Nat32             timeout = 1000.0 / configParams.statusLineRate;

/* restart timeout                                                            */
    statusLineTimeoutId =
	XtAppAddTimeOut(XtWidgetToApplicationContext(data->display),
		        timeout, updateStatusLine, (XtPointer) data);

    for (var = 0; var < configParams.numStatusLineVariables; var++)
    {
	if (configParams.statusLineItems[var] == TIME) 
    	    sprintf(tmpString, "%s %8.4f  ",
		    statusLineLabel[configParams.statusLineItems[var]],
	            dataValues[pointCount][configParams.statusLineItems[var]] / 
		    yTimeUnitsScale);
	else
    	    sprintf(tmpString, "%s %8.4f  ",
		    statusLineLabel[configParams.statusLineItems[var]],
	            dataValues[pointCount][configParams.statusLineItems[var]]);
    	strcat(statusLine, tmpString);
    }

    XClearArea(display, drawable, 0, data->yStatusLinePos - AXES_OFFSET4, 0,
	       0, FALSE);
    XSetForeground(display, gc, data->colours[Y]);
    XDrawString(display, drawable, gc, data->xStatusLinePos,
		data->yStatusLinePos, statusLine, strlen(statusLine));

}  /* updateStatusLine */


/******************************************************************************/
/* Function : statusRateCallback                                              */
/* Purpose  : Changes the status line display rate.                           */
/* Inputs   : Widget, client data and interval id.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
statusRateCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    static Widget     dialog = NULL;
    char              value[STRING_LENGTH];
    Flt32             rate;

    rate = atof(XmTextFieldGetString(w));

/* check for out of range values                                              */
    if ( (rate <= EPSILON) || (rate > 1.0) )
    {

/* replace new value with previous value                                      */
	sprintf(value, "%6.4f", configParams.statusLineRate);
	XmTextReplace(w, 0, STRING_LENGTH, value);

/* display warning message                                                    */
	if (!dialog)
	{
	    dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			  STATUS_RATE_MESSAGE, strlen(STATUS_RATE_MESSAGE)+1,
			  NULL);
	}
	XtManageChild(dialog);
    }
    else					/* set new rate               */
    	configParams.statusLineRate = rate;

}


/******************************************************************************/
/* Function : xApplyCallback                                                  */
/* Purpose  : Applies changes to the x item configuration parameters.         */
/* Inputs   : Widget, plotting data and interval id.                          */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
xApplyCallback(Widget w, XtPointer clientData, XtPointer call)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    Widget         xParamsForm = XtParent(w);
    Widget         parametersFrame = XtParent(xParamsForm);
    Widget         thisXForm = XtParent(parametersFrame);
    Int16          var, tick;			/* loop counters              */
    Int16          dx;  		        /* x axis tick spacing        */


/* determine which x item variable                                            */
    for (var = 0; var < configParams.numXVariables; var++)
	if (thisXForm == xForm[var])
	    break;

/* reset axis label if changed                                                */
    if (paramsAltered.labelChanged)
    {
	strcpy(configParams.xVariables[var].axisLabel, paramsAltered.newLabel);
	strcpy(plotParams->axisLabel[var], paramsAltered.newLabel);
	plotParams->labelLength[var] = strlen(paramsAltered.newLabel);

	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0, 0,
		       TRUE);

	paramsAltered.labelChanged = FALSE;
    }

/* reset minimum value if changed                                             */
/* make and reposition tick label and rescale axis                            */
    if (paramsAltered.minChanged)
    {
	configParams.xVariables[var].minValue = paramsAltered.newMin;
	plotParams->displayData.minValues[var] = paramsAltered.newMin;
	makeTickLabel(var, plotParams);
	plotParams->xLastTickLabelPos[var] = plotParams->displayData.xAxisEnd -
	    plotParams->lastTickLabelLength[var] * OFFSET_PER_CHAR;
	plotParams->displayData.scales[var] = plotParams->xAxisLength /
	    (plotParams->displayData.maxValues[var] -
	     plotParams->displayData.minValues[var]);
	rescale = TRUE;

	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0, 0,
		       TRUE);

	paramsAltered.minChanged = FALSE;
    }
	
/* reset maximum value if changed                                             */
/* make and reposition tick label and rescale axis                            */
    if (paramsAltered.maxChanged)
    {
	configParams.xVariables[var].maxValue = paramsAltered.newMax;
	plotParams->displayData.maxValues[var] = paramsAltered.newMax;
	makeTickLabel(var, plotParams);
	plotParams->xLastTickLabelPos[var] = plotParams->displayData.xAxisEnd -
	    plotParams->lastTickLabelLength[var] * OFFSET_PER_CHAR;
	plotParams->displayData.scales[var] = plotParams->xAxisLength /
	    (plotParams->displayData.maxValues[var] -
	     plotParams->displayData.minValues[var]);
	rescale = TRUE;

	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0, 0,
		       TRUE);

	paramsAltered.maxChanged = FALSE;
    }
	
/* reset axis colour if changed                                               */
    if (paramsAltered.colourChanged)
    {
	toColour(&configParams.xVariables[var], paramsAltered.newColourName);
	plotParams->displayData.colours[var] = paramsAltered.newColour;
	plotParams->xColours[var] = configParams.xVariables[var].colour;

	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0, 0,
		       TRUE);

	paramsAltered.colourChanged = FALSE;
    }

/* reset major divisions value if changed                                     */
/* reset number and position of tick marks                                    */
    if (paramsAltered.divisionsChanged)
    {
	configParams.xVariables[var].majorDivisions =
	    paramsAltered.newDivisions;
	plotParams->majorDivisions[var] = paramsAltered.newDivisions;
        plotParams->numTicks[var] = plotParams->majorDivisions[var] + 1;

        dx = plotParams->xAxisLength / (plotParams->numTicks[var] - 1);
        for (tick = 0; tick < plotParams->numTicks[var]; tick++)
        {
            plotParams->tickMarks[var][tick].x1 =
            plotParams->tickMarks[var][tick].x2 =
                plotParams->displayData.xAxisStart + tick * dx;
            plotParams->tickMarks[var][tick].y1 =
                plotParams->segments[var+2].y1;
            plotParams->tickMarks[var][tick].y2 = (odd(var)) ?
                plotParams->segments[var+2].y1 - plotParams->tickLength :
                plotParams->segments[var+2].y1 + plotParams->tickLength;
        }
        plotParams->tickMarks[var][plotParams->numTicks[var]-1].x1 =
        plotParams->tickMarks[var][plotParams->numTicks[var]-1].x2 =
            plotParams->displayData.xAxisEnd;

	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0, 0,
		       TRUE);

	paramsAltered.divisionsChanged = FALSE;
    }

    XtPopdown(XtParent(xForm[var]));

}  /* xApplyCallback */


/******************************************************************************/
/* Function : xCancelAllCallback                                              */
/* Purpose  : Cancels all changes to an x item configuration parameters.      */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
xCancelAllCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    Widget xParamsForm = XtParent(w);
    Widget parametersFrame = XtParent(xParamsForm);
    Widget thisXForm = XtParent(parametersFrame);
    char   value[STRING_LENGTH];
    Int16  var;

/* determine which x item variable                                            */
    for (var = 0; var < configParams.numXVariables; var++)
	if (thisXForm == xForm[var])
	    break;

/* reset axis label if changed                                                */
    if (paramsAltered.labelChanged)
    {
	XmTextReplace(paramsAltered.labelWidget, 0, STRING_LENGTH,
		      paramsAltered.oldLabel);
	paramsAltered.labelChanged = FALSE;
    }

/* reset minimum value if changed                                             */
    if (paramsAltered.minChanged)
    {
	sprintf(value, "%f", paramsAltered.oldMin);
	XmTextReplace(paramsAltered.minWidget, 0, STRING_LENGTH, value);
	paramsAltered.minChanged = FALSE;
    }

/* reset maximum value if changed                                             */
    if (paramsAltered.maxChanged)
    {
	sprintf(value, "%f", paramsAltered.oldMax);
	XmTextReplace(paramsAltered.maxWidget, 0, STRING_LENGTH, value);
	paramsAltered.maxChanged = FALSE;
    }

/* reset axis colour if changed                                               */
/* doesn't work because can't set colourPulldownMenu widget menuHistory 
    if (paramsAltered.colourChanged)
    {
	XtVaSetValues(paramsltered.colourWidget, XmNmenuHistory,
		      paramsAltered.oldColourButton, NULL);
	paramsAltered.colourChanged = FALSE;
    }
*/

/* reset major divisions value if changed                                     */
    if (paramsAltered.divisionsChanged)
    {
	sprintf(value, "%d", paramsAltered.oldDivisions);
	XmTextReplace(paramsAltered.divisionsWidget, 0, STRING_LENGTH, value);
	paramsAltered.divisionsChanged = FALSE;
    }

}  /* xCancelAllCallback */


/******************************************************************************/
/* Function : xCancelLastCallback                                             */
/* Purpose  : Cancels the last change to an x item configuration parameter.   */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
xCancelLastCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    char value[STRING_LENGTH];

    switch (paramsAltered.lastParam)
    {
	case LABEL :			/* reset axis label if changed        */
    	    if (paramsAltered.labelChanged)
    	    {
		XmTextReplace(paramsAltered.labelWidget, 0, STRING_LENGTH,
		      	      paramsAltered.oldLabel);
		paramsAltered.labelChanged = FALSE;
    	    }
	    break;

	case MIN_VALUE :		 /* reset minimum value if changed    */
    	    if (paramsAltered.minChanged)
    	    {
	        sprintf(value, "%f", paramsAltered.oldMin);
	        XmTextReplace(paramsAltered.minWidget, 0, STRING_LENGTH, value);
		paramsAltered.minChanged = FALSE;
    	    }
	    break;

	case MAX_VALUE :		 /* reset maximum value if changed    */
    	    if (paramsAltered.maxChanged)
    	    {
		sprintf(value, "%f", paramsAltered.oldMax);
		XmTextReplace(paramsAltered.maxWidget, 0, STRING_LENGTH, value);
		paramsAltered.maxChanged = FALSE;
    	    }
	    break;

	case COLOUR :			/* reset axis colour if changed       */
/* doesn't work because can't set colourPulldownMenu widget menuHistory 
    	    if (paramsAltered.colourChanged)
    	    {
	        XtVaSetValues(paramsAltered.colourWidget, XmNmenuHistory,
		              paramsAltered.oldColourButton, NULL);
	        paramsAltered.colourChanged = FALSE;
    	    }
*/
	    break;

	case MAJOR_DIVISIONS :			/* reset major divisions value*/
    	    if (paramsAltered.divisionsChanged) /* if changed                 */
    	    {
		sprintf(value, "%d", paramsAltered.oldDivisions);
		XmTextReplace(paramsAltered.divisionsWidget, 0, STRING_LENGTH,
                              value);
		paramsAltered.divisionsChanged = FALSE;
    	    }
	    break;

    }

}  /* xCancelLastCallback */


/******************************************************************************/
/* Function : xDivisionsCallback                                              */
/* Purpose  : Changes the x item number of major divisions.                   */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
xDivisionsCallback(Widget w, XtPointer clientData, XtPointer calldata)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    Widget         parameterInputRowColumn = XtParent(w);
    Widget         xParamsForm = XtParent(parameterInputRowColumn);
    Widget         parametersFrame = XtParent(xParamsForm);
    Widget         thisXForm = XtParent(parametersFrame);
    static Widget  dialog = NULL;
    char           value[STRING_LENGTH];
    Int16          var;
    Int16          divs;

/* determine which x item variable                                            */
    for (var = 0; var < configParams.numXVariables; var++)
	if (thisXForm == xForm[var])
	    break;

    divs = atoi(XmTextFieldGetString(w));

    if (divs < 1)
    {
/* replace new value with previous value                                      */
	sprintf(value, "%d", plotParams->majorDivisions[var]);
	XmTextReplace(w, 0, STRING_LENGTH, value);

/* display warning message                                                    */
	if (!dialog)
	{
	    dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			  DIVS_MESSAGE, strlen(DIVS_MESSAGE)+1, NULL);
	}
	XtManageChild(dialog);
    }
    else
    {
	paramsAltered.lastParam = MAJOR_DIVISIONS;
	paramsAltered.divisionsWidget = w;
	paramsAltered.divisionsChanged = TRUE;
	paramsAltered.newDivisions = divs;
	paramsAltered.oldDivisions = plotParams->majorDivisions[var];
    }

}  /* xDivisionsCallback */


/******************************************************************************/
/* Function : xItemsCallback                                                  */
/* Purpose  : Sets the x item variables list.                                 */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
xItemsCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    XmToggleButtonCallbackStruct *toggle =
	(XmToggleButtonCallbackStruct *) callData;
    Int16    item, var;
    XmString xmString[NUM_DISPLAY_ITEMS];

    if (toggle->set)
    {
	xItemsListed = TRUE;

    	for (item = 0; item < NUM_DISPLAY_ITEMS; item++)
            xmString[item] = XmStringCreateLtoR(listData.displayItems[item],
					        XmFONTLIST_DEFAULT_TAG);

	if (yItemListed)
	{
    	    XmListDeselectItem(listData.w, xmString[configParams.items[Y]]);
	    yItemListed = FALSE;
	}
	else if (statusItemsListed)
	{
#if DEBUG_ON
	    printf("deselecting status line  ");
#endif
	    for (var = 0; var < configParams.numStatusLineVariables; var++)
#if DEBUG_ON
	    {
	    	printf("%d  ", configParams.statusLineItems[var]);
#endif
		XmListDeselectItem(listData.w,
				   xmString[configParams.statusLineItems[var]]);
#if DEBUG_ON
	    }
		printf("\n");
#endif
	    statusItemsListed = FALSE;
	}

#if DEBUG_ON
	printf("selecting x ");
#endif
    	for (var = 0; var < configParams.numXVariables; var++)
#if DEBUG_ON
	{
	printf("%d  ", configParams.items[var]);
#endif
	    XmListSelectItem(listData.w, xmString[configParams.items[var]],
			     FALSE);
#if DEBUG_ON
	}
	printf("\n");
#endif

    	for (item = 0; item < NUM_DISPLAY_ITEMS; item++)
	    XmStringFree(xmString[item]);
    }

}  /* xItemsCallback */


/******************************************************************************/
/* Function : xLabelCallback                                                  */
/* Purpose  : Changes the x item axis label.                                  */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
xLabelCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    Widget         parameterInputRowColumn = XtParent(w);
    Widget         xParamsForm = XtParent(parameterInputRowColumn);
    Widget         parametersFrame = XtParent(xParamsForm);
    Widget         thisXForm = XtParent(parametersFrame);
    Int16          var;

/* determine which x item variable                                            */
    for (var = 0; var < configParams.numXVariables; var++)
	if (thisXForm == xForm[var])
	    break;

    strcpy(paramsAltered.newLabel, XmTextFieldGetString(w));

    paramsAltered.lastParam = LABEL;
    paramsAltered.labelWidget = w;
    paramsAltered.labelChanged = TRUE;
    strcpy(paramsAltered.oldLabel, plotParams->axisLabel[var]);

}  /* xLabelCallback */


/******************************************************************************/
/* Function : xMaxCallback                                                    */
/* Purpose  : Changes the x item maximum value.                               */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
xMaxCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    Widget         parameterInputRowColumn = XtParent(w);
    Widget         xParamsForm = XtParent(parameterInputRowColumn);
    Widget         parametersFrame = XtParent(xParamsForm);
    Widget         thisXForm = XtParent(parametersFrame);
    static Widget  dialog = NULL;
    char           value[STRING_LENGTH];
    Int16          var;
    Flt32          max;
    Flt32          min;

/* determine which x item variable                                            */
    for (var = 0; var < configParams.numXVariables; var++)
	if (thisXForm == xForm[var])
	    break;

    max = atof(XmTextFieldGetString(w));

    if (paramsAltered.minChanged)
	min = paramsAltered.newMin;
    else
	min = plotParams->displayData.minValues[var];

    if (max <= min)
    {
/* replace new value with previous value                                      */
	sprintf(value, "%f", plotParams->displayData.maxValues[var]);
	XmTextReplace(w, 0, STRING_LENGTH, value);

/* display warning message                                                    */
	if (!dialog)
	{
	    dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			  MAX_MESSAGE, strlen(MAX_MESSAGE)+1, NULL);
	}
	XtManageChild(dialog);
    }
    else
    {
	paramsAltered.lastParam = MAX_VALUE;
	paramsAltered.maxWidget = w;
	paramsAltered.maxChanged = TRUE;
	paramsAltered.newMax = max;
	paramsAltered.oldMax = plotParams->displayData.maxValues[var];
    }

}  /* xMaxCallback */


/******************************************************************************/
/* Function : xMinCallback                                                    */
/* Purpose  : Changes the x item minimum value.                               */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
xMinCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    Widget         parameterInputRowColumn = XtParent(w);
    Widget         xParamsForm = XtParent(parameterInputRowColumn);
    Widget         parametersFrame = XtParent(xParamsForm);
    Widget         thisXForm = XtParent(parametersFrame);
    static Widget  dialog = NULL;
    char           value[STRING_LENGTH];
    Int16          var;
    Flt32          min;
    Flt32          max;

/* determine which x item variable                                            */
    for (var = 0; var < configParams.numXVariables; var++)
	if (thisXForm == xForm[var])
	    break;

    min = atof(XmTextFieldGetString(w));

    if (paramsAltered.maxChanged)
	max = paramsAltered.newMax;
    else
	max = plotParams->displayData.maxValues[var];

    if (min >= max)
    {
/* replace new value with previous value                                      */
	sprintf(value, "%f", plotParams->displayData.minValues[var]);
	XmTextReplace(w, 0, STRING_LENGTH, value);

/* display warning message                                                    */
	if (!dialog)
	{
	    dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			  MIN_MESSAGE, strlen(MIN_MESSAGE)+1, NULL);
	}
	XtManageChild(dialog);
    }
    else
    {
	paramsAltered.lastParam = MIN_VALUE;
	paramsAltered.minWidget = w;
	paramsAltered.minChanged = TRUE;
	paramsAltered.newMin = min;
	paramsAltered.oldMin = plotParams->displayData.minValues[var];
    }

}  /* xMinCallback */


/******************************************************************************/
/* Function : yApplyCallback                                                  */
/* Purpose  : Applies changes to the y item configuration parameters.         */
/* Inputs   : Widget, plotting data and interval id.                          */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
yApplyCallback(Widget w, XtPointer clientData, XtPointer call)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    static Widget  dialog = NULL;
    char           buffer[AXIS_LABEL_LENGTH];
    char           *tmpString;
    Int16          word;
    Int16          largestYLabel;
    Int16          tick;			/* loop counter               */
    Int16          dy;  		        /* y axis tick spacing        */
    Int32          count;			/* loop counter               */

/* reset axis label if changed                                                */
    if (paramsAltered.labelChanged)
    {
	strcpy(configParams.yVariable.axisLabel, paramsAltered.newLabel);
	strcpy(plotParams->axisLabel[Y], paramsAltered.newLabel);
	plotParams->labelLength[Y] = strlen(paramsAltered.newLabel);

/* find position of words in y axis label                                     */
    	for (word = 0; word < NUM_Y_WORDS; word++)
             plotParams->yLabelSpace[word] = 0;
    	strcpy(buffer, configParams.yVariable.axisLabel);
    	if ( (tmpString = strtok(buffer, " ")) !=0)
    	{
            plotParams->yLabelSpace[0] = strlen(tmpString) + 1;
            largestYLabel = plotParams->yLabelSpace[0];
            plotParams->numYWords = 1;

            for (;;)
            {
            	if ( (tmpString = strtok(0, " ")) == 0)
                    break;

            	plotParams->yLabelSpace[plotParams->numYWords] =
                plotParams->yLabelSpace[plotParams->numYWords-1] +
                    strlen(tmpString) + 1;
            	largestYLabel = MAX(largestYLabel, strlen(tmpString) + 1);
            	plotParams->numYWords++;
            }
    	}
    	else
    	{
            plotParams->numYWords = 0;
            largestYLabel = 0;
    	}

    	largestYLabel = MAX(largestYLabel, plotParams->firstTickLabelLength[Y]);
    	largestYLabel = MAX(largestYLabel, plotParams->lastTickLabelLength[Y]);

    	plotParams->displayData.xAxisStart = AXES_OFFSET3 +
					     largestYLabel * OFFSET_PER_CHAR;

	initAxesGeom(plotParams);		/* recalculate axes geometry  */

	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0, 0,
		       TRUE);

	paramsAltered.labelChanged = FALSE;
    }

/* reset minimum value if changed                                             */
/* make and reposition tick label and rescale axis                            */
    if (paramsAltered.minChanged)
    {
	configParams.yVariable.minValue = paramsAltered.newMin;
	plotParams->displayData.minValues[Y] = paramsAltered.newMin;
	plotParams->displayData.maxValues[Y] = configParams.yVariable.maxValue;

	if (paramsAltered.timeUnitsChanged)	/* reset y time units scale   */
	{
            if (configParams.yVariable.timeUnits == SECONDS)
                yTimeUnitsScale = 1.0;  	/* seconds per second         */
            else if (configParams.yVariable.timeUnits == MINUTES)
                yTimeUnitsScale = SECONDS_PER_MINUTE;
            else if (configParams.yVariable.timeUnits == HOURS)
                yTimeUnitsScale = SECONDS_PER_HOUR;
	}

	makeTickLabel(Y, plotParams);
	plotParams->xFirstTickLabelPos[Y] = plotParams->displayData.xAxisStart -
	    plotParams->firstTickLabelLength[Y] * OFFSET_PER_CHAR;
	plotParams->xLastTickLabelPos[Y] = plotParams->displayData.xAxisStart -
	    plotParams->lastTickLabelLength[Y] * OFFSET_PER_CHAR;

	if (configParams.items[Y] == TIME)
    	    plotParams->displayData.scales[Y] = plotParams->yAxisLength /
	        ( (plotParams->displayData.maxValues[Y] -
		   plotParams->displayData.minValues[Y]) *
	           yTimeUnitsScale * DATA_UPDATE_RATE );
	else
    	    plotParams->displayData.scales[Y] = plotParams->yAxisLength /
	        ( (plotParams->displayData.maxValues[Y] -
		   plotParams->displayData.minValues[Y]) *
	           DATA_UPDATE_RATE );

/* reset plot start, end, span and max values                                 */
        if (configParams.items[Y] == SCAN_NUMBER)
    	{
            plotStart = configParams.yVariable.minValue;
            plotSpan = configParams.yVariable.maxValue -
		       configParams.yVariable.minValue;
            plotMax = configParams.yVariable.minValue + plotSpan;
	    plotEnd = pointCount;
	    if (plotEnd < plotStart)
		plotEnd = plotStart;
	    else if (plotEnd > plotMax)
		plotEnd = plotMax;
    	}

    	else if (configParams.items[Y] == TIME)
    	{
	    if (configParams.yVariable.timeUnits == SECONDS)
	    	minInSecs = configParams.yVariable.minValue;
	    else if (configParams.yVariable.timeUnits == MINUTES)
	    	minInSecs = configParams.yVariable.minValue *
			    (Flt64) SECONDS_PER_MINUTE;
	    else    /* configParams.yVariable.timeUnits == HOURS */
	    	minInSecs = configParams.yVariable.minValue *
			    (Flt64) SECONDS_PER_HOUR;
	    plotSpan = (maxInSecs - minInSecs) * DATA_UPDATE_RATE;
	    if (dataValues[0][TIME] >= minInSecs)
	    {
		plotStart = 0;
                plotMax = (maxInSecs - dataValues[0][TIME]) *
			   DATA_UPDATE_RATE;
	    }
            else
	    {
                plotStart = (minInSecs - dataValues[0][TIME]) *
			     DATA_UPDATE_RATE;
                plotMax = plotStart + plotSpan;
	    }
            if (pointCount <= plotStart)
                plotEnd = plotStart;
            else if (pointCount <= plotMax)
                plotEnd = pointCount;
            else
                plotEnd = plotMax;
    	}

	else				/* !SCAN_NUMBER and !TIME             */
	{				/* set start to first data value      */
	    plotStart = -1;		/* between min and max                */
	    for (count = 0; count <= pointCount; count++)
	        if ( (dataValues[count][configParams.items[Y]] >=
		      configParams.yVariable.minValue) &&
	             (dataValues[count][configParams.items[Y]] <=
		      configParams.yVariable.maxValue) )
	        {
		    plotStart = count;
		    break;
	        }
	    if (plotStart == -1)	/* min value not found in data array  */
   	    {
	        plotEnd = plotStart;
		plotSpan = 0;
	        plotMax = plotEnd;
	    }
	    else			/* min value found, max == end don't  */
					/* change, update span                */
	        plotSpan = plotMax - plotStart;
	}			

	rescale = TRUE;

	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0, 0,
		       TRUE);

	paramsAltered.minChanged = FALSE;
    }
	
/* reset maximum value if changed                                             */
/* make and reposition tick label and rescale axis                            */
    if (paramsAltered.maxChanged)
    {
	configParams.yVariable.maxValue = paramsAltered.newMax;
	plotParams->displayData.maxValues[Y] = paramsAltered.newMax;
	plotParams->displayData.minValues[Y] = configParams.yVariable.minValue;

	if (paramsAltered.timeUnitsChanged)	/* reset y time units scale   */
	{
            if (configParams.yVariable.timeUnits == SECONDS)
                yTimeUnitsScale = 1.0;  	/* seconds per second         */
            else if (configParams.yVariable.timeUnits == MINUTES)
                yTimeUnitsScale = SECONDS_PER_MINUTE;
            else if (configParams.yVariable.timeUnits == HOURS)
                yTimeUnitsScale = SECONDS_PER_HOUR;
	}

	makeTickLabel(Y, plotParams);
    	plotParams->xFirstTickLabelPos[Y] = plotParams->displayData.xAxisStart -
            plotParams->firstTickLabelLength[Y] * OFFSET_PER_CHAR;
    	plotParams->xLastTickLabelPos[Y] = plotParams->displayData.xAxisStart -
            plotParams->lastTickLabelLength[Y] * OFFSET_PER_CHAR;

	if (configParams.items[Y] == TIME)
    	    plotParams->displayData.scales[Y] = plotParams->yAxisLength /
	        ( (plotParams->displayData.maxValues[Y] -
		   plotParams->displayData.minValues[Y]) *
	           yTimeUnitsScale * DATA_UPDATE_RATE );
	else
    	    plotParams->displayData.scales[Y] = plotParams->yAxisLength /
	        ( (plotParams->displayData.maxValues[Y] -
		   plotParams->displayData.minValues[Y]) *
	           DATA_UPDATE_RATE );

/* reset plot start, end, span and max values                                 */
        if (configParams.items[Y] == SCAN_NUMBER)
    	{
	    plotStart = configParams.yVariable.minValue;
            plotSpan = configParams.yVariable.maxValue -
		       configParams.yVariable.minValue;
            plotMax = configParams.yVariable.minValue + plotSpan;
	    plotEnd = pointCount;
	    if (plotEnd < plotStart)
		plotEnd = plotStart;
	    else if (plotEnd > plotMax)
		plotEnd = plotMax;
	    if (plotEnd < plotMax)
		plotScrolling = FALSE;
    	}

    	else if (configParams.items[Y] == TIME)
    	{
	    if (configParams.yVariable.timeUnits == SECONDS)
	    	maxInSecs = configParams.yVariable.maxValue;
	    else if (configParams.yVariable.timeUnits == MINUTES)
	    	maxInSecs = configParams.yVariable.maxValue *
			    (Flt64) SECONDS_PER_MINUTE;
	    else    /* configParams.yVariable.timeUnits == HOURS */
	    	maxInSecs = configParams.yVariable.maxValue *
			    (Flt64) SECONDS_PER_HOUR;
	    plotSpan = (maxInSecs - minInSecs) * DATA_UPDATE_RATE;
            if (dataValues[0][TIME] >= minInSecs)
	    {
		plotStart = 0;
                plotMax = (maxInSecs - dataValues[0][TIME]) * DATA_UPDATE_RATE;
	    }
            else
	    {
                plotStart = (minInSecs - dataValues[0][TIME]) *
			     DATA_UPDATE_RATE;
                plotMax = plotStart + plotSpan;
	    }
            if (pointCount <= plotStart)
                plotEnd = plotStart;
            else if (pointCount <= plotMax)
                plotEnd = pointCount;
            else
                plotEnd = plotMax;
	    if (plotEnd < plotMax)
		plotScrolling = FALSE;
    	}

	else				/* !SCAN_NUMBER and !TIME             */
	{			
	    plotStart = -1;		/* reset start to min value           */
	    for (count = 0; count <= pointCount; count++)
	        if ( (dataValues[count][configParams.items[Y]] >=
		      configParams.yVariable.minValue) &&
	             (dataValues[count][configParams.items[Y]] <=
		      configParams.yVariable.maxValue) )
	        {
	       	    plotStart = count;
	            break;
	        }
					/* set end to latest data value       */
	    plotEnd = plotStart;	/* between min and max                */
	    for (count = pointCount; count >= 0; count--)
	        if ( (dataValues[count][configParams.items[Y]] >=
		      configParams.yVariable.minValue) &&
	             (dataValues[count][configParams.items[Y]] <=
		      configParams.yVariable.maxValue) )
	        {
		    plotEnd = count;
		    break;
	        }
	    if ((plotStart == -1) ||	/* min value not found in data array, */
		(plotEnd == plotStart)) /* or max value not found             */
   	    {
		plotSpan = 0;
	        plotMax = plotEnd;
	    }
	    else			/* end value found, max set to end,   */
	    {				/* update span                        */
		plotMax = plotEnd;
	        plotSpan = plotMax - plotStart;
	    }
	}			

	rescale = TRUE;

	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0, 0,
		       TRUE);

	paramsAltered.maxChanged = FALSE;
    }
	
/* reset major divisions value if changed                                     */
/* reset number and position of tick marks                                    */
    if (paramsAltered.divisionsChanged)
    {
	configParams.yVariable.majorDivisions = paramsAltered.newDivisions;
	plotParams->majorDivisions[Y] = paramsAltered.newDivisions;
        plotParams->numTicks[Y] = plotParams->majorDivisions[Y] - 1;

        dy = plotParams->yAxisLength / (plotParams->numTicks[Y] + 1);
        for (tick = 0; tick < plotParams->numTicks[Y]; tick++)
        {
            plotParams->tickMarks[Y][tick].x1 =
		plotParams->displayData.xAxisStart;
            plotParams->tickMarks[Y][tick].x2 =
                plotParams->displayData.xAxisStart + plotParams->tickLength;
            plotParams->tickMarks[Y][tick].y1 =
            plotParams->tickMarks[Y][tick].y2 =
		plotParams->displayData.yAxisStart + (tick + 1) * dy;
        }

	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0, 0,
		       TRUE);

	paramsAltered.divisionsChanged = FALSE;
    }

/* reset time units value if changed                                          */
/* reset min and max in seconds,                                              */
/* plot start, end, span and max,                                             */
/* y time units scale and,                                                    */
/* y plotting scale                                                           */
    if (paramsAltered.timeUnitsChanged)
    {
	configParams.yVariable.timeUnits = paramsAltered.newTimeUnits;

    	if (configParams.items[Y] == TIME)
    	{
					/* reset min and max in seconds       */
            if (configParams.yVariable.timeUnits == SECONDS)
            {
                minInSecs = configParams.yVariable.minValue;
                maxInSecs = configParams.yVariable.maxValue;
            }
            else if (configParams.yVariable.timeUnits == MINUTES)
            {
                minInSecs = configParams.yVariable.minValue *
			    (Flt64) SECONDS_PER_MINUTE;
                maxInSecs = configParams.yVariable.maxValue *
			    (Flt64) SECONDS_PER_MINUTE;
            }
            else    /* configParams.yVariable.timeUnits == HOURS */
            {
                minInSecs = configParams.yVariable.minValue *
			    (Flt64) SECONDS_PER_HOUR;
                maxInSecs = configParams.yVariable.maxValue *
			    (Flt64) SECONDS_PER_HOUR;
            }

					/* reset plot start, end, span and max*/
            plotSpan = (maxInSecs - minInSecs) * DATA_UPDATE_RATE;
            if (pointCount >= 0)
            {
                if (dataValues[0][TIME] >= minInSecs)
                {
                    plotStart = 0;
                    if (dataValues[0][TIME] >= maxInSecs)
                    {
                        plotMax = 0;
	  	        if (!dialog)		/* display warning message    */
			{
	    		    dialog = XmCreateWarningDialog(w, "dialog", NULL,
                                                           0);
	    		    XtVaSetValues(dialog, XtVaTypedArg,
                                          XmNmessageString, XmRString,
			                  TIME_MESSAGE, strlen(TIME_MESSAGE)+1,
                                          NULL);
			}
			XtManageChild(dialog);
                    }
                    else
                        plotMax = (maxInSecs - dataValues[0][TIME]) *
                                  DATA_UPDATE_RATE;
                }
                else
                {
                    plotStart = (minInSecs - dataValues[0][TIME]) *
                                DATA_UPDATE_RATE;
                    plotMax = plotStart + plotSpan;
                }
            	if (pointCount <= plotStart)
                    plotEnd = plotStart;
            	else if (pointCount <= plotMax)
                    plotEnd = pointCount;
            	else
                    plotEnd = plotMax;
	    	if (plotEnd < plotMax)
		    plotScrolling = FALSE;
            }
	    else
	    {
                plotStart = -1;
                plotEnd = plotStart;
                plotMax = plotStart;
	    }

					/* reset y time units scale           */
            if (configParams.yVariable.timeUnits == SECONDS)
                yTimeUnitsScale = 1.0;  /* seconds per second                 */
            else if (configParams.yVariable.timeUnits == MINUTES)
                yTimeUnitsScale = SECONDS_PER_MINUTE;
            else if (configParams.yVariable.timeUnits == HOURS)
                yTimeUnitsScale = SECONDS_PER_HOUR;

					/* reset y plotting scale             */
            plotParams->displayData.scales[Y] = plotParams->yAxisLength /
                ( (plotParams->displayData.maxValues[Y] -
		   plotParams->displayData.minValues[Y]) *
                   yTimeUnitsScale * DATA_UPDATE_RATE );
	    rescale = TRUE;
    	}

	if (XtIsRealized(plotParams->displayData.display))
	    XClearArea(XtDisplay(plotParams->displayData.display),
		       XtWindow(plotParams->displayData.display), 0, 0, 0, 0,
		       TRUE);

	paramsAltered.timeUnitsChanged = FALSE;
    }

    XtPopdown(XtParent(yForm));

}  /* yApplyCallback */


/******************************************************************************/
/* Function : yCancelAllCallback                                              */
/* Purpose  : Cancels all changes to a y item configuration parameters.       */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
yCancelAllCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    char value[STRING_LENGTH];

/* reset axis label if changed                                                */
    if (paramsAltered.labelChanged)
    {
	XmTextReplace(paramsAltered.labelWidget, 0, STRING_LENGTH,
		      paramsAltered.oldLabel);
	paramsAltered.labelChanged = FALSE;
    }

/* reset minimum value if changed                                             */
    if (paramsAltered.minChanged)
    {
	sprintf(value, "%f", paramsAltered.oldMin);
	XmTextReplace(paramsAltered.minWidget, 0, STRING_LENGTH, value);
	paramsAltered.minChanged = FALSE;
    }

/* reset maximum value if changed                                             */
    if (paramsAltered.maxChanged)
    {
	sprintf(value, "%f", paramsAltered.oldMax);
	XmTextReplace(paramsAltered.maxWidget, 0, STRING_LENGTH, value);
	paramsAltered.maxChanged = FALSE;
    }

/* reset major divisions value if changed                                     */
    if (paramsAltered.divisionsChanged)
    {
	sprintf(value, "%d", paramsAltered.oldDivisions);
	XmTextReplace(paramsAltered.divisionsWidget, 0, STRING_LENGTH, value);
	paramsAltered.divisionsChanged = FALSE;
    }

/* reset time units if changed                                                */
/* doesn't work because can't set unitsPulldownMenu widget menuHistory 
    if (paramsAltered.timeUnitsChanged)
    {
	XtVaSetValues(paramsltered.timeUnitsWidget, XmNmenuHistory,
		      paramsAltered.oldTimeUnitsButton, NULL);
	paramsAltered.timeUnitsChanged = FALSE;
    }
*/

}  /* yCancelAllCallback */


/******************************************************************************/
/* Function : yCancelLastCallback                                             */
/* Purpose  : Cancels the last change to a y item configuration parameter.    */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
yCancelLastCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    char value[STRING_LENGTH];

    switch (paramsAltered.lastParam)
    {
	case LABEL :			/* reset axis label if changed        */
    	    if (paramsAltered.labelChanged)
    	    {
		XmTextReplace(paramsAltered.labelWidget, 0, STRING_LENGTH,
		              paramsAltered.oldLabel);
		paramsAltered.labelChanged = FALSE;
    	    }
	    break;

	case MIN_VALUE :		 /* reset minimum value if changed    */
    	    if (paramsAltered.minChanged)
    	    {
	        sprintf(value, "%f", paramsAltered.oldMin);
	        XmTextReplace(paramsAltered.minWidget, 0, STRING_LENGTH, value);
		paramsAltered.minChanged = FALSE;
    	    }
	    break;

	case MAX_VALUE :		 /* reset maximum value if changed    */
    	    if (paramsAltered.maxChanged)
    	    {
		sprintf(value, "%f", paramsAltered.oldMax);
		XmTextReplace(paramsAltered.maxWidget, 0, STRING_LENGTH, value);
		paramsAltered.maxChanged = FALSE;
    	    }
	    break;

	case MAJOR_DIVISIONS :			/* reset major divisions value*/
    	    if (paramsAltered.divisionsChanged) /* if changed                 */
    	    {
		sprintf(value, "%d", paramsAltered.oldDivisions);
		XmTextReplace(paramsAltered.divisionsWidget, 0, STRING_LENGTH,
                              value);
		paramsAltered.divisionsChanged = FALSE;
    	    }
	    break;

	case TIME_UNITS :			/* reset time units if changed*/
/* doesn't work because can't set unitsPulldownMenu widget menuHistory 
    	    if (paramsAltered.timeUnitsChanged)
    	    {
		XtVaSetValues(paramsltered.timeUnitsWidget, XmNmenuHistory,
		              paramsAltered.oldTimeUnitsButton, NULL);
		paramsAltered.timeUnitsChanged = FALSE;
    	    }
*/
	    break;

    }

}  /* yCancelLastCallback */


/******************************************************************************/
/* Function : yDivisionsCallback                                              */
/* Purpose  : Changes the y item number of major divisions.                   */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
yDivisionsCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    static Widget  dialog = NULL;
    char           value[STRING_LENGTH];
    Int16          var;
    Int16          divs;

    divs = atoi(XmTextFieldGetString(w));

    if (divs < 1)
    {
/* replace new value with previous value                                      */
	sprintf(value, "%d", plotParams->majorDivisions[Y]);
	XmTextReplace(w, 0, STRING_LENGTH, value);

/* display warning message                                                    */
	if (!dialog)
	{
	    dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			  DIVS_MESSAGE, strlen(DIVS_MESSAGE)+1, NULL);
	}
	XtManageChild(dialog);
    }
    else
    {
	paramsAltered.lastParam = MAJOR_DIVISIONS;
	paramsAltered.divisionsWidget = w;
	paramsAltered.divisionsChanged = TRUE;
	paramsAltered.newDivisions = divs;
	paramsAltered.oldDivisions = plotParams->majorDivisions[Y];
    }

}  /* yDivisionsCallback */


/******************************************************************************/
/* Function : yItemCallback                                                   */
/* Purpose  : Sets the y item variable list.                                  */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
yItemCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    XmToggleButtonCallbackStruct *toggle =
	(XmToggleButtonCallbackStruct *) callData;
    Int16    item, var;
    XmString xmString[NUM_DISPLAY_ITEMS];

    if (toggle->set)
    {
	yItemListed = TRUE;

    	for (item = 0; item < NUM_DISPLAY_ITEMS; item++)
            xmString[item] = XmStringCreateLtoR(listData.displayItems[item],
					        XmFONTLIST_DEFAULT_TAG);

	if (xItemsListed)
	{
    	    for (var = 0; var < configParams.numXVariables; var++)
	        XmListDeselectItem(listData.w,
				   xmString[configParams.items[var]]);
	    xItemsListed = FALSE;
	}
	else if (statusItemsListed)
	{
#if DEBUG_ON
	printf("deselecting status items  ");
#endif
    	    for (var = 0; var < configParams.numStatusLineVariables; var++)
#if DEBUG_ON
	    {
	        printf("%d  ", configParams.statusLineItems[var]);
#endif
	        XmListDeselectItem(listData.w,
				   xmString[configParams.statusLineItems[var]]);
#if DEBUG_ON
	    }
	printf("\n");
#endif
	    statusItemsListed = FALSE;
	}

#if DEBUG_ON
    printf("selecting y  %d\n", configParams.items[Y]);
#endif
    	XmListSelectItem(listData.w, xmString[configParams.items[Y]], FALSE);

    	for (item = 0; item < NUM_DISPLAY_ITEMS; item++)
	    XmStringFree(xmString[item]);
    }

}  /* yItemCallback */


/******************************************************************************/
/* Function : yLabelCallback                                                  */
/* Purpose  : Changes the y item axis label.                                  */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
yLabelCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    plotParameters *plotParams = (plotParameters *) clientData;

    strcpy(paramsAltered.newLabel, XmTextFieldGetString(w));

    paramsAltered.lastParam = LABEL;
    paramsAltered.labelWidget = w;
    paramsAltered.labelChanged = TRUE;
    strcpy(paramsAltered.oldLabel, plotParams->axisLabel[Y]);

}  /* yLabelCallback */


/******************************************************************************/
/* Function : dotsPlotCallback                                                */
/* Purpose  : Sets dots drawing mode.                                         */
/* Inputs   : Widget, display data and call data.                             */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
dotsPlotCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    displayUpdateData            *data = (displayUpdateData *) clientData;
    XmToggleButtonCallbackStruct *toggle =
	(XmToggleButtonCallbackStruct *) callData;
    Display                      *display = XtDisplay(data->display);
    Drawable                     drawable = XtWindow(data->display);

    if (toggle->set)
	configParams.lineDrawing = FALSE;

    if (stopAcquisition)
	XClearArea(display, drawable, 0, 0, 0, 0, TRUE);
    else
    	restartDisplay = TRUE;

}  /* dotsPlotCallback */


/******************************************************************************/
/* Function : yMaxCallback                                                    */
/* Purpose  : Changes the y item maximum value.                               */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
yMaxCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    static Widget  dialog = NULL;
    char           value[STRING_LENGTH];
    Flt32          max;
    Flt32          min;
    Flt32          newMaxInSecs;

    max = atof(XmTextFieldGetString(w));

    if (paramsAltered.minChanged)
	min = paramsAltered.newMin;
    else
	min = configParams.yVariable.minValue;

/* check that max is not less than min                                        */
    if (max <= min)
    {
/* replace new value with previous value                                      */
	sprintf(value, "%f", configParams.yVariable.maxValue);
	XmTextReplace(w, 0, STRING_LENGTH, value);

/* display warning message                                                    */
	if (!dialog)
	{
	    dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			  MAX_MESSAGE, strlen(MAX_MESSAGE)+1, NULL);
	}
	XtManageChild(dialog);
    }
    else
    {

/* check that max (if time) is not before first value                         */
	if (configParams.items[Y] == TIME)
	{
    	    if (configParams.yVariable.timeUnits == SECONDS)
    	        newMaxInSecs = max;
    	    else if (configParams.yVariable.timeUnits == MINUTES)
    	        newMaxInSecs = max * (Flt64) SECONDS_PER_MINUTE;
    	    else    /* configParams.yVariable.timeUnits == HOURS */
    	        newMaxInSecs = max * (Flt64) SECONDS_PER_HOUR;

    	    if (dataValues[0][TIME] >= newMaxInSecs)
    	    {
/* replace new value with previous value                                      */
	        sprintf(value, "%f", configParams.yVariable.maxValue);
	        XmTextReplace(w, 0, STRING_LENGTH, value);

/* display warning message                                                    */
	        if (!dialog)
	        {
	            dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	            XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString,
				  XmRString, Y_MAX_MESSAGE,
				  strlen(Y_MAX_MESSAGE)+1, NULL);
	        }
	        XtManageChild(dialog);
            }
            else
            {
	        paramsAltered.lastParam = MAX_VALUE;
	        paramsAltered.maxWidget = w;
	        paramsAltered.maxChanged = TRUE;
	        paramsAltered.newMax = max;
	        paramsAltered.oldMax = configParams.yVariable.maxValue;
	    }
	}

        else
        {
	    paramsAltered.lastParam = MAX_VALUE;
	    paramsAltered.maxWidget = w;
	    paramsAltered.maxChanged = TRUE;
	    paramsAltered.newMax = max;
	    paramsAltered.oldMax = configParams.yVariable.maxValue;
        }
    }

}  /* yMaxCallback */


/******************************************************************************/
/* Function : yMinCallback                                                    */
/* Purpose  : Changes the y item minimum value.                               */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
yMinCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    static Widget  dialog = NULL;
    char           value[STRING_LENGTH];
    Flt32          min;
    Flt32          max;

    min = atof(XmTextFieldGetString(w));

    if (paramsAltered.maxChanged)
	max = paramsAltered.newMax;
    else
	max = configParams.yVariable.maxValue;

    if (min >= max)
    {
/* replace new value with previous value                                      */
	sprintf(value, "%f", configParams.yVariable.minValue);
	XmTextReplace(w, 0, STRING_LENGTH, value);

/* display warning message                                                    */
	if (!dialog)
	{
	    dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			  MIN_MESSAGE, strlen(MIN_MESSAGE)+1, NULL);
	}
	XtManageChild(dialog);
    }
    else
    {
	paramsAltered.lastParam = MIN_VALUE;
	paramsAltered.minWidget = w;
	paramsAltered.minChanged = TRUE;
	paramsAltered.newMin = min;
	paramsAltered.oldMin = configParams.yVariable.minValue;
    }

}  /* yMinCallback */


/******************************************************************************/
/* Function : colourCallback                                                  */
/* Purpose  : Changes the x item plotting colour.                             */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
colourCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    XmRowColumnCallbackStruct *menu = (XmRowColumnCallbackStruct *) callData;
    plotParameters            *plotParams = (plotParameters *) clientData;
    Widget                    menuShell = XtParent(w);
    Widget                    parameterInputRowColumn = XtParent(menuShell);
    Widget                    xParamsForm = XtParent(parameterInputRowColumn);
    Widget                    parametersFrame = XtParent(xParamsForm);
    Widget                    thisXForm = XtParent(parametersFrame);
    static Widget             dialog = NULL;
    Display                   *display = XtDisplay(w);
    char                      widgetName[STRING_LENGTH];
    char                      colourName[STRING_LENGTH];
    Int16                     i, var;
    Int16                     screen = DefaultScreen(display);
    Colormap                  colourMap = DefaultColormap(display, screen);
    XColor                    colour, ignore;

/* determine which x item variable                                            */
    for (var = 0; var < configParams.numXVariables; var++)
	if (thisXForm == xForm[var])
	    break;

/* get the label of the colour push button selected                           */
    strcpy(widgetName, XtName(menu->widget));
    if (!strcmp(widgetName, "redPushButton"))
	strcpy(colourName, "red");
    else if (!strcmp(widgetName, "greenPushButton"))
	strcpy(colourName, "green");
    else if (!strcmp(widgetName, "bluePushButton"))
    	strcpy(colourName, "blue");
    else if (!strcmp(widgetName, "cyanPushButton"))
    	strcpy(colourName, "cyan");
    else if (!strcmp(widgetName, "yellowPushButton"))
    	strcpy(colourName, "yellow");
    else if (!strcmp(widgetName, "magentaPushButton"))
    	strcpy(colourName, "magenta");
    else if (!strcmp(widgetName, "whitePushButton"))
    	strcpy(colourName, "white");

    if (XAllocNamedColor(display, colourMap, colourName, &colour, &ignore))
    {
	paramsAltered.newColour = colour.pixel;
	strcpy(paramsAltered.newColourName, "");
	i = 0;
	while (colourName[i] != '\0')
	{
	    colourName[i] = toupper(colourName[i]);
	    i++;
	}
	strcpy(paramsAltered.newColourName, colourName);
    	paramsAltered.lastParam = COLOUR;
    	paramsAltered.colourWidget = w;
    	paramsAltered.colourChanged = TRUE;
    	paramsAltered.oldColour = plotParams->displayData.colours[var];
	strcpy(paramsAltered.oldColourName,
	       configParams.xVariables[var].colourName);
	XtVaGetValues(w, XmNmenuHistory, &paramsAltered.oldColourButton, NULL);
    }
    else				/* display warning message            */
    {
	if (!dialog)
	{
	    dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	    XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
			  COLOUR_MESSAGE, strlen(COLOUR_MESSAGE)+1, NULL);
	}
	XtManageChild(dialog);
    }

}  /* colourCallback */


/******************************************************************************/
/* Function : timeUnitsCallback                                               */
/* Purpose  : Changes the y item time units.                                  */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
timeUnitsCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    XmRowColumnCallbackStruct *menu = (XmRowColumnCallbackStruct *) callData;
    char                      widgetName[STRING_LENGTH];

/* get the label of the units push button selected                            */
    strcpy(widgetName, XtName(menu->widget));
    if (!strcmp(widgetName, "secondsPushButton"))
	paramsAltered.newTimeUnits = SECONDS;
    else if (!strcmp(widgetName, "minutesPushButton"))
	paramsAltered.newTimeUnits = MINUTES;
    else if (!strcmp(widgetName, "hoursPushButton"))
	paramsAltered.newTimeUnits = HOURS;

    paramsAltered.lastParam = TIME_UNITS;
    paramsAltered.timeUnitsWidget = w;
    paramsAltered.timeUnitsChanged = TRUE;
    paramsAltered.oldTimeUnits = configParams.yVariable.timeUnits;
    XtVaGetValues(w, XmNmenuHistory, &paramsAltered.oldTimeUnitsButton, NULL);

}  /* timeUnitsCallback */


/******************************************************************************/
/* Function : numericVerifyCallback                                           */
/* Purpose  : Verifies numeric input.                                         */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
numericVerifyCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    XmTextVerifyCallbackStruct *textVerify =
			       (XmTextVerifyCallbackStruct *) callData;

    char  *string;
    Int16 i;

    if (textVerify->text->ptr)
    {
	string = textVerify->text->ptr;
	for (i = 0; i < textVerify->text->length; i++)
	    if ( (!isdigit(string[i])) &&
		 ( (string[i] != '.') && (string[i] != '-') ) )
		textVerify->doit = FALSE;
    }

}  /* numericVerify */


/******************************************************************************/
/* Function : printCallback                                                   */
/* Purpose  : Prints the graphics display window.                             */
/* Inputs   : Widget, plotting data and call data.                            */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
printCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    plotParameters *plotParams = (plotParameters *) clientData;
    char           printString[AXIS_LABEL_LENGTH];
    Window         windowId = XtWindow(plotParams->displayData.display);

/* original xpr format
    sprintf(printString, "xwd -id %ld | xpr -device ljet -cutoff 10.0 | lp",
*/

    sprintf(printString, "xwd -id %ld | xpr -device ps -gray 2 | lp",
	    windowId);
    system(printString);

}  /* printCallback */


/******************************************************************************/
/* Function : saveCallback                                                    */
/* Purpose  : Saves the graphics configuration parameters to a file.          */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
saveCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    static Widget dialog = NULL;		/* dialog widget              */
    XmString pattern = XmStringCreateLtoR("*.cfg*", XmFONTLIST_DEFAULT_TAG);

/* get the save file name                                                     */
    dialog = XmCreateFileSelectionDialog(w, "saveFileDialog", NULL, 0);
    XtVaSetValues(dialog, XmNpattern, pattern, NULL);

    XtAddCallback(dialog, XmNokCallback, saveFileOkCallback, NULL);
    XtAddCallback(dialog, XmNcancelCallback, saveFileCancelCallback, NULL);

    XtManageChild(dialog);

    XmStringFree(pattern);

}  /* saveCallback */


/******************************************************************************/
/* Function : saveFileOkCallback                                              */
/* Purpose  : Writes the graphics configuration parameters to a file.         */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
saveFileOkCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    XmFileSelectionBoxCallbackStruct *fileSelect = 
	(XmFileSelectionBoxCallbackStruct *) callData;
    static Widget dialog = NULL;		/* dialog widget              */
    FILE          *fpw;				/* file pointer               */
    char          *saveFilename;		/* save file name             */
    char          *saveMessage;			/* save message               */
    char          *dirName;			/* directory name             */
    char          filename[STRING_LENGTH];	/* file name                  */
    Int16         var;				/* loop counter               */

    XtUnmanageChild(w);

    XmStringGetLtoR(fileSelect->value, XmFONTLIST_DEFAULT_TAG, &saveFilename);

/* open the save file                                                         */
    if ( (fpw = fopen(saveFilename, "w")) == NULL)
    {
	sprintf(saveMessage, "Could not open file %s", saveFilename);
	dialog = XmCreateWarningDialog(w, "dialog", NULL, 0);
	XtVaSetValues(dialog, XtVaTypedArg, XmNmessageString, XmRString,
		      saveMessage, strlen(saveMessage)+1, NULL);
	XtManageChild(dialog);
    }
    else
    {
	XmStringGetLtoR(fileSelect->dir, XmFONTLIST_DEFAULT_TAG, &dirName);
	strcpy(filename, saveFilename+strlen(dirName));
    	fprintf(fpw, "FILENAME                %s\n", filename);
	fprintf(fpw, "MARK_FILENAME           %s\n", configParams.markFilename);
	fprintf(fpw, "GRAPHICS_DISPLAY_RATE   %-4.2f\n",
		configParams.graphicsDisplayRate);
	fprintf(fpw, "STATUS_LINE_RATE        %-4.2f\n",
                configParams.statusLineRate);
	if (configParams.lineDrawing)
	    fprintf(fpw, "PLOT_STYLE              LINES\n");
	else
	    fprintf(fpw, "PLOT_STYLE              DOTS\n");
	if (configParams.showXY)
	    fprintf(fpw, "SHOW_XY                 ON\n");
	else
	    fprintf(fpw, "SHOW_XY                 OFF\n");
	if (configParams.scrolling)
	    fprintf(fpw, "SCROLL                  ON\n");
	else
	    fprintf(fpw, "SCROLL                  OFF\n");
	fprintf(fpw, "STATUS_LINE_VARIABLES   ");
	for (var = 0; var < configParams.numStatusLineVariables; var++)
	    fprintf(fpw, "%s ", toItemName(configParams.statusLineItems[var]));
	fprintf(fpw,"\n");
	fprintf(fpw, "NUMBER_X_VARIABLES      %d\n",
		configParams.numXVariables);
	fprintf(fpw, "Y_VARIABLE              %s\n",
		toItemName(configParams.items[Y]));
	fprintf(fpw, "Y_LABEL                 %s\n",
		configParams.yVariable.axisLabel);
	fprintf(fpw, "Y_MIN_VALUE             %-8.4f\n",
		configParams.yVariable.minValue);
	fprintf(fpw, "Y_MAX_VALUE             %-8.4f\n",
		configParams.yVariable.maxValue);
	fprintf(fpw, "Y_MAJOR_DIVISIONS       %d\n",
		configParams.yVariable.majorDivisions);
	fprintf(fpw, "Y_TIME_UNITS            %s\n",
		toUnitsName(configParams.yVariable.timeUnits));
	fprintf(fpw, "X1_VARIABLE             %s\n",
		toItemName(configParams.items[X1]));
	fprintf(fpw, "X1_LABEL                %s\n",
		configParams.xVariables[X1].axisLabel);
	fprintf(fpw, "X1_MIN_VALUE            %-8.4f\n",
		configParams.xVariables[X1].minValue);
	fprintf(fpw, "X1_MAX_VALUE            %-8.4f\n",
		configParams.xVariables[X1].maxValue);
	fprintf(fpw, "X1_MAJOR_DIVISIONS      %d\n",
		configParams.xVariables[X1].majorDivisions);
	fprintf(fpw, "X1_COLOR                %s\n",
		toColourName(configParams.xVariables[X1].colour));
	fprintf(fpw, "X2_VARIABLE             %s\n",
		toItemName(configParams.items[X2]));
	fprintf(fpw, "X2_LABEL                %s\n",
		configParams.xVariables[X2].axisLabel);
	fprintf(fpw, "X2_MIN_VALUE            %-8.4f\n",
		configParams.xVariables[X2].minValue);
	fprintf(fpw, "X2_MAX_VALUE            %-8.4f\n",
		configParams.xVariables[X2].maxValue);
	fprintf(fpw, "X2_MAJOR_DIVISIONS      %d\n",
		configParams.xVariables[X2].majorDivisions);
	fprintf(fpw, "X2_COLOR                %s\n",
		toColourName(configParams.xVariables[X2].colour));
	fprintf(fpw, "X3_VARIABLE             %s\n",
		toItemName(configParams.items[X3]));
	fprintf(fpw, "X3_LABEL                %s\n",
		configParams.xVariables[X3].axisLabel);
	fprintf(fpw, "X3_MIN_VALUE            %-8.4f\n",
		configParams.xVariables[X3].minValue);
	fprintf(fpw, "X3_MAX_VALUE            %-8.4f\n",
		configParams.xVariables[X3].maxValue);
	fprintf(fpw, "X3_MAJOR_DIVISIONS      %d\n",
		configParams.xVariables[X3].majorDivisions);
	fprintf(fpw, "X3_COLOR                %s\n",
		toColourName(configParams.xVariables[X3].colour));
	fprintf(fpw, "X4_VARIABLE             %s\n",
		toItemName(configParams.items[X4]));
	fprintf(fpw, "X4_LABEL                %s\n",
		configParams.xVariables[X4].axisLabel);
	fprintf(fpw, "X4_MIN_VALUE            %-8.4f\n",
		configParams.xVariables[X4].minValue);
	fprintf(fpw, "X4_MAX_VALUE            %-8.4f\n",
		configParams.xVariables[X4].maxValue);
	fprintf(fpw, "X4_MAJOR_DIVISIONS      %d\n",
		configParams.xVariables[X4].majorDivisions);
	fprintf(fpw, "X4_COLOR                %s\n",
		toColourName(configParams.xVariables[X4].colour));
	fprintf(fpw, "\n\nplot style options are :\nLINES\nDOTS\n");
	fprintf(fpw, "\nx, y and status line variable options are :\n");
	fprintf(fpw, "BEAM_ATTENUATION\n");
	fprintf(fpw, "BEAM_PERCENT\n");
	fprintf(fpw, "CONDUCTIVITY\n");
	fprintf(fpw, "DEPTH\n");
	fprintf(fpw, "OXYGEN\n");
	fprintf(fpw, "OXYGEN_CURRENT\n");
	fprintf(fpw, "OXYGEN_TEMP\n");
	fprintf(fpw, "PRESSURE\n");
	fprintf(fpw, "SALINITY\n");
	fprintf(fpw, "SCAN_NUMBER\n");
	fprintf(fpw, "SIGMA_T\n");
	fprintf(fpw, "SIGMA_THETA\n");
	fprintf(fpw, "SOUND_VELOCITY\n");
	fprintf(fpw, "TEMPERATURE\n");
	fprintf(fpw, "THETA\n");
	fprintf(fpw, "TIME\n");
	fprintf(fpw, "TRANSMISSION\n");
	fprintf(fpw, "\nvariable color options are\n");
	fprintf(fpw, "RED\nBLUE\nGREEN\nCYAN\nMAGENTA\nYELLOW\nWHITE\n");
	fprintf(fpw, "\ny time units options are :\n");
	fprintf(fpw, "HOURS\nMINUTES\nSECONDS\n");
    }

    fclose(fpw);

}  /* saveFileOkCallback */


/******************************************************************************/
/* Function : saveFileCancelCallback                                          */
/* Purpose  : Removes the save file selector dialog from the screen.          */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
saveFileCancelCallback(Widget w, XtPointer clientData, XtPointer callData)
{

    XtUnmanageChild(w);

}  /* saveFileCancelCallback */


/******************************************************************************/
/* Function : toItemsName                                                     */
/* Purpose  : Translates item variable to an item name.                       */
/* Inputs   : Item variable.                                                  */
/* Outputs  : Item name string.                                               */
/******************************************************************************/
    char
*toItemName(itemVariables item)
{

    switch (item)
    {
	case BEAM_ATTENUATION :
	    return("BEAM_ATTENUATION");

	case BEAM_PERCENT :
	    return("BEAM_PERCENT");

	case CONDUCTIVITY :
	    return("CONDUCTIVITY");

	case DEPTH :
	    return("DEPTH");

	case OXYGEN :
	    return("OXYGEN");

	case OXYGEN_CURRENT :
	    return("OXYGEN_CURRENT");

	case OXYGEN_TEMP :
	    return("OXYGEN_TEMP");

	case PRESSURE :
	    return("PRESSURE");

	case SALINITY :
	    return("SALINITY");

	case SCAN_NUMBER :
	    return("SCAN_NUMBER");

	case SIGMA_T :
	    return("SIGMA_T");

	case SIGMA_THETA :
	    return("SIGMA_THETA");

	case SOUND_VELOCITY :
	    return("SOUND_VELOCITY");

	case TEMPERATURE :
	    return("TEMPERATURE");

	case THETA :
	    return("THETA");

	case TIME :
	    return("TIME");

	case TRANSMISSION :
	    return("TRANSMISSION");
    }
    return("NO_VAR");

}  /* toItemName */


/******************************************************************************/
/* Function : toUnitsName                                                     */
/* Purpose  : Translates time units to a time units name.                     */
/* Inputs   : Time units.                                                     */
/* Outputs  : Time units name string.                                         */
/******************************************************************************/
    char
*toUnitsName(unitsOfTime timeUnits)
{

    switch (timeUnits)
    {
	case HOURS :
	    return("HOURS");

	case MINUTES :
	    return("MINUTES");

	case SECONDS :
	    return("SECONDS");

    }
    return("NO_UNITS");

}  /* toUnitsName */


/******************************************************************************/
/* Function : toColourName                                                    */
/* Purpose  : Translates a display colour to a colour name.                   */
/* Inputs   : Display colour.                                                 */
/* Outputs  : Colour name string.                                             */
/******************************************************************************/
    char
*toColourName(displayColours colour)
{

    switch (colour)
    {
	case RED :
	    return("RED");

	case GREEN :
	    return("GREEN");

	case BLUE :
	    return("BLUE");

	case CYAN :
	    return("CYAN");

	case YELLOW :
	    return("YELLOW");

	case MAGENTA :
	    return("MAGENTA");

	case WHITE :
	    return("WHITE");

    }
    return("NO_COLOUR");

}  /* toColourName */


/******************************************************************************/
/* Function : statusItemCallback                                              */
/* Purpose  : Sets the status items list.                                     */
/* Inputs   : Widget, client data and call data.                              */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
statusItemsCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    XmToggleButtonCallbackStruct *toggle =
	(XmToggleButtonCallbackStruct *) callData;
    Int16    item, var;
    XmString xmString[NUM_DISPLAY_ITEMS];

    if (toggle->set)
    {
	statusItemsListed = TRUE;

    	for (item = 0; item < NUM_DISPLAY_ITEMS; item++)
            xmString[item] = XmStringCreateLtoR(listData.displayItems[item],
					        XmFONTLIST_DEFAULT_TAG);

	if (xItemsListed)
	{
	    for (var = 0; var < configParams.numXVariables; var++)
		XmListDeselectItem(listData.w,
				   xmString[configParams.items[var]]);
	    xItemsListed = FALSE;
	}
	else if (yItemListed)
	{
    	    XmListDeselectItem(listData.w, xmString[configParams.items[Y]]);
	    yItemListed = FALSE;
	}

#if DEBUG_ON
    printf("selecting status ");
#endif
    	for (var = 0; var < configParams.numStatusLineVariables; var++)
#if DEBUG_ON
	{
	    printf("%d  ", configParams.statusLineItems[var]);
#endif
	    XmListSelectItem(listData.w,
			     xmString[configParams.statusLineItems[var]],
			     FALSE);
#if DEBUG_ON
    	}
    	printf("\n");
#endif

    	for (item = 0; item < NUM_DISPLAY_ITEMS; item++)
	    XmStringFree(xmString[item]);
    }

}  /* statusItemsCallback */
