
/* include files */
#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/IntrinsicP.h>
#include <At/FontFamily.h>
#include <At/Text.h> 
#include "StripchartP.h"
#include "double.h"

/** defaults defined **/
#define DEFAULT_FONT_SZ         AtFontMEDIUM
#define DEFAULT_FONT_FAMILY     "new century schoolbook"
#define DEFAULT_DIV_SZ		10
#define DEFAULT_MARGIN_HT	2
#define DEFAULT_MARGIN_WD	2
#define DEFAULT_SHIFT		50
#define DEFAULT_SMP_INTERVAL	1
#define DEFAULT_TIMER_INTVL	1000
#define DEFAULT_X_DIV_UNTS	1
#define DEFAULT_Y_DIV_UNTS	1

#define MIN_CLIK_MV		4
#define GRID_LN_LEN             6
#define IMPOSSIBLE_VALUE        -1

#define selectNONE		0
#define selectPLOT		1
#define selectPEN		2


extern void CvtStringToDouble();

/* default translation table and action list */


/* declaration of some functions that we will need later          */
static void ClassInitialize();
static void Initialize();
static void Destroy();
static void Resize();
static void Redisplay();
static Boolean SetValues();

/* other private procedures */
static void DrawGridLines();
static void CalcGrid();
/*static void UpdateWidget(); */

/** debugging **/
static void ListSegments();

/** actions **/
static void SelectAction();
static void RubberBand();
static void DeselectAction();


static char defaultTranslations[] = 
   "<Btn1Down>:         Select() \n\
    <Btn1Motion>:	RubberBand() \n\
    <Btn1Up>:		EndSelect()";

static XtActionsRec actions[] =
{
  {"Select", (XtActionProc) SelectAction},
  {"RubberBand", (XtActionProc) RubberBand},
  {"EndSelect", (XtActionProc) DeselectAction},
};


#define offset(field) XtOffset(AtStripchartWidget,stripchart.field)

/* declares the defaults */
static XtResource resources[] = {
/*  {XtNfontSize, XtCFontSize, AtFontSize, sizeof(caddr_t),
    offset(ft_size), XtRImmediate, DEFAULT_FONT_SZ},
*/   {XtNfontFamily, XtCFontFamily, XtRString, sizeof(String),
    offset(font), XtRString, "new century schoolbook"}, 
  {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
       offset(foreground), XtRString, "Black"},
  {XtNforceSquare, XtCForceSquare, XtRBoolean, sizeof(Boolean),
     offset(force_square), XtRString, "TRUE"},
  {XtNtimerOn, XtCTimerOn, XtRBoolean, sizeof(Boolean),
     offset(timer_on), XtRString, "FALSE"},
  {XtNmarginHeight, XtCMarginHeight, XtRInt, sizeof(int),
     offset(margin_height), XtRString, "2"}, /* */
  {XtNmarginWidth, XtCMarginWidth, XtRInt, sizeof(int),
     offset(margin_width), XtRString, "2"}, /* */
/*  {XtNpercentShift, XtCPercentShift, XtRInt, sizeof(int),
     offset(percent_shift), XtRImmediate, DEFAULT_SHIFT},
  {XtNsampleInterval, XtCSampleInterval, XtRInt, sizeof(int),
     offset(sample_interval), XtRImmediate, DEFAULT_SMP_INTERVAL},
  {XtNtimerInterval, XtCSampleInterval, XtRInt, sizeof(int),
     offset(timer_interval),  XtRImmediate, DEFAULT_TIMER_INTVL},
*/  {XtNxDivisions, XtCDivisions, XtRInt, sizeof(int), 
     offset(xdivs), XtRString, "-1"}, /* */
  {XtNyDivisions, XtCDivisions, XtRInt, sizeof(int), 
     offset(ydivs), XtRString, "-1"}, /* */
  {XtNxDivisionSize, XtCDivisionSize, XtRInt, sizeof(int),
     offset(xdivmm), XtRString, "-1"}, /* */
  {XtNyDivisionSize, XtCDivisionSize, XtRInt, sizeof(int),
     offset(ydivmm), XtRString, "-1"}, /* */
  {XtNxUnitsPerDivision, XtCUnitsPerDivision, XtRDouble, sizeof(double),
     offset(xdiv_units),  XtRString, "1"}, /* */
  {XtNyUnitsPerDivision, XtCUnitsPerDivision, XtRDouble, sizeof(double),
     offset(ydiv_units),  XtRString, "1"}, /* */
  {XtNxUnits, XtCUnits, XtRString, sizeof(String),
     offset(xunits), XtRString, NULL},
  {XtNyUnits, XtCUnits, XtRString, sizeof(String),
     offset(yunits), XtRString, NULL},
  {XtNclickCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
     offset(click_callback), XtRCallback, NULL},
  {XtNdragCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
     offset(drag_callback), XtRCallback, NULL},
  {XtNdragGripperCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
     offset(drag_grip_callback), XtRCallback, NULL},
  {XtNselectCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
     offset(select_callback), XtRCallback, NULL},
};

