ftp.nice.ch/pub/next/unix/developer/pcn.2.0.s.tar.gz#/contrib/gauge/Xsw/Chart.c

This is Chart.c in view mode; [Download] [Up]

#if ( !defined(lint) && !defined(SABER))
  static char PCN_rcsid[] = "$Header: /ufs/comp/mei/PROJ_PCN/onprofile/IFModel/Xsw/RCS/Chart.c,v 1.1 1992/04/17 18:21:53 mei Exp $";
#endif

/******************************************************************************
*									      *
*	Copyright (C) The Aerospace Corporation 1991			      *
*									      *
*	This software was developed by The Aerospace Corporation as a 	      *
*	research endeavor for the United States Air Force 		      *
*	Space Systems Division.  The current version of the Gauge	      *
*	computer program is available for  release to you for		      *
*	educational and research purposes only.  It is not 		      *
*	to be used for commercial purposes.				      *
*									      *
*	In addition, the following conditions shall apply.		      *
*									      *
*	1) The computer software and documentation were designed to	      *
*	satisfy internal Aerospace requirements only.			      *
*	The software is provided ``as is,'' and The Aerospace Corporation     *
*	makes no warranty, expressed or implied, as to it accuracy,	      *
*	functioning, or fitness for a particular purpose.		      *
*									      *
*	2) The Aerospace Corporation and its personnel are not		      *
*	responsible for providing technical support or general assistance     *
*	with respect to the software.					      *
*									      *
*	3) Neither The Aerospace Corporation nor its personnel shall be	      *
*	liable for claims, losses, or damages arising out of or connected     *
*	with the use of this software.					      *
*	Your sole and exclusive remedy shall be to request a replacement      *
*	copy of the program.						      *
*									      *
******************************************************************************/

/*
 * Chart.c - chart widget.
 */

#include <X11/IntrinsicP.h>
#include "Xsw.h"
#include <string.h>

#include "ChartP.h"
#include "Patterns.h"

#define DEFAULT_CHART_WIDTH	32	/* in cells */
#define DEFAULT_CHART_HEIGHT	32	/* in cells */

#define DEFAULT_CELL_SIZE	16	/* in pixels */

static XtResource resources[] = {
#define offset(field) XtOffset(ChartWidget, chart.field)
  { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
      offset(foreground), XtRString, XtDefaultForeground },
  { XtNhighlight, XtCForeground, XtRPixel, sizeof(Pixel),
      offset(highlight), XtRString, XtDefaultForeground },

  { XtNcellWidth, XtCCellSize, XtRInt, sizeof(int),
      offset(cell_width), XtRImmediate, (XtPointer)DEFAULT_CELL_SIZE },
  { XtNcellHeight, XtCCellSize, XtRInt, sizeof(int),
      offset(cell_height), XtRImmediate, (XtPointer)DEFAULT_CELL_SIZE },
  { XtNcellSeparation, XtCCellSeparation, XtRInt, sizeof(int),
      offset(border), XtRImmediate, (XtPointer) 1 },
  { XtNboxThickness, XtCBoxThickness, XtRInt, sizeof(int),
      offset(frame), XtRImmediate, (XtPointer) 3 },

  { XtNbox, XtCBox, XtRBoolean, sizeof(Boolean),
      offset(box), XtRString, "True" },
  { XtNrigidZoom, XtCRigidZoom, XtRBoolean, sizeof(Boolean),
      offset(rigid_zoom), XtRString, "True" },

  { XtNcolors, XtCColors, XtRPixelList, sizeof(PixelList),
      offset(colors), XtRString, "black" },
  { XtNpatterns, XtCPatterns, XtRPatternList, sizeof(PatternList),
      offset(patterns), XtRString, "solid" },
  { XtNpalette, XtCPalette, XtRString, sizeof(String),
      offset(palette), XtRString, "Palette" },

  { XtNwidthInCells, XtCNumberCells, XtRDimension, sizeof(Dimension),
      offset(width), XtRImmediate, (XtPointer)DEFAULT_CHART_WIDTH },
  { XtNheightInCells, XtCNumberCells, XtRDimension, sizeof(Dimension),
      offset(height), XtRImmediate, (XtPointer)DEFAULT_CHART_HEIGHT },
  { XtNcurX, XtCCurX, XtRInt, sizeof(int),
      offset(cur_x), XtRImmediate, (XtPointer) 0 },
  { XtNcurY, XtCCurY, XtRInt, sizeof(int),
      offset(cur_y), XtRImmediate, (XtPointer) 0 },

  { XtNcellArray, XtCCellArray, XtRPointer, sizeof(double *),
      offset(cell), XtRImmediate, (XtPointer)NULL },
  { XtNstateArray, XtCStateArray, XtRPointer, sizeof(Boolean *),
      offset(state), XtRImmediate, (XtPointer)NULL },

  { XtNtwoD, XtCTwoD, XtRBoolean, sizeof(Boolean),
      offset(twoD), XtRString, "True" },
  { XtNzoom, XtCZoom, XtRBoolean, sizeof(Boolean),
      offset(zoom), XtRString, "True" },

  { XtNcallback, XtCCallback, XtRCallback, sizeof(XPointer),
	offset(callback), XtRCallback, NULL },
  { XtNchartResize, XtCCallback, XtRCallback, sizeof(XPointer),
	offset(chart_resize), XtRCallback, NULL },

};


