
/* include files */
#include <stdio.h>
#include <X11/StringDefs.h>
#include <Xm/XmP.h>
#include <X11/Xos.h>
#include <X11/Shell.h>
#include "StripchartP.h"
#include "double.h"

/** defaults defined **/
#define DEFAULT_FONT_SZ         AtFontMEDIUM
#define DEFAULT_FONT_FAMILY     "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 GRID_LN_LEN             4
#define IMPOSSIBLE_VALUE        -1

/* translations (for mouse buttons)*/

static char defaultTranslations[] = 
  "<Btn1Down>:		SelectionAct() 	\n\
   <Btn1Motion>:	MoveGripper()";
   <Btn1Up>:            

		
extern void CvtStringToDouble();


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

/* declares the defaults */
static XtResource resources[] = {
/*  {XtNfontSize, XtCFontSize, XtRInt, sizeof(int),
    offset(ft_size), XtRImmediate, DEFAULT_FONT_SZ},
    {XtNfontFamily, XtCFontFamily, XtRString, sizeof(String),
    offset(font), XtRString, DEFAULT_FONT_FAMILY}, */
  {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
       offset(fgpixel), 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_hgt), XtRString, "2"}, /* */
  {XtNmarginWidth, XtCMarginWidth, XtRInt, sizeof(int),
     offset(margin_wdth), 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(xdiv), XtRString, "-1"}, /* */
  {XtNyDivisions, XtCDivisions, XtRInt, sizeof(int), 
     offset(ydiv), XtRString, "-1"}, /* */
  {XtNxDivisionSize, XtCDivisionSize, XtRInt, sizeof(int),
     offset(xdivision_size), XtRString, "-1"}, /* */
  {XtNyDivisionSize, XtCDivisionSize, XtRInt, sizeof(int),
     offset(ydivision_size), 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(caddr_t),
     offset(click_callbk), XtRCallback, NULL},
  {XtNdragCallback, XtCCallback, XtRCallback, sizeof(caddr_t),
     offset(drag_callbk), XtRCallback, NULL},
  {XtNdragGripperCallback, XtCCallback, XtRCallback, sizeof(caddr_t),
     offset(drag_grip_callbk), XtRCallback, NULL},
  {XtNselectCallback, XtCCallback, XtRCallback, sizeof(caddr_t),
     offset(select_callbk), XtRCallback, NULL},
};

#undef offset

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

/* other private procedures */
static void InitLayout(), ConvertLayoutToPixel();
static void DrawGridLines(), CalcGridLines();
/*static void UpdateWidget(); */
static void ListSegments();

AtStripchartClassRec atstripchartClassRec = {
  { /* core fields    */
#define Superclass                      (&xmPrimitiveClassRec)
    /* 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                  */	NULL,
    /* num_actions              */	0,
    /* 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                 */	NULL,
    /* query_geometry           */	NULL,
  }
};

WidgetClass atstripchartWidgetClass = (WidgetClass) &atstripchartClassRec;

/**********************************************************************
* Now that all the initial things are done, it's time to declare all   *
* the private procedures for stripchart (like resize, initialize, etc) *
 **********************************************************************/


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

} /** end ClassInitialize **/


static void ListSegments(w)
/*************************************/
/** For debugging only... lists the **/
/** contents of the grid_lines array**/
/*************************************/
     AtStripchartWidget w;
{
  int 		num, i;
  XSegment	*grid_lines;

  num = w->stripchart.xdiv * w->stripchart.ydiv * 2;
  grid_lines = w->stripchart.grid_lines;

  for (i=0; i <= num; i++)
    printf ("x1 = %d, y1 = %d;  x2 = %d, y2 = %d\n", 
	    grid_lines[i].x1, grid_lines[i].y1, grid_lines[i].x2, 
	    grid_lines[i].y2);
}

static void Initialize (request, new)
/**************************************************************************/
/* This procedure initializes the layout for the widget.  Later on, it    */
/* initialize other things that are related to the traces...              */
/**************************************************************************/

     Widget request, new;

{
  AtStripchartWidget w = (AtStripchartWidget)new;
  XtGCMask      valuemask;
  XGCValues     gcv;
  Position **temp;
  
  
  /** initialize the layout structure **/
  InitLayout (w);
  

  /** allocates and clears the memory needed for the array of segments **/
  w->stripchart.grid_lines = (XSegment*)(XtCalloc 
		      (w->stripchart.layout.num_segs, sizeof(XSegment)));
  
  CalcGridLines(w);

} /** end Initialize **/


/** 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...      **/