#undef offset

#define offset(field) XtOffset(AtStripchartConstraints,field)
static XtResource constraint_resources[] = {
{XtNbaseline, XtCBaseline, XtRDouble,
       sizeof(double), offset(stripchart.baseline),
       XtRString, "0.0"}
};
#undef offset


AtStripchartClassRec atStripchartClassRec = {
  { /* core fields    */
#define Superclass                      (&constraintClassRec)
    /* superclass               */	(WidgetClass) Superclass,
    /* class_name               */	"AtStripchart",
    /* Size                     */	sizeof(AtStripchartRec),
    /* class_initialize         */	ClassInitialize,  
    /* class_part_initialize    */	NULL,
    /* class_inited             */	FALSE,
    /* initialize               */	Initialize,
    /* initialize_hook          */	NULL,
    /* realize                  */	XtInheritRealize,
    /* actions                  */	actions,
    /* num_actions              */	XtNumber(actions),
    /* resources                */	resources,
    /* num_resources            */	XtNumber(resources),
    /* xrm_class		*/	NULL,
    /* compress_motion          */	TRUE,
    /* compress_exposure        */	TRUE,
    /* compress_enterleave      */	TRUE,
    /* visible_interest         */	FALSE,
    /* destroy                  */	Destroy,
    /* resize                   */	Resize,
    /* expose                   */	Redisplay,
    /* set_values               */	SetValues,
    /* set_values_hook          */    	NULL,
    /* set_values_almost        */    	XtInheritSetValuesAlmost,
    /* get_values_hook          */	NULL,
    /* accept_focus             */	NULL,
    /* version                  */	XtVersion,
    /* callback_private         */	NULL,
    /* tm_table                 */	defaultTranslations,
    /* query_geometry           */	NULL,
    /* display_accelerator      */      NULL,
    /* extension                */      NULL
    },
    { /**** CompositeClassPart ****/
    /* geometry_handler     */  NULL,
    /* change_managed       */  NULL,
    /* insert_child         */  XtInheritInsertChild,
    /* delete_child         */  XtInheritDeleteChild,
    /* extension            */  NULL
    },
    { /**** ConstraintClassPart ****/
    /* resources            */  constraint_resources,
    /* num_resources        */  XtNumber(constraint_resources),
    /* constraint_size      */  sizeof(AtStripchartConstraintsRec),
    /* initialize           */  (XtInitProc) NULL,
    /* destroy              */  (XtWidgetProc) NULL,
    /* set_values           */  (XtSetValuesFunc) NULL,
    /* extension            */  NULL,
    },
    { /**** AtStripchartClassPart ****/
    /* meaningless field    */  0
    }
};

WidgetClass atStripchartWidgetClass = (WidgetClass) &atStripchartClassRec;


static void GetGCs(AtStripchartWidget w)
{
    XGCValues gcv;
    
    gcv.foreground = w->stripchart.foreground;
    w->stripchart.gridGC = XtGetGC(w, GCForeground, &gcv);

    gcv.foreground = w->core.background_pixel ^ w->stripchart.foreground;
    gcv.function = GXxor;
    w->stripchart.elastic.bandGC = XtGetGC(w, GCForeground | GCFunction, &gcv);
}    