static void Initialize();
static void ClassPartInitialize();
static void Redisplay();
static void Destroy();
static void Resize();
static Boolean SetValues();

/* the following are private functions unique to Chart */
static void DoCell(), DrawIntoBigPixmap();

/* the following are actions of Chart */
static void SelectCell(), ManyCells();

static char defaultTranslations[] =
	"<Btn1Down>:    SelectCell()              \n\
         <Btn1Motion>:  ManyCells()";

static XtActionsRec actions[] = {
        {"SelectCell", SelectCell},
	{"ManyCells", ManyCells},
};

ChartClassRec chartClassRec = {
    {
    /* core_class fields */
    /* superclass	  	 */ (WidgetClass) &coreClassRec,
    /* class_name	  	 */ "Chart",
    /* widget_size	  	 */ sizeof(ChartRec),
    /* class_initialize   	 */ NULL,
    /* class_part_initialize	 */ ClassPartInitialize,
    /* class_inited       	 */ FALSE,
    /* initialize	  	 */ Initialize,
    /* initialize_hook		 */ NULL,
    /* realize		  	 */ XtInheritRealize,
    /* actions		  	 */ actions,
    /* num_actions	  	 */ XtNumber(actions),
    /* resources	  	 */ resources,
    /* num_resources	  	 */ XtNumber(resources),
    /* xrm_class	  	 */ NULLQUARK,
    /* 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       */ XtInheritDisplayAccelerator,
    /* extension                 */ NULL
    },
    {
    /* dummy_field               */ 0,
    },
};

WidgetClass chartWidgetClass = (WidgetClass) & chartClassRec;

/*
CoreClassPart SuperClass;
#define Superclass  (&(chartClassRec.core_class.superclass))
*/

/* ARGSUSED */
Boolean CvtStringToPattern(display, args, nargs,
			      fromVal, toVal, converter_data)
     Display* display;
     XrmValuePtr args, fromVal, toVal;
     int *nargs;
     XtPointer* converter_data;
{
  static int result;
 
  if (XswStrCmp((char *)fromVal->addr, "solid"))
    result = 0;
  else if (XswStrCmp((char *)fromVal->addr, "checkered"))
    result = 1;
  else if (XswStrCmp((char *)fromVal->addr, "slantleft"))
    result = 2;
  else if (XswStrCmp((char *)fromVal->addr, "slantright"))
    result = 3;
  else if (XswStrCmp((char *)fromVal->addr, "vertical"))
    result = 4;
  else if (XswStrCmp((char *)fromVal->addr, "horizontal"))
    result = 5;
  else 
    XtStringConversionWarning((char *) fromVal->addr, "Pattern");

  DONE(Pattern, result);
}

CVT_STRING_TO_LIST(CvtStringToPatternList, Pattern, XtRPattern, 
		   (Pattern)END_OF_LIST)