#define NextX(x, increment)		      ((x) + (increment))
#define NextY(y, increment)		      ((y) - (increment))
#define ReachXend(x, end)		      ((x) >= (end))
#define ReachYend(y, end)		      ((y) <= (end))


static void InitLayout(w)
/**************************************************************************/
/* This procedure initializes the margin widths and heights and inits the */
/* GC for the widget.  Also, a procedure is called to calculate the other,*/
/* more involved fields of the layout.                                    */
/**************************************************************************/
     AtStripchartWidget     w;
{

  XtGCMask     valuemask;
  XGCValues    gcv;

  /** init the start and end positions of the axes **/
  printf("Entering InitLayout\n");
  /** first, verify that the info entered is OK **/
  if (w->stripchart.margin_hgt <= 0) {
    XtWarning ("Margin height cannot be <= 0 - using default");
    w->stripchart.margin_hgt = DEFAULT_MARGIN_HT;
  }
  if (w->stripchart.margin_wdth <= 0) {
    XtWarning ("Margin width cannot be <= 0 - using default");
    w->stripchart.margin_wdth = DEFAULT_MARGIN_WD;
  }
  
  w->stripchart.layout.ystart = w->core.height - w->stripchart.margin_hgt;
  w->stripchart.layout.yend = w->stripchart.margin_hgt;
  w->stripchart.layout.xstart = w->stripchart.margin_wdth;
  w->stripchart.layout.xend = w->core.width - w->stripchart.margin_wdth;

  /** create the drawGC **/
  /** Will add font stuff to this later **/
  
  valuemask = GCForeground | GCBackground | GCLineWidth;
  gcv.foreground = w->stripchart.fgpixel;
  gcv.background = w->core.background_pixel;
  gcv.line_width = 0;                        /* for faster drawing */
  
  w->stripchart.layout.drawGC = XtGetGC(w, valuemask, &gcv);

  /** since the resources are specified in mm, etc, they have to be **/
  /** converted to pixel... **/
  
  ConvertLayoutToPixel (w);
  printf("Leaving InitLayout\n");
}  /** end InitLayout **/