static void FreeGCs(AtStripchartWidget w)
{
    XtReleaseGC(w->stripchart.gridGC);
    XtReleaseGC(w->stripchart.elastic.bandGC);
}

static void CalcMargins(AtStripchartWidget w)
{
    w->stripchart.lo.y1 = w->core.height - w->stripchart.margin_height;
    w->stripchart.lo.y2 = w->stripchart.margin_height;
    w->stripchart.lo.height = w->core.height-2*w->stripchart.margin_height;
    w->stripchart.lo.x1 = w->stripchart.margin_width;
    w->stripchart.lo.x2 = w->core.width - w->stripchart.margin_width;
    w->stripchart.lo.width = w->core.width - 2*w->stripchart.margin_width;
}

static void InitGrid(AtStripchartWidget w)
{
    /* if either or both xDivisionSize or xDivisions are unspecified,
     * compute them.
     */

    if ((w->stripchart.xdivmm == IMPOSSIBLE_VALUE) &&
	(w->stripchart.xdivs == IMPOSSIBLE_VALUE)) {
	w->stripchart.xdivmm = DEFAULT_DIV_SZ;
    }
    
    if (w->stripchart.xdivmm == IMPOSSIBLE_VALUE) {
	w->stripchart.xdivmm = w->stripchart.lo.width / w->stripchart.xdivs;
	w->stripchart.lo.xdivpix = w->stripchart.xdivmm *
	                           w->stripchart.lo.xpix_per_mm;
    }
    else if (w->stripchart.xdivs == IMPOSSIBLE_VALUE) {
	w->stripchart.lo.xdivpix = w->stripchart.xdivmm
	    * w->stripchart.lo.xpix_per_mm;
	w->stripchart.xdivs = (int)(w->stripchart.lo.width /
				    w->stripchart.lo.xdivpix);
    }
    else {
	w->stripchart.lo.xdivpix = w->stripchart.xdivmm *
	                           w->stripchart.lo.xpix_per_mm;
	w->core.width = (int)(w->stripchart.xdivs*w->stripchart.lo.xdivpix) + 
			      (2 * w->stripchart.margin_width);
    }
	
    /* if forceSquare, y divisions are the same size as x divisions */
    if (w->stripchart.force_square)
	w->stripchart.ydivmm = w->stripchart.xdivmm;


    /* if either or both yDivisionSize or yDivisions are unspecified,
     * compute them.
     */

    if ((w->stripchart.ydivmm == IMPOSSIBLE_VALUE) &&
	(w->stripchart.ydivs == IMPOSSIBLE_VALUE)) {
	w->stripchart.ydivmm = DEFAULT_DIV_SZ;
    }
    
    if (w->stripchart.ydivmm == IMPOSSIBLE_VALUE) {
	w->stripchart.ydivmm = w->stripchart.lo.height / w->stripchart.ydivs;
	w->stripchart.lo.ydivpix = w->stripchart.ydivmm *
	                           w->stripchart.lo.ypix_per_mm;
    }
    else if (w->stripchart.ydivs == IMPOSSIBLE_VALUE) {
	w->stripchart.lo.ydivpix = w->stripchart.ydivmm *
	                           w->stripchart.lo.ypix_per_mm;
	w->stripchart.ydivs = (int)(w->stripchart.lo.height /
				    w->stripchart.lo.ydivpix);
    }
    else {
	w->stripchart.lo.ydivpix = w->stripchart.ydivmm *
	                           w->stripchart.lo.ypix_per_mm;
	w->core.height = (int)(w->stripchart.ydivs*w->stripchart.lo.ydivpix) +
			      (2 * w->stripchart.margin_width);
    }

    /* figure out the number of line segments in the grid */
    /* and allocate space for them. */
    w->stripchart.lo.num_segs = 2 * (w->stripchart.xdivs+1) *
	                            (w->stripchart.ydivs+1);
    w->stripchart.grid_lines =
	(XSegment *)XtMalloc(sizeof(XSegment) * w->stripchart.lo.num_segs);
}