static void
GetAllGC(cw)
ChartWidget cw;
{
    XGCValues values;
    XtGCMask mask = GCForeground | GCBackground | GCFillStyle | GCLineWidth;
    int i, n_clrs, n_pats;
    unsigned int colors[300];
    Boolean bw = False;
    String file_name;

    /* This GC used to erase areas */
    values.background = cw->core.background_pixel;
    values.fill_style = FillSolid;
    values.foreground = cw->core.background_pixel;
    values.line_width = cw->chart.frame;
    cw->chart.cleargc = XtGetGC((Widget)cw, mask, &values);

    /* This GC used for drawing the box to indicate selections */
    values.foreground = cw->chart.highlight;
    cw->chart.boxgc = XtGetGC((Widget)cw, mask, &values);

    /* Get GCs for multiple bars in 2D chart */
    mask = mask | GCStipple;
    values.fill_style = FillOpaqueStippled;
    n_pats = 0;
    while (cw->chart.patterns[n_pats] != END_OF_LIST) n_pats++;
    n_clrs = 0;
    while(cw->chart.colors[n_clrs] != END_OF_LIST) n_clrs++;

    cw->chart.styles = MIN(n_pats, n_clrs);
    cw->chart.style_gc = (GC *)XtMalloc((cw->chart.styles+1) *
					sizeof(GC));
    for(i=0; i < cw->chart.styles; i++) {
      if (i < n_pats) {
	values.stipple = 
	  XCreateBitmapFromData(XtDisplay(cw),
				ROOTWINDOW(cw),
				&patterns[cw->chart.patterns[i]*
					  PAT_WIDTH*PAT_HEIGHT/8],
				PAT_WIDTH, PAT_HEIGHT);
      } else {
	values.stipple = 
	  XCreateBitmapFromData(XtDisplay(cw),
				ROOTWINDOW(cw),
				hundred,
				PAT_WIDTH, PAT_HEIGHT);
      }
      if (i < n_clrs) {	  
	values.foreground = cw->chart.colors[i];
      } else {
	values.foreground = cw->chart.foreground;
      }

      cw->chart.style_gc[i] = XtGetGC((Widget)cw, mask, &values);
    }

    /* Now get GCs to use to indicate values in 3D chart */
    
    /* load in selected palette */
    file_name = XswGetLibName(cw->chart.palette);
    if (file_name != NULL) {
      cw->chart.values = 
	load_color_palette(XtDisplay(cw),DefaultScreen(XtDisplay(cw)),
			   file_name, colors);
    } else {
      cw->chart.values = -1;
    }

    if (cw->chart.values < 0) /* if no palette then use greyscale */
      cw->chart.values = 
	load_color_palette(XtDisplay(cw),DefaultScreen(XtDisplay(cw)),
			   "Greyscale64", colors); 

    if (cw->chart.values <= 0) { /* if one-bit pixel display */
      cw->chart.values = NUM_PERCENTS;
      bw = True;
    }

    cw->chart.value_gc = (GC *)XtMalloc((cw->chart.values+1) * 
					sizeof(GC));
    values.stipple = 
      XCreateBitmapFromData(XtDisplay(cw),
			    ROOTWINDOW(cw),
			    hundred,
			    PAT_WIDTH, PAT_HEIGHT);
    for (i = 0; i < cw->chart.values; i++) {
      if (bw) {
	values.stipple = 
	  XCreateBitmapFromData(XtDisplay(cw),
				ROOTWINDOW(cw),
				&percents[i*PAT_WIDTH*PAT_HEIGHT/8],
				PAT_WIDTH, PAT_HEIGHT);
	values.foreground = cw->chart.foreground;
      }
      else {
	values.foreground = colors[i];
      }
      cw->chart.value_gc[i] = XtGetGC((Widget)cw, mask, &values);
    }

    /* get highlight GC to use is box is turned off*/
    values.foreground = cw->chart.highlight;
    values.stipple = 
      XCreateBitmapFromData(XtDisplay(cw),
			    ROOTWINDOW(cw),
			    hundred,
			    PAT_WIDTH, PAT_HEIGHT);
    cw->chart.value_gc[cw->chart.values] = 
      XtGetGC((Widget)cw, mask, &values);
    cw->chart.style_gc[cw->chart.styles] = 
      XtGetGC((Widget)cw, mask, &values);
}

/* ARGSUSED */
static void
ClassPartInitialize(class)
WidgetClass class;
{
  XtSetTypeConverter(XtRString, XtRPattern,
		     CvtStringToPattern, NULL, 0,
		     XtCacheAll, NULL); 
  XtSetTypeConverter(XtRString, XtRPatternList,
		     CvtStringToPatternList, NULL, 0,
		     XtCacheAll, NULL); 
}

/* ARGSUSED */
static void
Initialize(request, new)
ChartWidget request, new;
{
  /* 
   *  Check instance values set by resources that may be invalid. 
   */
  
  if (new->chart.twoD) new->chart.width = 1;
  
  if ((new->chart.width < 1) ||
      (new->chart.height < 1))  {
    XtWarning("Chart: pixmapWidth and/or pixmapHeight is too small (using 10 x 10)."); 
    new->chart.width = 10;
    new->chart.height = 10;
  }
  
  if (new->chart.cell_width < (2*new->chart.border + 1)) {
    XtWarning("Chart: cellWidth is too small."); 
    new->chart.cell_width = (2*new->chart.border + 1);
  }
  if (new->chart.cell_height < (2*new->chart.border + 1)) {
    XtWarning("Chart: cellHeight is too small."); 
    new->chart.cell_height = (2*new->chart.border + 1);
  }
  new->chart.save_width = new->chart.cell_width;
  new->chart.save_height = new->chart.cell_height;
  
  if ((new->chart.cur_x < 0) ||  (new->chart.cur_y < 0)) {
    XtWarning("Chart: cur_x and cur_y must be non-negative (using 0, 0)."); 
    new->chart.cur_x = 0;
    new->chart.cur_y = 0;
  }

  new->chart.save_y = new->chart.cur_y;
  new->chart.save_x = new->chart.cur_x;
  new->chart.save_border = new->chart.border;
  new->chart.save_frame = new->chart.frame;
  
  if (new->chart.cell == NULL)
    new->chart.cell = (double *)XtCalloc(new->chart.width * new->chart.height, sizeof(double));
  
  if (new->chart.state == NULL)
    new->chart.state = (Boolean *)XtCalloc(new->chart.width * new->chart.height, sizeof(Boolean));
  
  if (new->core.width == 0) {
    if (new->chart.twoD) {
      new->core.width = 200;
    }
    else new->core.width = new->chart.width * new->chart.cell_width +
      new->chart.border;
  }
  if (new->core.height == 0) {
    new->core.height = new->chart.height * new->chart.cell_height +
      new->chart.border;
  }
  new->core.height = MIN(new->core.height, 1000);
  new->core.width = MIN(new->core.width, 1000);  

  GetAllGC(new);
  if (new->chart.zoom) {
    Resize(new);
  }
  

}