static void ConvertLayoutToPixel (w)
/**************************************************************************/
/* This procedure converts the real-world division sizes                  */
/* and converts them to pixels.  It also determines which resources are   */
/* unspecified, and does calculations for division_size, width, and # div */
/* It does this for both the x and y axes.                                */
/**************************************************************************/
     AtStripchartWidget     w;
{

  printf("Entering ConvertLayoutToPixel\n");
  /** to convert from mm to pixel, must make a scaling factor **/
  /** the macros XWidthMMOfScreen, XWidthOfScreen, etc will be used **/
  /** Assumption: pixels not necessarily square **/

  w->stripchart.layout.xpix_per_mm = (double)(XWidthOfScreen(XtScreen(w)) /
					   XWidthMMOfScreen(XtScreen(w)));

  w->stripchart.layout.ypix_per_mm = (double)(XHeightOfScreen(XtScreen(w)) /
					   XHeightMMOfScreen(XtScreen(w)));

  
  /** if division_size is specified, convert # to pixels and store in layout **/
  
  if (w->stripchart.xdivision_size != IMPOSSIBLE_VALUE)
    w->stripchart.layout.xdivision_pixels = (double)
      (w->stripchart.xdivision_size * w->stripchart.layout.xpix_per_mm);

  /** if forced_square, then ydivision_size = xdivision_size **/
  if (w->stripchart.force_square) 
    w->stripchart.ydivision_size = w->stripchart.xdivision_size;

  if (w->stripchart.ydivision_size != IMPOSSIBLE_VALUE)
    w->stripchart.layout.ydivision_pixels = (double)
	(w->stripchart.ydivision_size * w->stripchart.layout.ypix_per_mm);

  /** Now to do the x axis calculations **/
  /** There are 3 different resources that can be used to specify     **/
  /** things on the axis (xdiv, xdivision_size, and width).           **/
  /** Specifying any two is enough (without having to use defaults).  **/
  /**1) If xdiv and xdivision_size are specified, then the width is   **/
  /** overridden by (xdiv * xdivision_pixels) + (2 * margin_wdth)     **/
  /**2) If only xdiv is unspecified,  xdiv = (wwidth/xdivision_pixels)**/
  /** wwidth = width - (2 * margin_wdth)                              **/
  /**3) If only xdivision_pixels is unspecified, the other 2 resources**/
  /** 						remain untouched...   **/
  /**4) If neither specified, then xdivision_size = 10mm and          **/
  /**                xdiv = width / xdivision_pixels                  **/
  /** the calculations for the y axis are similar...                  **/

  /** calculation 1 **/
  if ((w->stripchart.xdiv != IMPOSSIBLE_VALUE) &&
      (w->stripchart.xdivision_size != IMPOSSIBLE_VALUE)) {

    w->core.width = (int)((w->stripchart.xdiv * 
                    w->stripchart.layout.xdivision_pixels) + 
		    (2 * w->stripchart.margin_wdth));
  } /* end calculation 1 */

    /** calculation 2 **/
  else {
    if ((w->stripchart.xdiv = IMPOSSIBLE_VALUE) && 
	(w->stripchart.xdivision_size != IMPOSSIBLE_VALUE)) {
	
      /** overriding the resource **/
      w->stripchart.xdiv = (int)((w->core.width - 
				  (2* w->stripchart.margin_wdth)) / 
				 w->stripchart.layout.xdivision_pixels);
    } /* end calculation 2 */

    /** calculation 4 **/
    else {
      if ((w->stripchart.xdiv = IMPOSSIBLE_VALUE) && 
	  (w->stripchart.xdivision_size = IMPOSSIBLE_VALUE)) {

	w->stripchart.xdivision_size = DEFAULT_DIV_SZ;
	w->stripchart.layout.xdivision_pixels = (double)
	  (w->stripchart.layout.xpix_per_mm * w->stripchart.xdivision_size);

	w->stripchart.xdiv = (int) (w->core.width / 
				    w->stripchart.layout.xdivision_pixels);
      } /* end calculation 4 */
    } /* calculation 3 doesn't change anything */
  }  


  /** now to repeat calculations for the y-axis **/

  /** calculation 1 **/
  if ((w->stripchart.ydiv != IMPOSSIBLE_VALUE) &&
      (w->stripchart.ydivision_size != IMPOSSIBLE_VALUE)) {

    w->core.height = (int)((w->stripchart.ydiv * 
                    w->stripchart.layout.ydivision_pixels) + 
		    (2 * w->stripchart.margin_hgt));
  } /* end calculation 1 */

    /** calculation 2 **/
  else {
    if ((w->stripchart.ydiv = IMPOSSIBLE_VALUE) && 
	(w->stripchart.ydivision_size != IMPOSSIBLE_VALUE)) {
	
      /** overriding the resource **/
      w->stripchart.ydiv = (int)((w->core.height - 
				  (2* w->stripchart.margin_hgt)) / 
				 w->stripchart.layout.ydivision_pixels);
    } /* end calculation 2 */

    /** calculation 4 **/
    else {
      if ((w->stripchart.ydiv = IMPOSSIBLE_VALUE) && 
	  (w->stripchart.ydivision_size = IMPOSSIBLE_VALUE)) {

	w->stripchart.ydivision_size = DEFAULT_DIV_SZ;
	w->stripchart.layout.ydivision_pixels = (double)
	  (w->stripchart.layout.ypix_per_mm * w->stripchart.ydivision_size);
	
	w->stripchart.ydiv = (int) (w->core.width / 
				    w->stripchart.layout.ydivision_pixels);
      } /* end calculation 4 */
    } /* calculation 3 doesn't change anything */
  }  
  printf("Leaving ConvertLayoutToPixel\n");

  /** gives the total number of line segments that have to be drawn **/
  w->stripchart.layout.num_segs = 2 * (w->stripchart.xdiv * w->stripchart.ydiv);


  } /** end ConvertLayoutToPixel **/



static void Resize(widget)
/**************************************************************************/
/* this procedure recalculates various fields of the widget.  the         */
/* inner dimensions and ranges have to be recalculated.  since this       */
/* creates an expose event, the redisplay procedure will be called so that*/
/* the widget can be re-drawn to show its changes.                        */
/**************************************************************************/
     Widget widget;
{

  AtStripchartWidget    w = (AtStripchartWidget)widget;
printf("Entering Resize\n");
  ConvertLayoutToPixel (w);
  CalcGridLines (w);
printf("Leaving Resize\n");
} /** end resize **/



static void Redisplay (widget, event, region)
/**************************************************************************/
/* this procedure is called whenever there is a resize or expose event.   */
/* this procedure calls several sub procedures to draw different parts of */
/* the widget.                                                            */
/**************************************************************************/
     Widget widget;
     XEvent *event;
     Region region;
{
  AtStripchartWidget    w = (AtStripchartWidget)widget;
  printf("Entering Redisplay\n");
  DrawGridLines (w);
  printf("Leaving Redisplay\n");
} /** end redisplay **/