static void ResizeGrid(AtStripchartWidget w)
{
  /* should use a different resource, independent of forceSquare */
  if (w->stripchart.force_square) {/* same division size, change # divisions */
      w->stripchart.xdivs = (int)(w->stripchart.lo.width / 
				   w->stripchart.lo.xdivpix);
      w->stripchart.ydivs = (int)(w->stripchart.lo.height /
				  w->stripchart.lo.ydivpix);

      w->stripchart.lo.num_segs = 2 * (w->stripchart.xdivs+1) *
	                              (w->stripchart.ydivs+1);
      w->stripchart.grid_lines =
	  (XSegment *)XtRealloc(w->stripchart.grid_lines,
			       w->stripchart.lo.num_segs*sizeof(XSegment));
  }
  else { /* same # of divisions, change division size */
      w->stripchart.lo.xdivpix = w->stripchart.lo.width /
	                         ((double)w->stripchart.xdivs);
      w->stripchart.lo.ydivpix = w->stripchart.lo.height /
	                         ((double)w->stripchart.ydivs);
      w->stripchart.xdivmm = w->stripchart.lo.xdivpix /
	                     w->stripchart.lo.xpix_per_mm;
      w->stripchart.ydivmm = w->stripchart.lo.ydivpix /
	                     w->stripchart.lo.ypix_per_mm;
  }
}

static void ChangeGrid(AtStripchartWidget current, AtStripchartWidget new)
{
    ScreenLayout *l = &new->stripchart.lo;
    AtStripchartPart *s = &new->stripchart;
    Boolean ynumChanged, xnumChanged, ysizeChanged, xsizeChanged;
    
#define Changed(field) (current->stripchart.field != new->stripchart.field)

    ynumChanged = Changed(ydivs);
    xnumChanged = Changed(xdivs);
    ysizeChanged = Changed(ydivmm);
    xsizeChanged = Changed(ydivmm);
    
#undef Changed

    if ((ynumChanged) && !(xnumChanged && s->force_square)) {
	l->ydivpix = l->height / s->ydivs;
	s->ydivmm = l->ydivpix / l->ypix_per_mm;
	if (s->force_square)  {
	    l->xdivpix = l->ydivpix;
	    s->xdivmm = l->xdivpix / l->xpix_per_mm;
	}
    }

    if (xnumChanged) {
	l->xdivpix = l->height / s->xdivs;
	s->xdivmm = l->xdivpix / l->xpix_per_mm;
	if (s->force_square) {
	    l->ydivpix = l->xdivpix;
	    s->ydivmm = l->ydivpix / l->ypix_per_mm;
	}
    }
	
    if ((ysizeChanged) && !(xsizeChanged && s->force_square)) {
	l->ydivpix = s->ydivmm * l->ypix_per_mm;
	s->ydivs = l->height / l->ydivpix;
	if (s->force_square) {
	    s->xdivmm = s->ydivmm;
	    l->xdivpix = s->xdivmm * l->xpix_per_mm;
	    s->xdivs = l->width / l->xdivpix;
	}
    }

    if (xsizeChanged) {
	l->xdivpix = s->xdivmm * l->xpix_per_mm;
	s->xdivs = l->width / l->xdivpix;
	if (s->force_square) {
	    s->ydivmm = s->xdivmm;
	    l->ydivpix = s->ydivmm * l->ypix_per_mm;
	    s->ydivs = l->height / l->ydivpix;
	}
    }

    l->num_segs = 2 * (s->xdivs+1) * (s->ydivs+1);
    s->grid_lines = (XSegment *) XtRealloc(s->grid_lines,
					   l->num_segs * sizeof(XSegment));
}


/** The following macros abstract away the location of the origin (0,0) **/
/** on the window.  They also make numerical manipulation of the x,y    **/
/** coordinates easier when calculating the next grid line, etc...      **/
/** I find it easier to work with the origin being the lower left corner**/
/** instead of the upper-left corner...                                 **/

#define LessThanX1(w,x)	((x) < w->stripchart.lo.x1) 	      
#define LessThanY1(w,y)     ((y) > w->stripchart.lo.y1)
#define GreaterThanX2(w,x)	((x) > w->stripchart.lo.x2)
#define GreaterThanY2(w,y)	((y) < w->stripchart.lo.y2)