/* ARGSUSED */
static void
Redisplay(cw, event)
ChartWidget cw;
XExposeEvent *event;
{
    register int x, y;
    int maxX, maxY;

    if (!XtIsRealized((Widget) cw))
	return;


    if (event) {  /* called from btn-event or expose */
      if (cw->chart.zoom && !cw->chart.rigid_zoom) {
	x = event->x / 
	  (double)((double)cw->core.width/(double)cw->chart.width);
	y = event->y / 
	  (double)((double)cw->core.height/(double)cw->chart.height);
	maxX = (event->x + event->width) / 
	  (double)((double)cw->core.width/(double)cw->chart.width);
	maxY = (event->y + event->height) / 
	  (double)((double)cw->core.height/(double)cw->chart.height);
      } else {
	x = cw->chart.cur_x + event->x / cw->chart.cell_width;
	y = cw->chart.cur_y + event->y / cw->chart.cell_height;
	maxX = cw->chart.cur_x + 
	  (event->width+event->x)/cw->chart.cell_width + 1;
	maxY = cw->chart.cur_y + 
	  (event->height+event->y)/cw->chart.cell_height + 1;
      }

      if (cw->chart.twoD) x = 0;

      maxX = MIN(maxX, cw->chart.width);
      maxY = MIN(maxY, cw->chart.height);
      x = MIN(x, cw->chart.width);
      y = MIN(y, cw->chart.height);
      
      /* draw current chart */
      DoCell(cw, x, y, maxX, maxY);
    } 
    else {        /* called because complete redraw */
      DrawIntoBigPixmap(cw);
   }

}

static void ScrollChart(cw, x0, y0, x1, y1)
ChartWidget cw;
int x0, y0, x1, y1;
{
  int y, x;
  unsigned int height, width, excess;
  XExposeEvent fake_event;
  unsigned int event_x, event_y, event_width, event_height;

  y = (y1 - y0) * cw->chart.cell_height;
  x = (x1 - x0) * cw->chart.cell_width;

  if (y != 0) {
    if (cw->chart.zoom && !cw->chart.rigid_zoom) {
      excess = 0;
    } else {
      excess = cw->core.height % cw->chart.cell_height;
    }
    if (y > 0) {
      y0 = y;
      y1 = 0;
      height = cw->core.height - y - 
	excess;
      fake_event.y = height;
      fake_event.height = y0;
      event_y = y0;
      event_height = cw->core.height - y0;
    }
    else {
      y0 = 0;
      y1 = -y;
      height = cw->core.height + y - 
	excess;
      fake_event.y = 0;
      fake_event.height = y1;
      event_y = 0;
      event_height = cw->core.height - y1;
    }
  }
  else {
    y0 = 0;
    y1 = 0;
    if (cw->chart.zoom && !cw->chart.rigid_zoom) {
      height = cw->core.height;
    } else {
      if (cw->core.height < cw->chart.height*cw->chart.cell_height)
	height = cw->core.height;
      else height = cw->chart.height*cw->chart.cell_height;
    }
    event_y = 0;
    event_height = cw->core.height;
  }
  if (x != 0) {
    if (cw->chart.zoom && !cw->chart.rigid_zoom) {
      excess = 0;
    } else {
      excess = cw->core.width % cw->chart.cell_width;
    }
    if (x > 0) {
      x0 = x;
      x1 = 0;
      width = cw->core.width - x -
	excess;
      event_x = width;
      event_width = x0;
    }
    else {
      x0 = 0;
      x1 = -x;
      width = cw->core.width + x - 
	excess;
      event_x = 0;
      event_width = x1;
    }
  }
  else {
    x0 = 0;
    x1 = 0;
    if (cw->chart.zoom && !cw->chart.rigid_zoom) {
      width = cw->core.width;
    } else {
      if (cw->core.width < cw->chart.width*cw->chart.cell_width)
	width = cw->core.width;
      else  {
	if (cw->chart.twoD) width = cw->core.width;
	else width = cw->chart.width*cw->chart.cell_width;
      }
    }
  }


  XCopyArea(XtDisplay(cw), XtWindow(cw), XtWindow(cw),
	    cw->chart.boxgc, x0, y0, width,
	    height, x1, y1);

  if (y != 0) {
    fake_event.x = 0;
    fake_event.width = width;
    Redisplay(cw, &fake_event);
  }
  if (x != 0) {
    fake_event.y = event_y;
    fake_event.height = event_height;
    fake_event.x = event_x;
    fake_event.width = event_width;
    Redisplay(cw, &fake_event);
  } 
}
   