static void DrawGridLines (w)
/**************************************************************************/
/* This procedure uses XDrawSegments to draw the lines on the window to   */
/* form a "grid".  This separates the drawing from the calculations.      */
/**************************************************************************/
     AtStripchartWidget w;
{
  printf("Entering DrawGridLines\n");
  XDrawSegments (XtDisplay(w), XtWindow(w), w->stripchart.layout.drawGC,
		 w->stripchart.grid_lines, w->stripchart.layout.num_segs);
  printf("Leaving DrawGridLines\n");
} /** end DrawGridLines **/


static void CalcGridLines(w)
/**************************************************************************/
/* This procedure calculates the positions of the grid lines in the x and */
/* y directions and puts them into an array of segments.                  */
/**************************************************************************/
     AtStripchartWidget	w;
{
  XSegment	segment;
  short		x1, x2, y1, y2;
  int		xend, xstart;
  int 		yend, ystart;
  int 		xdiv, ydiv;
  int		xdiv_pixels, ydiv_pixels;
  int 		x, y;
  int 		count1, count2;
  int           index = 0;
  
  /** giving widget fields some shorter names **/
  xstart = w->stripchart.layout.xstart;
  xend = w->stripchart.layout.xend;
  ystart = w->stripchart.layout.ystart;
  yend = w->stripchart.layout.yend;
  xdiv = w->stripchart.xdiv;
  ydiv = w->stripchart.ydiv;
  xdiv_pixels = w->stripchart.layout.xdivision_pixels;
  ydiv_pixels = w->stripchart.layout.ydivision_pixels;
  y = ystart;   x = xstart;
  printf("Calculating the grid lines\n");
  for (y = NextY(y, ydiv_pixels), count1 = 1 ; 
       ((count1 <= ydiv) && (!ReachYend(y, yend)));
       y = NextY(y, ydiv_pixels), count1++) { 
    for (x = NextX(x, xdiv_pixels), count2 = 1;
	 ((count2 <= xdiv) && (!ReachXend(x, xend)));
	 x = NextX(x, xdiv_pixels), count2++) {
      
      /** 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;

      w->stripchart.grid_lines[index] = segment;
      index++;
      
      /** 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));
      
      w->stripchart.grid_lines[index] = segment;
      index++;
      
    } /* end for x */
    x = xstart;
  } /* end for y */
  printf("Completed CalcGridLines\n");
  
} /** end  CalcGridLines **/

 
static void Destroy (dead_widget)
/**************************************************************************/
/* This procedure calls the Xt intrinsics procedure XtDestroyGC           */
/* This is done when the widget is destroyed.                             */
/**************************************************************************/

     Widget dead_widget;
{

  AtStripchartWidget w = (AtStripchartWidget)dead_widget;
  int i;


  /** remove the timer if it exists **/

/*  if (timer_on) {
    if (w->stripchart.interval_id)
    XtRemoveTimeOut(w->stripchart.interval_id);
  }
*/
  /** remove the drawing GC **/ 

  XtDestroyGC (w->stripchart.layout.drawGC);

}  /** 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 (fgpixel) || CoreChanged(background_pixel)) {
    printf ("GC changed\n");
    InitLayout(new);
    XtFree (current->stripchart.layout.drawGC);
    redisplay = TRUE;
  } /* end if layout.drawGC stuff changes */


  /** determine if margins have changed and update if necessary **/
  if (Changed(margin_hgt) || Changed(margin_wdth)) {
    
    InitLayout(new);
    
    /* release memory held by the grid_lines in current */
    XtFree(current->stripchart.grid_lines);
    printf("Margins changed\n");
    /** allocates and clears the memory needed for the array of segments **/
    new->stripchart.grid_lines = (XSegment*)(XtCalloc 
		     (new->stripchart.layout.num_segs, sizeof(XSegment)));
    CalcGridLines(new);
    
    redisplay = TRUE;
  } /* end if margins change */


  /** determine if divisions/scale fctrs change and update if necessary **/
  if (Changed(xdivision_size) || Changed(ydivision_size) || 
      Changed(xdiv) || Changed(ydiv) ||
      Changed(force_square) || 
      Changed (xdiv_units)|| Changed (ydiv_units)) {

    ConvertLayoutToPixel(new);
    printf("division sizes, # divs, force square, or Units Per Division changed\n");
    /* release memory held by the grid_lines in current */
    XtFree(current->stripchart.grid_lines);

    /** allocates and clears the memory needed for the array of segments **/
    new->stripchart.grid_lines = (XSegment*)(XtCalloc 
			(new->stripchart.layout.num_segs, sizeof(XSegment)));
    CalcGridLines(new);
    
    redisplay = TRUE;
  } /* end if divisions, scales, etc change */



  /** 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 **/