/**************************************************************************/
/* This procedure calculates the positions of the grid lines in the x and */
/* y directions and puts them into an array of segments.                  */
/**************************************************************************/
static void CalcGrid(w)
AtStripchartWidget w;
{
  XSegment	*segment;
  int		x2, x1;
  int 		y2, y1;
  int 		xdivs, ydivs;
  int		xdiv_pixels, ydiv_pixels;
  int 		x, y;
  int           i,j;
  
  /** giving widget fields some shorter names **/
  x1 = w->stripchart.lo.x1;
  x2 = w->stripchart.lo.x2;
  y1 = w->stripchart.lo.y1;
  y2 = w->stripchart.lo.y2;
  xdivs = w->stripchart.xdivs;
  ydivs = w->stripchart.ydivs;
  xdiv_pixels = w->stripchart.lo.xdivpix;
  ydiv_pixels = w->stripchart.lo.ydivpix;

  segment = w->stripchart.grid_lines;
  for (y = y1, j = 0; j <= ydivs; y -= ydiv_pixels,j++) {
      for (x= x1, i = 0; i <= xdivs; x += xdiv_pixels,i++) {

	  /* segment in the x direction */
	  segment->x1 = (short)(x - (int)(GRID_LN_LEN/2));
	  segment->x2 = (short)(x + (int)(GRID_LN_LEN/2));
	  segment->y1 = (short)y; 
	  segment->y2 = (short)y;
	  segment++;
	  
	  /** segment in the y direction **/
	  segment->x1 = (short)x;
	  segment->x2 = (short)x;
	  segment->y1 = (short)(y - (int)(GRID_LN_LEN/2)); 
	  segment->y2 = (short)(y + (int)(GRID_LN_LEN/2));
	  segment++;
      }
  }
} /** end  CalcGrid **/



static void ClassInitialize()
/*************************************************************************/
/* This procedure adds the converter for double                          */
/*************************************************************************/
{
  XtAddConverter(XtRString, XtRDouble, CvtStringToDouble, NULL, 0);

} /** end ClassInitialize **/


/**************************************************************************/
/* This procedure initializes the layout for the widget.  Later on, it    */
/* initialize other things that are related to the traces...              */
/**************************************************************************/
static void Initialize (request, new)
Widget request, new;
{
    XtGCMask	valuemask;
    XGCValues	gcv;
    AtStripchartWidget w = (AtStripchartWidget)new;
    
    if (w->stripchart.margin_height <= 0) {
	XtWarning ("Margin height cannot be <= 0 - using default");
	w->stripchart.margin_height = DEFAULT_MARGIN_HT;
    }

    if (w->stripchart.margin_width <= 0) {
	XtWarning ("Margin width cannot be <= 0 - using default");
	w->stripchart.margin_width = DEFAULT_MARGIN_WD;
    }

    w->stripchart.lo.xpix_per_mm = ((double)WidthOfScreen(XtScreen(w)) /
					WidthMMOfScreen(XtScreen(w)) * 10);

    w->stripchart.lo.ypix_per_mm = ((double)HeightOfScreen(XtScreen(w)) /
					HeightMMOfScreen(XtScreen(w)) *	10);

    CalcMargins(w);
    InitGrid(w);
    CalcMargins(w);  /* we need to call it again in case size has changed */
    CalcGrid(w);
    
    /** create the drawGC **/
    /** Will add font stuff to this later **/
    w->stripchart.ff = AtFontFamilyGet(XtDisplay(w), w->stripchart.font); 

    GetGCs(w);
    
    /* initialize rubber-banding variables */
    w->stripchart.elastic.xband = 0; 
    w->stripchart.elastic.yband = 0;
    w->stripchart.elastic.bandwdth = 0;
    w->stripchart.elastic.bandhgt = 0;
    w->stripchart.elastic.banded = FALSE;

    w->stripchart.select_mode = selectNONE;

} /** end Initialize **/