static void
CalibrateXY(cw)
ChartWidget cw;
{   
  while ((cw->chart.cur_y > 0) &&
	 (cw->chart.cell_height*
	  (cw->chart.height-(cw->chart.cur_y-1)) < 
	  cw->core.height))
    cw->chart.cur_y--;
  if (!cw->chart.twoD)
    while ((cw->chart.cur_x > 0) &&
	   (cw->chart.cell_width*
	    (cw->chart.width-(cw->chart.cur_x-1)) < 
	    cw->core.width))
      cw->chart.cur_x--;
} 

static void
CalibrateSpace(cw)
ChartWidget cw;
{  
  int i;
  int original_frame = cw->chart.frame;
  unsigned int smallest; 
  if ((cw->chart.zoom) && (!cw->chart.rigid_zoom)) {
    smallest = MIN(cw->core.height/cw->chart.height,
		   cw->core.width/cw->chart.width);
  } else {
    smallest = MIN(cw->chart.cell_height, cw->chart.cell_width);
  } 
  cw->chart.border = cw->chart.save_border;
  cw->chart.frame = cw->chart.save_frame;

  if (smallest > 1) {
    while (smallest <= cw->chart.border+2*cw->chart.frame + 1) {
      cw->chart.border > cw->chart.frame ?
	cw->chart.border-- : cw->chart.frame--;
    }
  } else {
    cw->chart.border = 0;
    cw->chart.frame = 0;
  }
  if (cw->chart.frame != original_frame) {
    if (cw->chart.boxgc)
      XtReleaseGC((Widget) cw, cw->chart.boxgc);
    if (cw->chart.cleargc)
      XtReleaseGC((Widget) cw, cw->chart.cleargc);
    for(i = 0; i <= cw->chart.values; i++)
      if (cw->chart.value_gc[i])
	XtReleaseGC((Widget) cw, cw->chart.value_gc[i]);
    for(i = 0; i <= cw->chart.styles; i++)
      if (cw->chart.style_gc[i])
	XtReleaseGC((Widget) cw, cw->chart.style_gc[i]);
    GetAllGC((Widget) cw);
  } 
} 

/* ARGSUSED */
static Boolean
SetValues(current, request, new)
Widget current, request, new;
{
  
  ChartWidget curcw = (ChartWidget) current;
  ChartWidget newcw = (ChartWidget) new;
  Boolean do_redisplay = False;
  int i;
  
  if ((curcw->chart.foreground != newcw->chart.foreground) ||
      (curcw->chart.colors != newcw->chart.colors) ||
      (curcw->chart.patterns != newcw->chart.patterns)) {
    if (newcw->chart.boxgc)
      XtReleaseGC((Widget) newcw, newcw->chart.boxgc);
    if (newcw->chart.cleargc)
      XtReleaseGC((Widget) newcw, newcw->chart.cleargc);
    for(i = 0; i <= newcw->chart.values; i++)
      if (newcw->chart.value_gc[i])
	XtReleaseGC((Widget) newcw, newcw->chart.value_gc[i]);
    for(i = 0; i <= newcw->chart.styles; i++)
      if (newcw->chart.style_gc[i])
	XtReleaseGC((Widget) newcw, newcw->chart.style_gc[i]);
    GetAllGC((Widget) newcw);
    do_redisplay = True;
  }
  
  if ((curcw->chart.cur_x != newcw->chart.cur_x) || 
      (curcw->chart.cur_y != newcw->chart.cur_y)) {
    CalibrateXY(newcw);
    if (newcw->chart.cur_x < 0)
      newcw->chart.cur_x = 0;
    if (newcw->chart.cur_y < 0)
      newcw->chart.cur_y = 0;
    ScrollChart(newcw, curcw->chart.cur_x, curcw->chart.cur_y,
		newcw->chart.cur_x, newcw->chart.cur_y);
  }
  
  if (curcw->chart.log != newcw->chart.log)
    do_redisplay = True;
  
  
  if ((curcw->chart.width != newcw->chart.width) ||
      (curcw->core.width != newcw->core.width) ||
      (curcw->chart.height != newcw->chart.height) ||
      (curcw->core.height != newcw->core.height)) {
    if(newcw->core.height <= 0) {
      newcw->core.height = newcw->chart.height * 
              newcw->chart.cell_height +  newcw->chart.border;
      XtWarning("Correct the Chart height (0)");
    }
/* 4/1992 found redundant hui when debugging from R4->R5
    Resize(newcw); 
*/
    do_redisplay = True;
  }  

  if (curcw->chart.twoD != newcw->chart.twoD) {
    if (newcw->chart.twoD) {
      if (!curcw->chart.zoom)
	newcw->chart.save_x = newcw->chart.cur_x;
      newcw->chart.cur_x = 0;
    } else {
      if (!curcw->chart.zoom)
	newcw->chart.cur_x = newcw->chart.save_x;
    }
  }
  if (curcw->chart.zoom != newcw->chart.zoom) {
    if (newcw->chart.zoom) {
      newcw->chart.save_y = newcw->chart.cur_y;
      newcw->chart.cur_y = 0;
      if (!newcw->chart.twoD) {
	newcw->chart.save_x = newcw->chart.cur_x;
	newcw->chart.cur_x = 0;
      }
    } else {
	newcw->chart.cur_y = newcw->chart.save_y;
	if (!newcw->chart.twoD)
	  newcw->chart.cur_x = newcw->chart.save_x;
      }
  }
  
  if ((curcw->chart.zoom != newcw->chart.zoom) ||
      (curcw->chart.twoD != newcw->chart.twoD)){
    newcw->chart.cell_height = newcw->chart.save_height;
    newcw->chart.cell_width = newcw->chart.save_width;
    Resize(newcw); 
    if (!newcw->chart.zoom)
      CalibrateSpace(newcw);
    do_redisplay = True;
  }
  
  return do_redisplay;
}