static void Resize(widget)
Widget widget;
{
  AtStripchartWidget    w = (AtStripchartWidget)widget;

  CalcMargins(w);
  ResizeGrid(w);
  CalcGrid (w);

} /** end resize **/



static void Redisplay (widget, event, region)
Widget widget;
XEvent *event;
Region region;
{
  AtStripchartWidget    w = (AtStripchartWidget)widget;

  XDrawSegments (XtDisplay(w), XtWindow(w), w->stripchart.gridGC,
		 w->stripchart.grid_lines, w->stripchart.lo.num_segs);
} 



 
static void Destroy (dead_widget)
Widget dead_widget;
{
  AtStripchartWidget w = (AtStripchartWidget)dead_widget;
  int i;

/*
  if (timer_on) {
      if (w->stripchart.interval_id)
      XtRemoveTimeOut(w->stripchart.interval_id);
  }
*/
  FreeGCs(w);
  XtFree(w->stripchart.grid_lines);
}  /** end Destroy **/



/** these macros clean up the code in SetValues **/
#define Changed(field)    ((new->stripchart.field) != (current->stripchart.field))
#define CoreChanged(field)  ((new->core.field) != (current->core.field))

static Boolean SetValues (temp_current, request, temp_new)
/**************************************************************************/
/* This procedure is called every time the user wants to change something */
/* in the widget (after its been initialized).  It returns TRUE when all  */
/* the necessary fields of the widget have been appropriately changed.    */
/**************************************************************************/

     Widget temp_current, request, temp_new;
{

  AtStripchartWidget current = (AtStripchartWidget) temp_current;
  AtStripchartWidget new = (AtStripchartWidget) temp_new;
  int count;
  Boolean redisplay = FALSE;

  XtGCMask valuemask;
  XGCValues gcv;
  Position  **temp;

  printf("Entering SetValues\n");
  /** determine if GC stuff has changed and update if necessary **/
  if (Changed (foreground) || CoreChanged(background_pixel)) {
    FreeGCs(new);
    GetGCs(new);
    redisplay = TRUE;
  } /* end if layout.drawGC stuff changes */


  /** determine if margins have changed and update if necessary **/
  if (Changed(margin_height) || Changed(margin_width)) {
      CalcMargins(new);
      CalcGrid(new);
      redisplay = TRUE;
  }
  
  /** determine if divisions/scale fctrs change and update if necessary **/
  if (Changed(xdivmm) || Changed(ydivmm) || 
      Changed(xdivs) || Changed(ydivs)) {

      ChangeGrid(current, new);
      CalcGrid(new);
      redisplay = TRUE;
  } 



  /** determine if the timer_interval has changed and update if necessary **/
 /* if (Changed(timer_interval)) {
*/
    /** remove the old timer **/
/*    XtRemoveTimeOut (current->stripchart.interval_id);
*/
    /** insert the new timer with the new timer_interval **/
/*    new->stripchart.interval_id = 
      XtAppAddTimeOut (XtWidgetToApplicationContext(new),
		new->stripchart.timer_interval, UpdateWidget, (caddr_t) current);
*/
/*    redisplay = TRUE;
  }*/  /* end if timer_interval changes */


  /** if timer_on changes, then determine if the timer is turned on or off **/
/*  if (Changed(timer_on)) {
    new->stripchart.interval_id = 
      XtAppAddTimeOut (XtWidgetToApplicationContext(new),
		new->stripchart.timer_interval, UpdateWidget, (caddr_t) current);
    else {
      XtRemoveTimeOut (current->stripchart.interval_id);
      XtRemoveTimeOut (new->stripchart.interval_id);
    }*/ /* end else */

/*    redisplay = TRUE;
  }*/ /* if timer_interval changed */

  if (Changed (percent_shift) || Changed(xunits) || Changed(yunits))
    redisplay = TRUE;
    printf("returning redisplay - Leaving SetValues");
  return (redisplay);

}  /** end SetValues **/


/**************************************************************************/
/***************************** A C T I O N S ******************************/
/**************************************************************************/

/***************************************/
/** macros to determine where we are  **/
/***************************************/

#define InSideMargin(w,event) \
  ((LessThanX1(w,event->x)) || (GreaterThanX2(w,event->x)))

#define InTopBottomMargin(w,event) \
  ((LessThanY1(w,event->y)) || (GreaterThanY2(w,event->y)))

#define InPlottingRegion(w,event) \
          (!(InSideMargin(w,event) || InTopBottomMargin(w,event)))

#define abs(x) 	(((x)>=0)?(x):(-(x)))

#define DrawBandRect(w) \
  XDrawRectangle(XtDisplay(w), XtWindow(w), w->stripchart.elastic.bandGC,\
	 w->stripchart.elastic.xband, w->stripchart.elastic.yband,\
	 w->stripchart.elastic.bandwdth, w->stripchart.elastic.bandhgt)

#define EraseBandRect(w) DrawBandRect(w)


static void SelectAction(w, event, params, num_params)
/**************************************************************************/
/* This proc is called whenever the user lets off on the mouse button     */
/* This procedure determines if the mouse button was just clicked or      */
/* dragged.... If the mouse moves more than MIN_CLIK_MV, then we call the */
/* RubberBand procedure...                                                */
/**************************************************************************/
     AtStripchartWidget w;
     XMotionEvent 	*event;
     String		*params;
     Cardinal		*num_params;
 
  {
    /** no matter what we do, we should give the x, y, etc parts of **/
    /** the elastic structure the current values for this situation **/

    w->stripchart.elastic.xband = event->x;
    w->stripchart.elastic.yband = event->y;
    
    if (InPlottingRegion(w,event)) {
      w->stripchart.select_mode = selectPLOT;
    }
    
    else if (InSideMargin(w,event)) {
      w->stripchart.select_mode = selectPEN;
      }

    else {
      w->stripchart.select_mode = selectNONE;
    }

 } /**end SelectAction **/



static void RubberBand (w, event, params, num_params)
/**************************************************************************/
/* This procedure is called when mouse button 1 is pressed down and draggd*/
/* A rubber-band (or expanding rectangle) draws a rectangle from where the*/
/* mouse button was originally clicked to where the mouse currently is.   */
/* Note that the last 2 parameters (params, num_params) are not used, but */
/* the motion event sends the info to us anyway...                        */
/**************************************************************************/
     AtStripchartWidget w;
     XMotionEvent 	*event;
     String		*params;
     Cardinal		*num_params;
     
{
  /** if the pointer is outside the margins of the chart, don't bother **/

  if (w->stripchart.select_mode == selectPLOT) {

    /* inside plotting region initially - see if still there */
    if (InPlottingRegion(w,event)) {
      
      /** check to see if rubber-banding - if so, erase old rect. **/

      if (w->stripchart.elastic.banded) {
	/** ok, rubber-banding so erase old rectangle **/
	EraseBandRect(w);
      }
    
    /** now, reset bandwdth and bandhgt, redraw rect and **/
    /** set banded to true **/

      w->stripchart.elastic.bandwdth = event->x - w->stripchart.elastic.xband;
      w->stripchart.elastic.bandhgt = event->y - w->stripchart.elastic.yband;
      DrawBandRect(w);
      w->stripchart.elastic.banded = True;
    }

    else {
      /** not in region, erase rectangle... **/
      if (w->stripchart.elastic.banded) {
	EraseBandRect(w);
	w->stripchart.elastic.banded = False;
      }
      w->stripchart.select_mode = selectNONE;
    }
  /* else if (w->stripchart.select_mode == selectPEN) hmmmmmmm */
  }

} /** end RubberBand **/



static void DeselectAction(w, event, params, num_params)
/**************************************************************************/
/* This proc is called whenever the user lets off on the mouse button     */
/* and resets everything...                                               */
/**************************************************************************/

     AtStripchartWidget w;
     XMotionEvent 	*event;
     String		*params;
     Cardinal		*num_params;
{

  if (w->stripchart.select_mode == selectPLOT) {
    if (w->stripchart.elastic.banded) {
      EraseBandRect(w);
      w->stripchart.elastic.banded = False;
      w->stripchart.select_mode = selectNONE;
    }
  }
} /** DeselectAction **/