static void
Destroy(cw)
ChartWidget cw;
{   
  int i;

  if (cw->chart.boxgc)
    XtReleaseGC((Widget) cw, cw->chart.boxgc);
  if (cw->chart.cleargc)
    XtReleaseGC((Widget) cw, cw->chart.cleargc);
  for(i = 0; i <= cw->chart.values; i++)
    if (cw->chart.value_gc[i])
      XtReleaseGC((Widget) cw, cw->chart.value_gc[i]);
  XtFree((char *)cw->chart.value_gc);
  for(i = 0; i <= cw->chart.styles; i++)
    if (cw->chart.style_gc[i])
      XtReleaseGC((Widget) cw, cw->chart.style_gc[i]);
  XtFree((char *)cw->chart.style_gc);
}


static Boolean
GetCoord(w, event, xPtr, yPtr)
ChartWidget w;
XButtonEvent *event;
int * xPtr, * yPtr;
{

  if (w->chart.zoom && !w->chart.rigid_zoom) {
    *xPtr = event->x / 
      (double)((double)w->core.width/(double)w->chart.width);
    *yPtr = event->y / 
      (double)((double)w->core.height/(double)w->chart.height);

    if (w->chart.twoD) *xPtr = 0;

    if (*xPtr >= w->chart.width)
      *xPtr = w->chart.width-1;
    if (*yPtr >= w->chart.height)
      *yPtr = w->chart.height-1;
  } else {
    *xPtr = w->chart.cur_x + event->x / w->chart.cell_width;
    *yPtr = w->chart.cur_y + event->y / w->chart.cell_height;
    
    if (w->chart.twoD) *xPtr = 0;
    
    if ((*xPtr >= (w->core.width/w->chart.cell_width)+w->chart.cur_x) ||
	(*yPtr >= (w->core.height/w->chart.cell_height)+w->chart.cur_y) ||
	(*xPtr < w->chart.cur_x) || (*yPtr < w->chart.cur_y) ||
	(*xPtr >= w->chart.width) || (*yPtr >= w->chart.height))
      return False;
  }

  return True;
}

static void
SelectCell(w, event)
ChartWidget w;
XButtonEvent *event;
{
  int newx;
  int newy;

  if (!GetCoord(w, event, &newx, &newy)) return;

  w->chart.state[newx + newy * w->chart.width] =
    !w->chart.state[newx + newy * w->chart.width];
  
  w->chart.tog_state = 
    w->chart.state[newx + newy * w->chart.width];

  w->chart.info.state = w->chart.tog_state; 
  w->chart.info.x = newx;
  w->chart.info.y = newy;
  XtCallCallbacks((Widget) w, XtNcallback, (XtPointer)&(w->chart.info));

  DoCell(w, newx, newy, 
	 (int)(w->chart.twoD ? w->chart.width : newx+1), newy+1);
}

static void
ManyCells(w, event)
ChartWidget w;
XButtonEvent *event;
{
  int newx;
  int newy;

  if (!GetCoord(w, event, &newx, &newy)) return;

  if (w->chart.state[newx + newy * w->chart.width] !=
      w->chart.tog_state) {
    w->chart.state[newx + newy * w->chart.width] =
      w->chart.tog_state;
    w->chart.info.state = w->chart.tog_state; 
    w->chart.info.x = newx;
    w->chart.info.y = newy;
    XtCallCallbacks((Widget) w, XtNcallback, (XtPointer)&(w->chart.info));
    
    DoCell(w, newx, newy, 
	   (int)(w->chart.twoD ? w->chart.width : newx+1), newy+1);
  }
}


static void
DrawIntoBigPixmap(cw)
ChartWidget cw;
{
  int maxX, maxY;

  if (cw->chart.zoom) {
    DoCell(cw, 0, 0, (int)cw->chart.width, (int)cw->chart.height);
  } else {
    maxX = cw->chart.cur_x + cw->core.width/cw->chart.cell_width;
    maxY = cw->chart.cur_y + cw->core.height/cw->chart.cell_height;

    if (maxX > cw->chart.width)
      maxX = cw->chart.width;
    if (maxY > cw->chart.height)
      maxY = cw->chart.height;

    DoCell(cw, cw->chart.cur_x, cw->chart.cur_y, maxX, maxY);
  }
}

/* A Public function, not static */
double *
ChartGetArrayString(w)
ChartWidget w;
{
    return (w->chart.cell);
}

static void
Resize(cw)
ChartWidget cw;
{
  if (cw->chart.zoom) {
    /* 
     * Calculate the maximum cell size that will allow the
     * entire chart to be displayed.
     */
    cw->chart.cell_width = 
      MAX(cw->core.width / cw->chart.width, 1);
    cw->chart.cell_height = 
      MAX(cw->core.height / cw->chart.height, 1);
    CalibrateSpace(cw);
  }
  else {
    CalibrateXY(cw);
  }
  XtCallCallbacks((Widget) cw, XtNchartResize, (XtPointer)NULL);

}
 
static void
GetRectangle(w, x, y, pIndex, pRectangle)
ChartWidget w;
int x, y;
int * pIndex;
XRectangle * pRectangle;
{
  double percent;
  
  if (w->chart.zoom && !w->chart.rigid_zoom) {
    pRectangle->y = 
      (int)((double)y*(double)w->core.height/(double)w->chart.height) +
	w->chart.border;
    pRectangle->height =  
      (int)((double)(y+1)*(double)w->core.height/(double)w->chart.height)
	+ w->chart.border - pRectangle->y;      
  } else {
    pRectangle->y = w->chart.cell_height * (y - w->chart.cur_y)
      + w->chart.border;
    pRectangle->height = (unsigned int)w->chart.cell_height;
  }
  percent = w->chart.cell[x + y*w->chart.width];
  if (w->chart.twoD) {
    *pIndex = w->chart.values-1;
    pRectangle->x = w->chart.border;
    pRectangle->width = percent*w->core.width;
  }
  else {
    *pIndex = percent*(double)w->chart.values;
    if (*pIndex >= w->chart.values) *pIndex = w->chart.values-1;
    if (w->chart.zoom && !w->chart.rigid_zoom) {    
      pRectangle->x = 
	(int)((double)x*(double)w->core.width/(double)w->chart.width) +
	  w->chart.border;
      pRectangle->width =  
	(int)((double)(x+1)*(double)w->core.width/(double)w->chart.width)
	  + w->chart.border - pRectangle->x;
    } else {
      pRectangle->x = w->chart.cell_width * (x - w->chart.cur_x)
	+ w->chart.border;
      pRectangle->width = (unsigned int)w->chart.cell_width;
    }
  }
/*  if (pRectangle->width < w->chart.border+2*w->chart.frame)
    pRectangle->width = w->chart.border+2*w->chart.frame; */
  if (pRectangle->height < w->chart.border+2*w->chart.frame)
    pRectangle->height = w->chart.border+2*w->chart.frame;
}

static void
DoCell(w, x0, y0, x1, y1)
ChartWidget w;
int x0, y0, x1, y1;
{
  int x, y;
  int fill_types;
  int next_x;
  int frame, index;
  XRectangle rectangle;
  XRectangle * drawRect;
  int drawRects = 0;
  XRectangle ** fillRect;
  int * fillRects;
  XRectangle * clearRect;
  int clearRects = 0;
  int i, nRects;
  Display * disp;
  Window win;
  
  nRects = (x1-x0)*(y1-y0);
  drawRect = (XRectangle *)XtMalloc(nRects * sizeof(XRectangle));
  clearRect = (XRectangle *)XtMalloc(nRects * sizeof(XRectangle));
  fill_types = (w->chart.twoD ? w->chart.styles : w->chart.values)+1;

  fillRect = (XRectangle **)XtMalloc(fill_types * sizeof(XRectangle *));
  fillRects = (int *)XtMalloc(fill_types * sizeof(int));
  for(i = 0; i < fill_types; i++) {
    fillRect[i] = (XRectangle *)XtMalloc(nRects * sizeof(XRectangle));
    fillRects[i] = 0;
  }
  
  for (y = y0; y < y1; y++) {
    for(x = x0; x < x1; x++) {
      GetRectangle(w, x, y, &index, &rectangle);  
      
      if ((rectangle.y+rectangle.height-1 <= w->core.height) &&
	  (rectangle.x+rectangle.width-1 <= w->core.width)) {
	if (w->chart.twoD) {
	  if (w->chart.state[x0 + y * w->chart.width]) 
	    frame = w->chart.frame;
	  else frame = 0;
	  
	  index = x - x0;
	  if (x > x0) rectangle.x = next_x;
	  else rectangle.width -= w->chart.border+frame;
	  next_x = rectangle.x+rectangle.width;
	}
	if (!w->chart.twoD || (x == x1-1)) {
	  if (w->chart.box && (w->chart.frame > 0)) {
	    if (w->chart.state[(w->chart.twoD ? x0 : x) +
			       y * w->chart.width]) {
	      frame = w->chart.frame;
	      if (w->chart.twoD) {
		drawRect[drawRects].x = w->chart.border+frame/2;
		drawRect[drawRects].width = next_x-w->chart.border;
	      } else {
		drawRect[drawRects].x = rectangle.x+frame/2;
		drawRect[drawRects].width =
		  rectangle.width-w->chart.border-frame;
	      }
	      drawRect[drawRects].y = rectangle.y+frame/2;
	      drawRect[drawRects].height = 
		rectangle.height-w->chart.border-frame;
	      drawRects++;	    
	    } else {
	      frame = 0;
	    }
	  } else {
	    frame = 0;
	    if (w->chart.state[(w->chart.twoD ? x0 : x) +
			       y * w->chart.width]) 
	      index = w->chart.twoD ? w->chart.styles : w->chart.values;
	  }
	}
	if (w->chart.twoD)
	  if (x != x1-1) {
	    fillRect[index][fillRects[index]].width =
	      rectangle.width;
	  } else {
	    fillRect[index][fillRects[index]].width =
	      rectangle.width - frame;
	  }
	else
	  fillRect[index][fillRects[index]].width =
	    rectangle.width-w->chart.border-2*frame;
	
	fillRect[index][fillRects[index]].x = rectangle.x+frame;
	fillRect[index][fillRects[index]].y = rectangle.y+frame;
	fillRect[index][fillRects[index]].height = 
	  rectangle.height-w->chart.border-2*frame;
	fillRects[index]++;
	
	if (w->chart.twoD && (x == x1-1)) {
	  clearRect[clearRects].x = next_x+frame;
	  clearRect[clearRects].y = rectangle.y;
	  clearRect[clearRects].width =
	    w->core.width-next_x-frame;
	  clearRect[clearRects].height = 
	   rectangle.height;
	  clearRects++;
	}
      }
    }
  }
  if (drawRects > 0) {
    disp = XtDisplay(w);
    win = XtWindow(w);
    XDrawRectangles(disp, win,
		    w->chart.boxgc,
		    drawRect, drawRects);
  }
  XtFree((char *)drawRect);
  for (i=0; i < fill_types; i++) {
    if (fillRects[i] >0)
      XFillRectangles(XtDisplay(w), XtWindow(w),
		      w->chart.twoD ? 
		      w->chart.style_gc[i] : w->chart.value_gc[i],
		      fillRect[i], fillRects[i]);
    XtFree((char *)fillRect[i]);
  }
  XtFree((char *)fillRect);
  
  if (clearRects > 0)
    XFillRectangles(XtDisplay(w), XtWindow(w),
		    w->chart.cleargc,
		    clearRect, clearRects);
  XtFree((char *)clearRect);
}


int
XswChartNumValues(w)
ChartWidget w;
{
  return w->chart.values;
}

void
XswChartSetRow(w, row, state)
ChartWidget w;
int row;
Boolean state;
{
  int j;

  for(j = 0; j < w->chart.width; j++) {
    if (w->chart.state[j + row * w->chart.width] != state) {
      w->chart.state[j + row * w->chart.width] = state;
      w->chart.info.state = state; 
      w->chart.info.x = j;
      w->chart.info.y = row;
      XtCallCallbacks((Widget) w, XtNcallback, &(w->chart.info));
    }
  }
  
  if (w->chart.zoom) {
    DoCell(w, 0, row, (int)w->chart.width, row + 1);
  } else {
    DoCell(w, w->chart.cur_x, row,
	   (int)MIN(w->core.width/w->chart.cell_width + w->chart.cur_x,
		 (int)w->chart.width),
	   row + 1);
  }
}

void
XswChartSetCol(w, col, state)
ChartWidget w;
int col;
Boolean state;
{
  int j;

  for(j = 0; j < w->chart.height; j++) {
    if (w->chart.state[col + j * w->chart.width] != state) {
      w->chart.state[col + j * w->chart.width] = state;
      w->chart.info.state = state; 
      w->chart.info.x = col;
      w->chart.info.y = j;
      XtCallCallbacks((Widget) w, XtNcallback, &(w->chart.info));
    }
  }
  if (w->chart.zoom) {
    DoCell(w, col, 0, col + 1, (int)w->chart.height);
  } else {
    DoCell(w, col, w->chart.cur_y, col + 1,
	   (int)MIN(w->core.height/w->chart.cell_height + w->chart.cur_y,
		    w->chart.height));
  }
}

void XswChartRedisplay(w)
ChartWidget w;
{
  if (!XtIsRealized((Widget) w))
    return;
  else
    DrawIntoBigPixmap(w);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.