/* $Header: /afs/athena.mit.edu/astaff/project/atdev/src/plotter/RCS/Plotter.c,v 3.11 94/09/23 12:16:53 dot Exp Locker: dot $ */

/*******************************************************************
  Copyright (C) 1990 by the Massachusetts Institute of Technology

   Export of this software from the United States of America is assumed
   to require a specific license from the United States Government.
   It is the responsibility of any person or organization contemplating
   export to obtain such a license before exporting.

WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
distribute this software and its documentation for any purpose and
without fee is hereby granted, provided that the above copyright
notice appear in all copies and that both that copyright notice and
this permission notice appear in supporting documentation, and that
the name of M.I.T. not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.  M.I.T. makes no representations about the suitability of
this software for any purpose.  It is provided "as is" without express
or implied warranty.

***************************************************************** */

#include <math.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>    

#ifdef _AtDevelopment_
#include "AtConverters.h"    
#include "Scale.h"    
#include "PlotterP.h"
#include "AxisP.h"
#include "PlotP.h"
#include "BarchartP.h"
#else
#include <At/AtConverters.h>
#include <At/Scale.h>   
#include <At/PlotterP.h>
#include <At/AxisP.h>
#include <At/PlotP.h>
#include <At/BarchartP.h>
#endif

#define XSCALE(w) (w->plotter.xaxis->axis.scale)
#define YSCALE(w,i) ((CONSTRAINT(w,i)->plotter.useY2Axis)?(w->plotter.y2axis->axis.scale):(w->plotter.yaxis->axis.scale))
#define YSCALEPLOT(pw, p) ((((AtPlotterConstraints)(p->core.constraints))->plotter.useY2Axis)?(pw->plotter.y2axis->axis.scale):(pw->plotter.yaxis->axis.scale))
#define REGION(pw) ((Region) pw->plotter.clip_region)
    
#define NUMCHILDREN(w) (w->composite.num_children)
#define CHILD(w,i) (w->composite.children[i])
#define CONSTRAINT(w,i) ((AtPlotterConstraints)(CHILD(w,i)->core.constraints))
#define ISDISPLAYED(cw) (((AtPlotterConstraints)((AtPlotWidgetClass)(cw)->core.constraints))->plotter.displayed)
#define NTHCHILDISDISPLAYED(pw,i) (CONSTRAINT(pw, i)->plotter.displayed)
#define REDRAWPLOT(pw, p, r) (*((AtPlotWidgetClass)p->core.widget_class)->plot_class.draw)(XtDisplay(pw), XtWindow(pw), (AtPlotWidget)p, XSCALE(pw), YSCALEPLOT(pw, p), r)
#define REDRAWCHILD(w,i,r) (*((AtPlotWidgetClass)CHILD(w,i)->core.widget_class)->plot_class.draw)(XtDisplay(w), XtWindow(w), (AtPlotWidget)CHILD(w,i), XSCALE(w), YSCALE(w,i), r)
#define RESIZECHILD(w,i) (*((AtPlotWidgetClass)CHILD(w,i)->core.widget_class)->plot_class.resize)((AtPlotWidget)CHILD(w,i), XSCALE(w), YSCALE(w,i))
#define RESCALECHILD(w,i) (*((AtPlotWidgetClass)CHILD(w,i)->core.widget_class)->plot_class.rescale)((AtPlotWidget)CHILD(w,i), XSCALE(w), YSCALE(w,i))
#define HITPROC(w,i) (*((AtPlotWidgetClass)CHILD(w,i)->core.widget_class)->plot_class.checkhit)
#define CHECKHIT(w, i, x, y) ((HITPROC(w,i)==NULL) ? False : (HITPROC(w,i))((AtPlotWidget)CHILD(w,i),x,y))
#define LEGENDICON(w,i,x,y,width,height) (*((AtPlotWidgetClass)CHILD(w,i)->core.widget_class)->plot_class.drawIcon)(XtDisplay(w), XtWindow(w), (AtPlotWidget)CHILD(w,i),x,y,width,height)

#define BARGETRECTLIST(pw,w,r,rl,nrl)(*((AtBarchartWidgetClass)w->core.widget_class)->barchart_class.getrects)((AtPlotWidget)w, XSCALE(pw), YSCALEPLOT(pw,w), r, rl, nrl)
#define BARREDRAW(pw,w,rct)(*((AtBarchartWidgetClass)w->core.widget_class)->barchart_class.redraw)(XtDisplay(pw), XtWindow(pw), (AtPlotWidget)w, rct)
#define BARDRAWINC(pw,w,r)(*((AtBarchartWidgetClass)w->core.widget_class)->barchart_class.drawinc)(XtDisplay(pw), XtWindow(pw), (AtPlotWidget)w, XSCALE(pw), YSCALEPLOT(pw,w), r)


#define AtWarning(w,msg) XtWarning(msg)

/* Shouldn't need this in R4.  Put in because XtNewString(NULL) dumps core
   on the DECstation. */
#define AtNewString(str) ((str) != NULL ? \
			  (strcpy(XtMalloc((unsigned)strlen(str) + 1), str)) : NULL)

#define SelectNONE 0
#define SelectREGION 1
#define SelectLEGEND 2

void AtPlotterLayout(AtPlotterWidget);
void ReRankOrderChildren(Widget);
static void RankOrderChildren(Widget);
static void RankOrderRemove(Widget);
static void PlotterError(AtPlotterWidget, char *);

void Autoscale(AtPlotterWidget);

static void RecalcLegend(AtPlotterWidget w);
static void ResizeLegend(AtPlotterWidget w);
static void RedrawLegend(AtPlotterWidget w);

static void StartSelection();
static void Drag();
static void EndSelection();
static void CancelSelection();
static void HandleMotion();

/*  Default translation table and action list  */

static char defaultTranslations[] =
    "<Btn1Down>:       start-selection() \n\
     <Btn1Motion>:     drag() \n\
     <Btn1Up>:         end-selection() \n\
     <Btn3Down>:       cancel-selection() \n\
     <Key>Escape:      cancel-selection() \n\
     <Motion>:         motion-notify()";

static XtActionsRec actions[] =
{
  { "motion-notify", (XtActionProc) HandleMotion },
  { "start-selection", (XtActionProc) StartSelection },
  { "drag", (XtActionProc) Drag },
  { "end-selection", (XtActionProc) EndSelection},
  { "cancel-selection", (XtActionProc) CancelSelection},
};
    
#define offset(field) XtOffset(AtPlotterWidget,field)
static XtResource plotter_resources[] = {
    {XtNfontFamily, XtCFontFamily, XtRString,
     sizeof(String), offset(plotter.fontFamily),
     XtRString, "new century schoolbook"},
    {XtNtitle, XtCTitle, XtRString,
     sizeof(String), offset(plotter.title), XtRString, NULL},
    {XtNlegendTitle, XtCLegendTitle, XtRString,
     sizeof(String), offset(plotter.legendTitle), XtRString, "Legend"},
    {XtNtitleSize, XtCFontSize, XtRFontSize,
     sizeof(int), offset(plotter.titleSize),
     XtRImmediate, (caddr_t)AtFontBIG},
    {XtNlegendTitleSize, XtCFontSize, XtRFontSize,
     sizeof(int), offset(plotter.legendTitleSize),
     XtRImmediate, (caddr_t)AtFontNORMAL},
    {XtNlegendSize, XtCFontSize, XtRFontSize,
     sizeof(int), offset(plotter.legendSize),
     XtRImmediate, (caddr_t)AtFontNORMAL},
    {XtNtitleColor, XtCForeground, XtRPixel, sizeof(Pixel),
     offset(plotter.titleColor), XtRString, (caddr_t)XtDefaultForeground},
    {XtNlegendColor, XtCForeground, XtRPixel, sizeof(Pixel),
     offset(plotter.legendColor), XtRString, (caddr_t)XtDefaultForeground},
    {XtNautoScale, XtCAutoScale, XtRBoolean,
     sizeof(Boolean), offset(plotter.autoScale), XtRImmediate, (caddr_t)True},
    {XtNforceSquare, XtCForceSquare, XtRBoolean,
     sizeof(Boolean), offset(plotter.forceSquare),XtRImmediate,(caddr_t)False},
    {XtNforceAspect, XtCForceAspect, XtRBoolean, sizeof(Boolean),
     offset(plotter.forceAspect), XtRImmediate, (caddr_t)False},
    {XtNfloatingX, XtCFloatingAxes, XtRBoolean,
     sizeof(Boolean), offset(plotter.floatingX), XtRImmediate, (caddr_t)False},
    {XtNfloatingY, XtCFloatingAxes, XtRBoolean,
     sizeof(Boolean), offset(plotter.floatingY), XtRImmediate, (caddr_t)False},
    {XtNframedAxes, XtCFramedAxes, XtRBoolean,
     sizeof(Boolean), offset(plotter.framedAxes), XtRImmediate,(caddr_t)False},
    {XtNshowLegend, XtCShowLegend, XtRBoolean,
     sizeof(Boolean), offset(plotter.showLegend), XtRImmediate, (caddr_t)True},
    {XtNlegendWidth, XtCLegendWidth, XtRShort,
     sizeof(short), offset(plotter.legendWidth), XtRImmediate, (caddr_t)100},
    {XtNlegendSpacing, XtCMargin, XtRShort,
     sizeof(short), offset(plotter.legendSpacing), XtRImmediate, (caddr_t) 3},
    {XtNmarginWidth, XtCMargin, XtRShort,
      sizeof(short), offset(plotter.marginWidth), XtRImmediate, (caddr_t)3},
    {XtNmarginHeight, XtCMargin, XtRShort,
     sizeof(short), offset(plotter.marginHeight), XtRImmediate, (caddr_t)3},
    {XtNrankChildren, XtCRankChildren, XtRBoolean, sizeof(Boolean), 
       offset(plotter.rankChildren), XtRImmediate, (caddr_t)False},
    {XtNdoubleClickInterval, XtCDoubleClickInterval, XtRInt, sizeof(int),
     offset(plotter.doubleClickInterval), XtRImmediate, (caddr_t)250},
    {XtNmotionCallback, XtCCallback, XtRCallback,sizeof(XtCallbackList),
     offset(plotter.motionCallback), XtRCallback, NULL},
    {XtNclickCallback, XtCCallback, XtRCallback,sizeof(XtCallbackList),
     offset(plotter.clickCallback), XtRCallback, NULL},
    {XtNdragCallback, XtCCallback, XtRCallback,sizeof(XtCallbackList),
     offset(plotter.dragCallback), XtRCallback, NULL},
    {XtNselectCallback, XtCCallback, XtRCallback,sizeof(XtCallbackList),
     offset(plotter.selectCallback), XtRCallback, NULL},
    {XtNdoubleSelectCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
     offset(plotter.doubleSelectCallback), XtRCallback, NULL},
    {XtNerrorCallback, XtCCallback, XtRCallback,sizeof(XtCallbackList),
     offset(plotter.errorCallback), XtRCallback, NULL}
};
#undef offset

/*
 * The default bounding box is a nonsensical one, with xmin > xmax.
 * Unless it is modified to a sensible value, Autoscale() will ignore it.
 * This is so that TextPlots (annotations) and similar plot types
 * won't be involved in the autoscale computations.
 */
static BoundingBox defaultBoundingBox = {1.0, 0.0, 0.0, 0.0};

#define offset(field) XtOffset(AtPlotterConstraints,field)
static XtResource constraint_resources[] = {
{XtNdisplayed, XtCDisplayed, XtRBoolean,
     sizeof(Boolean), offset(plotter.displayed),
     XtRImmediate, (caddr_t)True},
{XtNneedsRedraw, XtCNeedsRedraw, XtRBoolean,
     sizeof(Boolean), offset(plotter.needsRedraw),
     XtRImmediate, (caddr_t)False},
{XtNneedsRefresh, XtCNeedsRefresh, XtRBoolean,
     sizeof(Boolean), offset(plotter.needsRefresh),
     XtRImmediate, (caddr_t)False},
{XtNlegendName, XtCLegendName, XtRString,
     sizeof(String), offset(plotter.legendName),
     XtRString, NULL},
{XtNuseY2Axis, XtCUseY2Axis, XtRBoolean,
     sizeof(Boolean), offset(plotter.useY2Axis),
     XtRImmediate, (caddr_t)False},
{XtNboundingBox, XtCBoundingBox, XtRBoundingBox,
     sizeof(BoundingBox), offset(plotter.boundingBox),
     XtRBoundingBox, (caddr_t)&defaultBoundingBox},
{XtNrankOrder, XtCRankOrder, XtRInt, sizeof(int), 
   offset(plotter.rankOrder), XtRImmediate, (caddr_t) 0},
};
#undef offset

static void ClassInitialize();
static void Initialize(AtPlotterWidget, AtPlotterWidget);
static void Destroy(AtPlotterWidget);
static void Resize(AtPlotterWidget);
static void Realize(AtPlotterWidget, XtValueMask *,XSetWindowAttributes *);
static void Redisplay(AtPlotterWidget, XEvent *, Region);
static Boolean SetValues(AtPlotterWidget, AtPlotterWidget, AtPlotterWidget);
static void InsertChild(Widget);
static void DeleteChild(Widget);
static void ConstraintInitialize(Widget, Widget);
static void ConstraintDestroy(Widget);
static Boolean ConstraintSetValues(Widget, Widget, Widget);
static void AxisNeedsRedraw();
static void AxisErrorCB(AtAxisObject, AtPlotterWidget, String);

#define superclass (&constraintClassRec)

externaldef(compositeclassrec) AtPlotterClassRec atPlotterClassRec = {
  { /******* CoreClassPart *******/
    /* superclass           */  (WidgetClass) superclass,
    /* class_name           */  "AtPlotter",
    /* widget_size          */  sizeof(AtPlotterRec),
    /* class_initialize     */  ClassInitialize,
    /* class_part_initialize*/  NULL,
    /* class_inited         */  FALSE,
    /* initialize           */  (XtInitProc)Initialize,
    /* initialize_hook      */  NULL,
    /* realize              */  Realize,
    /* actions              */  actions,
    /* num_actions          */  XtNumber(actions),
    /* resources            */  plotter_resources,
    /* num_resources        */  XtNumber(plotter_resources),
    /* xrm_class            */  NULLQUARK,
    /* compress_motion      */  FALSE,
#ifdef X11R4_INTRINSICS
    /* compress_exposure    */  XtExposeCompressMultiple |
                                XtExposeGraphicsExposeMerged,
#else
    /* compress_exposure    */  TRUE,
#endif
    /* compress_enterleave  */  TRUE,
    /* visible_interest     */  FALSE,
    /* destroy              */  (XtWidgetProc)Destroy,
    /* resize               */  (XtWidgetProc)Resize,
    /* expose               */  (XtExposeProc)Redisplay,
    /* set_values           */  (XtSetValuesFunc) SetValues,
    /* set_values_hook      */  NULL,
    /* set_values_almost    */  XtInheritSetValuesAlmost,
    /* get_values_hook      */  NULL,
    /* accept_focus         */  NULL,
    /* version              */  XtVersion,
    /* callback_offsets     */  NULL,
    /* tm_table             */  defaultTranslations,
    /* query_geometry       */  NULL,
    /* display_accelerator  */  NULL,
    /* extension            */  NULL
  },
  { /**** CompositeClassPart ****/
    /* geometry_handler     */  NULL,
    /* change_managed       */  NULL,
    /* insert_child         */  (XtWidgetProc)InsertChild,
    /* delete_child         */  (XtWidgetProc)DeleteChild,
    /* extension            */  NULL
  },
  { /**** ConstraintClassPart ****/
    /* resources            */  constraint_resources,
    /* num_resources        */  XtNumber(constraint_resources),
    /* constraint_size      */  sizeof(AtPlotterConstraintsRec),
    /* initialize           */  (XtInitProc) ConstraintInitialize,
    /* destroy              */  (XtWidgetProc) ConstraintDestroy,
    /* set_values           */  (XtSetValuesFunc) ConstraintSetValues,
    /* extension            */  NULL,
  },
  { /**** AtPlotterClassPart ****/
    /* meaningless field    */  0
  }
};

WidgetClass atPlotterWidgetClass = (WidgetClass) &atPlotterClassRec;


static void ClassInitialize()
{
#ifdef X11R4_INTRINSICS
  static CompositeClassExtensionRec ext;

  ext.next_extension = NULL;
  ext.record_type = NULLQUARK;
  ext.version = XtCompositeExtensionVersion;
  ext.record_size = sizeof(CompositeClassExtensionRec);
  ext.accepts_objects = True;
  atPlotterClassRec.composite_class.extension = (XtPointer) &ext;
#endif    
  AtRegisterDoubleConverter();
  AtRegisterFontSizeConverter();

  if (_AtStringToTransformRegistered == False) {
    XtAddConverter(XtRString, XtRTransform,
AtCvtStringToTransform, 
		   NULL,0);
    _AtStringToTransformRegistered = True;
  }    
}

static void GetTitle(AtPlotterWidget w)
{
  if (w->plotter.title != NULL) 
    w->plotter.titleText = AtTextCreate(w->plotter.title,
					w->plotter.ff,
					w->plotter.titleSize);
  else
    w->plotter.titleText = NULL;
}

static void FreeTitle(AtPlotterWidget w)
{
  if (w->plotter.titleText) AtTextDestroy(w->plotter.titleText);
  w->plotter.titleText = NULL;
}

static void GetLegendTitle(AtPlotterWidget w)
{
  if (w->plotter.legendTitle != NULL) 
    w->plotter.legendTitleText = AtTextCreate(w->plotter.legendTitle,
					      w->plotter.ff,
					      w->plotter.legendTitleSize);
  else
    w->plotter.legendTitleText = NULL;
}

static void FreeLegendTitle(AtPlotterWidget w)
{
  if (w->plotter.legendTitleText) AtTextDestroy(w->plotter.legendTitleText);
  w->plotter.legendTitleText = NULL;
}


/************************************************************************
 *
 *  Initialize
 *     The main widget instance initialization routine.
 *
 ************************************************************************/

static void Initialize (AtPlotterWidget request,
			AtPlotterWidget new)
{
  XGCValues gcv;
  Arg al[10];
  int ac;

  /* make private copies of string resource */
  new->plotter.fontFamily = AtNewString(new->plotter.fontFamily);
  new->plotter.title = AtNewString(new->plotter.title);
  new->plotter.legendTitle = AtNewString(new->plotter.legendTitle);

  new->plotter.ff = AtFontFamilyGet(XtDisplay(new), 
				    new->plotter.fontFamily);
  GetTitle(new);
  GetLegendTitle(new);

  gcv.foreground = new->plotter.titleColor;
  new->plotter.titleGC = XtGetGC((Widget)new, GCForeground, &gcv);
  
  gcv.foreground = new->plotter.legendColor;
  new->plotter.legendGC = XtGetGC((Widget)new, GCForeground, &gcv);

  gcv.foreground = new->core.background_pixel ^ new->plotter.titleColor;
  gcv.function = GXxor;
  new->plotter.dragGC = XtGetGC((Widget)new, GCForeground | GCFunction, &gcv);

#ifdef X11R4_INTRINSICS
  new->plotter.xaxis = (AtAxisObject)
    XtVaCreateWidget("xaxis", atAxisObjectClass, (Widget)new,
			    XtNvertical, False, NULL);

#else
  XtSetArg (al[0], XtNvertical, False);
  new->plotter.xaxis = (AtAxisObject)
    XtCreateWidget("xaxis", atAxisObjectClass, (Widget)new,
			    al, 1);
#endif

  new->plotter.yaxis = (AtAxisObject)
    XtCreateWidget("yaxis", atAxisObjectClass, (Widget)new, NULL, 0);

  ac = 0;
  XtSetArg(al[ac], XtNmirror, True); ac++;

  /*
   * Check the "sub" resources for y2 axis and see if it is displayed...
   * (This is needed so we can emulate the old default behaviour of X
   * and Y axes displayed, Y2 not displayed.  We check the resource
   * file before we create y2axis so we can override the default True
   * if we need to!)
   */
  {
    static Boolean disp;
    static XtResource disp_res = {
      XtNdisplayed, XtCDisplayed, XtRBoolean, sizeof(Boolean), 0,
      XtRImmediate, (caddr_t)False }; /* NB - default = FALSE!!! */

    XtGetSubresources(new, &disp, "y2axis", "AtAxis", &disp_res, 1,
		      NULL, 0);
    if (!disp) {
      XtSetArg(al[ac], XtNdisplayed, False);
      ac++;
    }
  }
  new->plotter.y2axis = (AtAxisObject)
    XtCreateWidget("y2axis", atAxisObjectClass, new, al, ac); 

  ac = 0;
  XtSetArg(al[ac], XtNmirror, True); ac++;
  XtSetArg(al[ac], XtNautoNumber, False); ac++;
  XtSetArg(al[ac], XtNvertical, False); ac++;
  new->plotter.xframeAxis = (AtAxisObject)
    XtCreateManagedWidget("xframeAxis", atAxisObjectClass, new, al, ac);

  ac--;				/* y axes are not vertical! */
  new->plotter.yframeAxis = (AtAxisObject)
    XtCreateManagedWidget("yframeAxis", atAxisObjectClass, new, al, ac);

  /* Y2 frame is neither mirrored nor vertical! */
#ifdef X11R4_INTRINSICS
  new->plotter.y2frameAxis = (AtAxisObject)
    XtVaCreateManagedWidget("y2frameAxis", atAxisObjectClass, (Widget)new,
			    XtNautoNumber, False, NULL);
#else
  XtSetArg (al[0], XtNautoNumber, False);
  new->plotter.y2frameAxis = (AtAxisObject)
    XtCreateManagedWidget("y2frameAxis", atAxisObjectClass, (Widget)new,
			    al, 1);
#endif

  if (NUMCHILDREN(new) != FIRST_PUBLIC_CHILD || 
      new->plotter.y2frameAxis != (AtAxisObject)CHILD(new, CHILD_Y2FRAMEAXIS)) {
    PlotterError(new, 
		 "AtPlotter Initialize: Private children in public spaces!");
  }
  
  XtAddCallback(new->plotter.xaxis, XtNaxisNeedsRedraw, AxisNeedsRedraw, new);
  XtAddCallback(new->plotter.yaxis, XtNaxisNeedsRedraw, AxisNeedsRedraw, new);
  XtAddCallback(new->plotter.y2axis, XtNaxisNeedsRedraw, AxisNeedsRedraw, new);
  XtAddCallback(new->plotter.xaxis, XtNerrorCallback, AxisErrorCB, new);
  XtAddCallback(new->plotter.yaxis, XtNerrorCallback, AxisErrorCB, new);
  XtAddCallback(new->plotter.y2axis, XtNerrorCallback, AxisErrorCB, new);

  new->plotter.clickx = new->plotter.clicky = 0;
  new->plotter.dragwidth = new->plotter.dragheight = 0;
  new->plotter.lastclick = CurrentTime;  /* a magic time value */
  new->plotter.dragging = False;
  new->plotter.selectionMode = SelectNONE;
  new->plotter.legendItem = -1;
  new->plotter.boundingBox.xmin = new->plotter.xaxis->axis.axis_min;
  new->plotter.boundingBox.xmax = new->plotter.xaxis->axis.axis_max;
  new->plotter.boundingBox.ymin = new->plotter.yaxis->axis.axis_min;
  new->plotter.boundingBox.ymax = new->plotter.yaxis->axis.axis_max;
  new->plotter.boundingBox.y2min = new->plotter.y2axis->axis.axis_min;
  new->plotter.boundingBox.y2max = new->plotter.y2axis->axis.axis_max;
  new->plotter.newBoundingBox = False;
  new->plotter.ordered_children = NULL;
}

/* Called by the redisplay procedure and some other places */

static void Redraw(AtPlotterWidget pw, Region region)
{
  int i;

  if ((region == NULL) && XtIsRealized(pw))
    XClearWindow(XtDisplay(pw), XtWindow(pw));

  if (XtIsRealized(pw)) {
       /* Draw the Title, if it exists */
       if (pw->plotter.titleText != NULL) {
	    AtTextDraw(XtDisplay(pw), XtWindow(pw), pw->plotter.titleGC,
		       pw->plotter.titleText,
		       pw->plotter.layout.titleX, pw->plotter.layout.titleY);
       }
  }
    
  RedrawLegend(pw);

  /* do autoscaling, if necessary */
  if ((pw->plotter.autoScale) && (pw->plotter.newBoundingBox)) {
    Autoscale(pw);
    pw->plotter.newBoundingBox = False;
  }
    
  /* Draw the Axes, rescaling if necessary */
  if (NTHCHILDISDISPLAYED(pw, CHILD_XAXIS))
    AtAxisDraw(XtDisplay(pw), XtWindow(pw), pw->plotter.xaxis, 
	       pw->core.background_pixel);
  if (NTHCHILDISDISPLAYED(pw, CHILD_YAXIS))
    AtAxisDraw(XtDisplay(pw), XtWindow(pw), pw->plotter.yaxis,
	       pw->core.background_pixel);
  if (NTHCHILDISDISPLAYED(pw, CHILD_Y2AXIS))
    AtAxisDraw(XtDisplay(pw), XtWindow(pw), pw->plotter.y2axis,
	       pw->core.background_pixel);
  if (pw->plotter.framedAxes) {
    AtAxisDraw(XtDisplay(pw), XtWindow(pw), pw->plotter.xframeAxis,
	       pw->core.background_pixel);
    if (!NTHCHILDISDISPLAYED(pw, CHILD_Y2AXIS))
      AtAxisDraw(XtDisplay(pw), XtWindow(pw), pw->plotter.yframeAxis,
		 pw->core.background_pixel);
    if (!NTHCHILDISDISPLAYED(pw, CHILD_YAXIS))
      AtAxisDraw(XtDisplay(pw), XtWindow(pw), pw->plotter.y2frameAxis,
		 pw->core.background_pixel);
  }
    
  /* Draw the axis grids */
  if (NTHCHILDISDISPLAYED(pw, CHILD_XAXIS) && 
      pw->plotter.xaxis->axis.drawGrid)  {
    if (region == NULL)
      AtAxisDrawGrid(XtDisplay(pw), XtWindow(pw), pw->plotter.xaxis,
		     pw->plotter.layout.y1, pw->plotter.layout.y2);
    else
      AtAxisDrawGridRegion(XtDisplay(pw), XtWindow(pw), pw->plotter.xaxis,
			   pw->plotter.layout.y1, pw->plotter.layout.y2,
			   region);
  }
  if (NTHCHILDISDISPLAYED(pw, CHILD_YAXIS) && 
      pw->plotter.yaxis->axis.drawGrid)  {
    if (region == NULL)
      AtAxisDrawGrid(XtDisplay(pw), XtWindow(pw), pw->plotter.yaxis,
		     pw->plotter.layout.x1, pw->plotter.layout.x2);
    else
      AtAxisDrawGridRegion(XtDisplay(pw), XtWindow(pw), pw->plotter.yaxis,
			   pw->plotter.layout.x1, pw->plotter.layout.x2,
			   region);
  }
  if (NTHCHILDISDISPLAYED(pw, CHILD_Y2AXIS) && pw->plotter.y2axis->axis.drawGrid)  {
    if (region == NULL)
      AtAxisDrawGrid(XtDisplay(pw), XtWindow(pw), pw->plotter.y2axis,
		     pw->plotter.layout.x2, pw->plotter.layout.x1);
    else
      AtAxisDrawGridRegion(XtDisplay(pw), XtWindow(pw), pw->plotter.y2axis,
			   pw->plotter.layout.x2, pw->plotter.layout.x1,
			   region);
  }
	
  /* Draw the plots */

  if (pw->plotter.rankChildren) { /* draw children according to rank */
    Rank *tmp = pw->plotter.ordered_children;
    while (tmp) {
      if (ISDISPLAYED(tmp->child)) 
	REDRAWPLOT(pw, tmp->child, region);
      tmp = tmp->next;
    }
  }
  else /* draw children according to birth */
    for (i = FIRST_PUBLIC_CHILD; i < NUMCHILDREN(pw); i++)
      if (NTHCHILDISDISPLAYED(pw, i)) 
	REDRAWCHILD(pw, i, region);
    
    return;
}    

/************************************************************************
 *
 *  Redisplay
 *     General redisplay function called on exposure events.
 *
 ************************************************************************/

static void Redisplay(AtPlotterWidget pw,
		      XEvent *event,
		      Region region)
{
  Redraw(pw, region);
}

/************************************************************************
 *
 *  Destroy
 *	Clean up allocated resources when the widget is destroyed.
 *
 ************************************************************************/

static void Destroy (pw)
AtPlotterWidget pw;
{
  /* Free up the private data */
  FreeTitle(pw);
  FreeLegendTitle(pw);
  AtFontFamilyRelease(pw->plotter.ff);
  XtReleaseGC(pw, pw->plotter.titleGC);
  XtReleaseGC(pw, pw->plotter.legendGC);

  /* free our private copies of string resource */
  XtFree(pw->plotter.fontFamily);
  XtFree(pw->plotter.title);
  XtFree(pw->plotter.legendTitle);

  /* free the linked list of ordered_children */
  if (pw->plotter.ordered_children) { 
    Rank *tmp = pw->plotter.ordered_children;
    while (tmp->next) {
      tmp = tmp->next;
      XtFree(tmp->prev);
    }
    XtFree(tmp);
  }

  return;
}

/************************************************************************
 *
 *  Resize
 *
 ************************************************************************/

static void Resize(AtPlotterWidget pw)
{
  int i;

  if (XtIsRealized(pw)) {
    AtPlotterLayout(pw);
    ResizeLegend(pw);
	
    /* Resize the plots */
    for (i = FIRST_PUBLIC_CHILD; i < NUMCHILDREN(pw); i++)
      if (NTHCHILDISDISPLAYED(pw, i))
	RESIZECHILD(pw,i);
  }
}

/************************************************************************
 *
 *  Realize
 *
 ***********************************************************************/

static void Realize(AtPlotterWidget w, XtValueMask *vm,
		    XSetWindowAttributes *wa)
{
  (superclass->core_class.realize)(w, vm, wa);
  Resize(w);
}

/************************************************************************
 *
 *  SetValues
 *
 ************************************************************************/

#define Changed(field) (new->plotter.field != current->plotter.field)
static Boolean SetValues (current, request, new)
     AtPlotterWidget current, request, new;
{
  Boolean redraw = False;
  Boolean layout = False;
  Boolean recalcLegend = False;
  
  if (Changed(rankChildren))
    redraw = True;
  
  if (Changed(autoScale) && new->plotter.autoScale) {
    Autoscale(new);
    redraw = True;
  }
  if (Changed(forceSquare) && new->plotter.forceSquare) {
    layout = True;
    redraw = True;
  }
  if (Changed(floatingX) || Changed(floatingY) || Changed(framedAxes)) {
    layout = True;
    redraw = True;
  }

  if (Changed(fontFamily)) {
    XtFree(current->plotter.fontFamily);
    new->plotter.fontFamily = AtNewString(new->plotter.fontFamily);
    AtFontFamilyRelease(new->plotter.ff);
    new->plotter.ff = AtFontFamilyGet(XtDisplay(new),
				      new->plotter.fontFamily);
    if (!Changed(title))
      AtTextReformat(new->plotter.titleText,
		     new->plotter.ff, new->plotter.titleSize);
    if (!Changed(legendTitle))
      AtTextReformat(new->plotter.legendTitleText,
		     new->plotter.ff, new->plotter.legendTitleSize);
  }
    
  if (Changed(title)) {
    XtFree(current->plotter.title);
    new->plotter.title = AtNewString(new->plotter.title);
    FreeTitle(new);
    GetTitle(new);
    layout = redraw = True;
  }

  if (Changed(legendTitle)) {
    XtFree(current->plotter.legendTitle);
    new->plotter.title = AtNewString(new->plotter.legendTitle);
    FreeLegendTitle(new);
    GetLegendTitle(new);
    redraw = recalcLegend = True;
  }
    
  if ((layout) && XtIsRealized(new)) AtPlotterLayout(new);
  return redraw;
}
#undef Changed

static void InsertChild(Widget w)
{
  AtPlotterWidget p = (AtPlotterWidget) XtParent(w);

  /* warn if child of wrong class */
  if (!XtIsSubclass(w, atPlotWidgetClass) &&
      !XtIsSubclass(w, atAxisObjectClass))
    AtWarning(w, "Attempt to add child that is not a subclass of AtPlot");
    
  /* call the superclass's insert_child proc to actually add the child */
  (*superclass->composite_class.insert_child)(w);
  if (XtIsSubclass(w, atPlotWidgetClass)) {
    RecalcLegend(p);
    RankOrderChildren(w);
    /*
     * maintain this list even if no rank order is requested, so if
     * rank order is wanted later the list is ready.
     */
    
    if (p->plotter.autoScale) Autoscale(p);
    if (XtIsRealized(p)) Redraw(p, (Region) NULL);
  }
}

static void DeleteChild(Widget w)
{
  AtPlotterWidget p = (AtPlotterWidget)XtParent(w);
  int i;

  /* if a plot is selected, figure out which child is being deleted */
  /* and adjust the selected plot as necessary */
    
  if (XtIsSubclass(w, atPlotWidgetClass) && p->plotter.legendItem != -1) {
    for(i=FIRST_PUBLIC_CHILD; i<NUMCHILDREN(p); i++) 
      if (CHILD(p, i) == w) break;
    if (i == p->plotter.legendItem)
      p->plotter.legendItem = -1;
    else if (i < p->plotter.legendItem)
      p->plotter.legendItem -= 1;
  }

  /* call the superclass's delete_child proc to actually delete the child */
  (*superclass->composite_class.delete_child)(w);
  if (XtIsSubclass(w, atPlotWidgetClass)) {
    RecalcLegend(p);
    RankOrderRemove(w);
    if (p->plotter.autoScale) Autoscale(p);
    if (XtIsRealized(p)) Redraw(p, (Region) NULL);
  }
}



void AtPlotterLayout(AtPlotterWidget w)
{
  int approxheight, approxwidth;
  int xaxiswidth, yaxiswidth, y2axiswidth;
  AtAxisObject xaxis = w->plotter.xaxis;
  AtAxisObject yaxis = w->plotter.yaxis;
  AtAxisObject y2axis = w->plotter.y2axis;
  Boolean floatx, floaty;
  int originx, originy;
  double xmin, xmax, ymin, ymax, y2min, y2max;
  int i;
    
  w->plotter.layout.x1 = w->plotter.marginWidth;
  w->plotter.layout.y1 = w->plotter.marginHeight;
  w->plotter.layout.x2 = w->core.width - 1 - w->plotter.marginWidth;
  w->plotter.layout.y2 = w->core.height - 1 - w->plotter.marginHeight;

  if (w->plotter.showLegend) {
    w->plotter.layout.x2 -= w->plotter.legendWidth +
      w->plotter.marginWidth;
    w->plotter.layout.legendX = w->plotter.layout.x2 +
      w->plotter.marginWidth;
  }
    
  if (w->plotter.titleText != NULL) {
    w->plotter.layout.titleY = w->plotter.layout.y1 +
      AtTextAscent(w->plotter.titleText);
    w->plotter.layout.y1 += AtTextHeight(w->plotter.titleText) +
      w->plotter.marginHeight;
  }

  /*
   * guess at the size of the axes, and use the guess when
   * calculating the ticSpacing for the axes.
   * XXX this is a big overestimate.  Should be more accurate
   */
  approxwidth = w->plotter.layout.x2 - w->plotter.layout.x1;
  approxheight = w->plotter.layout.y2 - w->plotter.layout.y1;

  if ((w->plotter.floatingX) && (!w->plotter.framedAxes) &&
      (yaxis->axis.axis_min < 0.0) && (yaxis->axis.axis_max > 0.0)) 
    floatx = True;
  else
    floatx = False;

  if ((w->plotter.floatingY) && (!w->plotter.framedAxes) &&
      (xaxis->axis.axis_min < 0.0) && (xaxis->axis.axis_max))
    floaty = True;
  else
    floaty = False;

  if (NTHCHILDISDISPLAYED(w, CHILD_XAXIS))
    AtAxisComputeTicSpacing(w->plotter.xaxis, approxwidth);
  if (NTHCHILDISDISPLAYED(w, CHILD_YAXIS))
    AtAxisComputeTicSpacing(w->plotter.yaxis, approxheight);
  if (NTHCHILDISDISPLAYED(w, CHILD_Y2AXIS))
    AtAxisComputeTicSpacing(w->plotter.y2axis, approxheight);

  AtAxisGetBounds(w->plotter.xaxis, &xmin, &xmax);
  AtAxisGetBounds(w->plotter.yaxis, &ymin, &ymax);
  AtAxisGetBounds(w->plotter.y2axis, &y2min, &y2max);

  if (w->plotter.framedAxes) {
    AtAxisChangeBounds(w->plotter.xframeAxis, xmin, xmax);
    AtAxisChangeBounds(w->plotter.yframeAxis, ymin, ymax);
    AtAxisChangeBounds(w->plotter.y2frameAxis, y2min, y2max);
    /* mildly illegal... */
    AtAxisChangeNumbering(w->plotter.xframeAxis,
			  w->plotter.xaxis->axis.ticInterval,
			  w->plotter.xaxis->axis.subtics);
    AtAxisChangeNumbering(w->plotter.yframeAxis,
			  w->plotter.yaxis->axis.ticInterval,
			  w->plotter.yaxis->axis.subtics);
    AtAxisChangeNumbering(w->plotter.y2frameAxis,
			  w->plotter.y2axis->axis.ticInterval,
			  w->plotter.y2axis->axis.subtics);
  }

  xaxiswidth = yaxiswidth = y2axiswidth = 0;
  
  if (NTHCHILDISDISPLAYED(w, CHILD_XAXIS))
    xaxiswidth = AtAxisWidth(w->plotter.xaxis);
  if (NTHCHILDISDISPLAYED(w, CHILD_YAXIS))
    yaxiswidth = AtAxisWidth(w->plotter.yaxis);
  if (NTHCHILDISDISPLAYED(w, CHILD_Y2AXIS))
    y2axiswidth = AtAxisWidth(w->plotter.y2axis);

  if (!floaty) w->plotter.layout.x1 += yaxiswidth;
  if (!floatx) w->plotter.layout.y2 -= xaxiswidth;
  w->plotter.layout.x2 -= y2axiswidth;
  if (w->plotter.framedAxes) {
    int ydisp = NTHCHILDISDISPLAYED(w, CHILD_YAXIS);
    int y2disp = NTHCHILDISDISPLAYED(w, CHILD_Y2AXIS);
    
    if (ydisp && !y2disp)
      w->plotter.layout.x2 -= AtAxisWidth(w->plotter.yframeAxis);
    if (y2disp && !ydisp)
      w->plotter.layout.x1 += AtAxisWidth(w->plotter.y2frameAxis);
    w->plotter.layout.y1 += AtAxisWidth(w->plotter.xframeAxis);
  }

  w->plotter.layout.width = w->plotter.layout.x2 - w->plotter.layout.x1;
  w->plotter.layout.height = w->plotter.layout.y2 - w->plotter.layout.y1;
    
  if (w->plotter.titleText != NULL)
    w->plotter.layout.titleX = w->plotter.layout.x1 +
      (w->plotter.layout.width - AtTextWidth(w->plotter.titleText))/2;

  if (w->plotter.showLegend) ResizeLegend(w);

  if (NTHCHILDISDISPLAYED(w, CHILD_XAXIS))
    AtAxisSetLocation(w->plotter.xaxis, w->plotter.layout.x1,
		      w->plotter.layout.y2, w->plotter.layout.x2,
		      w->plotter.layout.y2);
  if (NTHCHILDISDISPLAYED(w, CHILD_YAXIS))
    AtAxisSetLocation(w->plotter.yaxis, w->plotter.layout.x1,
		      w->plotter.layout.y1, w->plotter.layout.x1,
		      w->plotter.layout.y2);
  if (NTHCHILDISDISPLAYED(w, CHILD_Y2AXIS))
    AtAxisSetLocation(w->plotter.y2axis,
		      w->plotter.layout.x2, w->plotter.layout.y1,
		      w->plotter.layout.x2, w->plotter.layout.y2);
  if (w->plotter.framedAxes) {
    AtAxisSetLocation(w->plotter.xframeAxis,
		      w->plotter.layout.x1, w->plotter.layout.y1,
		      w->plotter.layout.x2, w->plotter.layout.y1);
    AtAxisSetLocation(w->plotter.yframeAxis,
		      w->plotter.layout.x2, w->plotter.layout.y1,
		      w->plotter.layout.x2, w->plotter.layout.y2);
    AtAxisSetLocation(w->plotter.y2frameAxis,
		      w->plotter.layout.x1, w->plotter.layout.y1,
		      w->plotter.layout.x1, w->plotter.layout.y2);
  }

  /* The axes are the right length now, but the location may be wrong. */
  if (floatx) {
    originy = AtScaleUserToPixel(yaxis->axis.scale, 0.0);
    AtAxisSetLocation(xaxis,
		      w->plotter.layout.x1, originy,
		      w->plotter.layout.x2, originy);
  }
  if (floaty) {
    originx = AtScaleUserToPixel(xaxis->axis.scale, 0.0);
    AtAxisSetLocation(yaxis, originx, w->plotter.layout.y1,
		      originx, w->plotter.layout.y2);
  }

  /*
   * deal with the forceSquare resource.
   * This code may modify the size of one of the axes.
   * If so, it recomputes the tic spacing, but doing that might
   * change the width of the axis enough that they should be
   * re-layed out, but if that happens, then we may need to adjust
   * the axes again to make them square.
   * The fix for now is just to hope it's not a problem.
   */
  if (w->plotter.forceSquare) {
    double xspan = xmax - xmin;
    double yspan = ymax - ymin;

    if (xspan >= yspan)  {
      double xr = xspan / w->plotter.layout.width;
	
      ymax = ymin + xr * w->plotter.layout.height;
      AtAxisChangeBounds(w->plotter.yaxis, ymin, ymax);
      AtAxisComputeTicSpacing(w->plotter.yaxis,w->plotter.layout.height);
    }
    else {
      double yr = yspan / w->plotter.layout.height;
	
      xmax = xmin + yr * w->plotter.layout.width;
      AtAxisChangeBounds(w->plotter.xaxis, xmin, xmax);
      AtAxisComputeTicSpacing(w->plotter.xaxis,w->plotter.layout.width);
    }
  }
  else if (w->plotter.forceAspect) {
    /*** keep aspect ratio the same, do NOT extend an
     *** axis outside the visible range
     ***/
    double aspect_ratio = (xmax-xmin)/(ymax-ymin);
    double plot_ar = (double)w->plotter.layout.width/w->plotter.layout.height;
    int extrah,extraw;
    if(aspect_ratio>plot_ar){
      /*** width is the determining factor; shorten y axis 
       *** recalc y axis and yframe axis; reposition the axes
       *** to vertically center the plot.
       ***/
      double xr = (xmax - xmin)/w->plotter.layout.width;
      int new_plot_height = (ymax-ymin)/xr;
      /*** get new bounds ***/
      extrah = w->plotter.layout.height - new_plot_height;
      w->plotter.layout.y2 = w->plotter.layout.y1+new_plot_height;
      w->plotter.layout.height = w->plotter.layout.y2 - w->plotter.layout.y1;
      /*** center the plot vertically ***/
      w->plotter.layout.y1+=extrah/2;
      w->plotter.layout.y2+=extrah/2;
      AtAxisSetLocation(w->plotter.yaxis,
			w->plotter.layout.x1,w->plotter.layout.y1,
			w->plotter.layout.x1,w->plotter.layout.y2);
      AtAxisComputeTicSpacing(w->plotter.yaxis,new_plot_height);
      AtAxisSetLocation(w->plotter.xaxis,
			w->plotter.layout.x1,w->plotter.layout.y2,
			w->plotter.layout.x2,w->plotter.layout.y2);
      if (w->plotter.framedAxes) {
	AtAxisSetLocation(w->plotter.yframeAxis, 
			  w->plotter.layout.x2,w->plotter.layout.y1,
			  w->plotter.layout.x2,w->plotter.layout.y2);
	AtAxisChangeNumbering(w->plotter.yframeAxis,
			      w->plotter.yaxis->axis.ticInterval,
			      w->plotter.yaxis->axis.subtics);
	AtAxisSetLocation(w->plotter.y2frameAxis, 
			  w->plotter.layout.x1,w->plotter.layout.y1,
			  w->plotter.layout.x1,w->plotter.layout.y2);
	AtAxisChangeNumbering(w->plotter.y2frameAxis,
			      w->plotter.y2axis->axis.ticInterval,
			      w->plotter.y2axis->axis.subtics);
	AtAxisSetLocation(w->plotter.xframeAxis,
			  w->plotter.layout.x1,w->plotter.layout.y1,
			  w->plotter.layout.x2,w->plotter.layout.y1);
      }
    }
    else {
      /*** height is the determining factor; shorten x axis 
       *** recalc xaxis and xframe axis; reposition axes to 
       *** horizontally center the plot.
       ***/
      double yr = (ymax - ymin)/w->plotter.layout.height;
      int new_plot_width = (xmax-xmin)/yr;
      /*** get new bounds ***/
      extraw = w->plotter.layout.width - new_plot_width;
      w->plotter.layout.x2 = w->plotter.layout.x1+new_plot_width;
      w->plotter.layout.width = w->plotter.layout.x2 - w->plotter.layout.x1;
      /*** center the plot horizontally ***/
      w->plotter.layout.x1+=extraw/2;
      w->plotter.layout.x2+=extraw/2;
      AtAxisSetLocation(w->plotter.xaxis,
			w->plotter.layout.x1,w->plotter.layout.y2,
			w->plotter.layout.x2,w->plotter.layout.y2);
      AtAxisComputeTicSpacing(w->plotter.xaxis,new_plot_width);
      AtAxisSetLocation(w->plotter.yaxis,
			w->plotter.layout.x1,w->plotter.layout.y1,
			w->plotter.layout.x1,w->plotter.layout.y2);
      if (w->plotter.framedAxes) {
	AtAxisSetLocation(w->plotter.yframeAxis, 
			  w->plotter.layout.x2,w->plotter.layout.y1,
			  w->plotter.layout.x2,w->plotter.layout.y2);
	AtAxisSetLocation(w->plotter.xframeAxis, 
			  w->plotter.layout.x1,w->plotter.layout.y1,
			  w->plotter.layout.x2,w->plotter.layout.y1);
	AtAxisSetLocation(w->plotter.y2frameAxis, 
			  w->plotter.layout.x1,w->plotter.layout.y1,
			  w->plotter.layout.x1,w->plotter.layout.y2);
	AtAxisChangeNumbering(w->plotter.xframeAxis,
			      w->plotter.xaxis->axis.ticInterval,
			      w->plotter.xaxis->axis.subtics);
      }
    }
  }

  if (w->plotter.framedAxes) {
    AtAxisChangeBounds(w->plotter.yframeAxis, ymin, ymax);
    AtAxisChangeNumbering(w->plotter.yframeAxis,
			  w->plotter.yaxis->axis.ticInterval,
			  w->plotter.yaxis->axis.subtics);
    AtAxisChangeBounds(w->plotter.y2frameAxis, y2min, y2max);
    AtAxisChangeNumbering(w->plotter.y2frameAxis,
			  w->plotter.y2axis->axis.ticInterval,
			  w->plotter.y2axis->axis.subtics);
  }
    
  for(i=FIRST_PUBLIC_CHILD; i<NUMCHILDREN(w); i++)
    if (NTHCHILDISDISPLAYED(w, i))
      RESIZECHILD(w, i);
}


static void GetLegendText(AtPlotterConstraints c, AtPlotterWidget p)
{
  if (c->plotter.legendName != NULL) {
    c->plotter.legendName=strcpy(XtMalloc(strlen(c->plotter.legendName)+1),
				 c->plotter.legendName);
    c->plotter.legendText = AtTextCreate(c->plotter.legendName,
					 p->plotter.ff,
					 p->plotter.legendSize);
  }
  else
    c->plotter.legendText = NULL;
}

static void FreeLegendText(AtPlotterConstraints cur, AtPlotterConstraints new)
{
  if (cur->plotter.legendName != NULL)
    XtFree(cur->plotter.legendName);
  if (new->plotter.legendText != NULL) {
    AtTextDestroy(new->plotter.legendText);
    new->plotter.legendText = NULL;
  }
}

static void ConstraintInitialize(Widget request, Widget new)
{
  AtPlotterConstraints c = (AtPlotterConstraints)new->core.constraints;
  AtPlotterWidget p = (AtPlotterWidget) XtParent(new);

  GetLegendText(c,p);
}

static void ConstraintDestroy(Widget w)
{
  AtPlotterConstraints c = (AtPlotterConstraints)w->core.constraints;
  FreeLegendText(c,c);
}

static Boolean ConstraintSetValues(Widget current, Widget request, Widget new)
{
#define Changed(field) (newc->plotter.field != curc->plotter.field)    
  AtPlotterConstraints newc = (AtPlotterConstraints)new->core.constraints;
  AtPlotterConstraints curc =(AtPlotterConstraints)current->core.constraints;
  AtPlotterWidget parent = (AtPlotterWidget) XtParent(new);
  int i, j;
  Boolean redrawn = False;

  if (Changed(boundingBox.xmin) || Changed(boundingBox.xmax) ||
      Changed(boundingBox.ymin) || Changed(boundingBox.ymax) ||
      Changed(useY2Axis)) {
    parent->plotter.newBoundingBox = True;
  }
  
  if (Changed(displayed) && XtIsSubclass(current, atAxisObjectClass)) { 
    newc->plotter.needsRedraw = True;
    parent->plotter.newBoundingBox = True;
  }

  if (!XtWindow(parent)) { return False;  }
  
  if (Changed(legendName)) {
    FreeLegendText(curc,newc);
    GetLegendText(newc, (AtPlotterWidget)XtParent(new));
    RecalcLegend(parent);
    RedrawLegend(parent);
  }

  if (parent->plotter.rankChildren && Changed(rankOrder))  {
    ReRankOrderChildren(new);
    newc->plotter.needsRedraw = True;
  }

  if (Changed(displayed) || (newc->plotter.needsRedraw == True)) {
    Redraw(parent, (Region) NULL);
    redrawn = True;
    newc->plotter.needsRedraw = False;
  }
  else if (newc->plotter.needsRefresh == True)  {
    if (!redrawn)  {
      if (XtIsSubclass(new, atBarchartWidgetClass)) {
	XRectangle *rlist;
	int num_rlist;
	parent->plotter.clip_region = XCreateRegion();
	BARGETRECTLIST(parent, new, parent->plotter.clip_region,
		       &rlist, &num_rlist);
	for (i = 0; i < num_rlist; i++)  {
	  /**** This should probably be a barchart method too. ****/
	  XClearArea(XtDisplay(parent), XtWindow(parent), rlist[i].x, 
		     rlist[i].y, rlist[i].width, rlist[i].height, False);
	  if (parent->plotter.rankChildren) { /* draw children according to rank */
	    Rank *tmp = parent->plotter.ordered_children;
	    while (tmp) {
	      if (tmp->child == (Widget) new) {
		tmp = tmp->next;
		continue; /* dont draw the child that shrunk */
	      }
	      else if (ISDISPLAYED(tmp->child))
		BARREDRAW(parent, tmp->child, &rlist[i]);
	      tmp = tmp->next;
	    }
	  }
	  else   /* draw children according to birthorder */
	    for (j = FIRST_PUBLIC_CHILD; j < NUMCHILDREN(parent); j++)
	      if (NTHCHILDISDISPLAYED(parent, j))
		BARREDRAW(parent, parent->composite.children[j], &rlist[i]);
	}
	XtFree(rlist);

	if (parent->plotter.xaxis->axis.drawGrid)
	  AtAxisDrawGridRegion(XtDisplay(parent), XtWindow(parent), 
			       parent->plotter.xaxis, parent->plotter.layout.y1, 
			       parent->plotter.layout.y2, 
			       parent->plotter.clip_region);
	if (parent->plotter.yaxis->axis.drawGrid)
	  AtAxisDrawGridRegion(XtDisplay(parent), XtWindow(parent),
			       parent->plotter.yaxis, parent->plotter.layout.x1,
			       parent->plotter.layout.x2,
			       parent->plotter.clip_region);
      
	if (parent->plotter.rankChildren) { /* draw children according to rank */
	  Rank *tmp = parent->plotter.ordered_children;
	  while (tmp) {
	    BARDRAWINC(parent, tmp->child, parent->plotter.clip_region);
	    tmp = tmp->next;
	  }
	}
	else   /* draw children according to birthorder */
	  for (i = FIRST_PUBLIC_CHILD; i < NUMCHILDREN(parent); i++)
	    if (NTHCHILDISDISPLAYED(parent, i))
	      BARDRAWINC(parent, parent->composite.children[i], 
			 parent->plotter.clip_region);
	
	/* we don't need the region anymore. So destroy it. */
	XDestroyRegion(parent->plotter.clip_region);
      }
      else
	REDRAWPLOT(parent, new, NULL);
    }
    newc->plotter.needsRefresh = False;
  }
  return False;
#undef Changed    
}

static void ChangePlotterBounds(AtPlotterWidget pw,
				double xmin, double xmax,
				double ymin, double ymax,
				double y2min, double y2max)
{
  int i;
    
  if (NTHCHILDISDISPLAYED(pw, CHILD_XAXIS))
    AtAxisChangeBounds(pw->plotter.xaxis, xmin, xmax);
  if (NTHCHILDISDISPLAYED(pw, CHILD_YAXIS))
    AtAxisChangeBounds(pw->plotter.yaxis, ymin, ymax);
  if (NTHCHILDISDISPLAYED(pw, CHILD_Y2AXIS))
    AtAxisChangeBounds(pw->plotter.y2axis, y2min, y2max);

  if(XtIsRealized(pw))
    AtPlotterLayout(pw);
    
  for(i=FIRST_PUBLIC_CHILD; i<NUMCHILDREN(pw); i++)
    if (NTHCHILDISDISPLAYED(pw, i))
      RESCALECHILD(pw, i);
}


static void Autoscale(AtPlotterWidget pw)
{
#define child(field) CONSTRAINT(pw,i)->plotter.boundingBox.field
#define parent(field) pw->plotter.boundingBox.field
  int i;
  Boolean firstx = True, firsty = True, firsty2 = True;
  double tmp;

  for(i = FIRST_PUBLIC_CHILD; i < NUMCHILDREN(pw); i++) {
    if (!NTHCHILDISDISPLAYED(pw, i)) continue; /* Don't scale on undisplayed */
    /* if bounding box doesn't make sense, ignore it! */
    /* by default, text plots have non-sensical boxes */
    if (child(xmin) > child(xmax)) continue;
    if (firstx) {                     /* autoscale x axis */
      parent(xmin) = child(xmin);
      parent(xmax) = child(xmax);
      firstx = False;
    }
    else {
      if (child(xmin) < parent(xmin)) parent(xmin) = child(xmin);
      if (child(xmax) > parent(xmax)) parent(xmax) = child(xmax);
    }

    if (!CONSTRAINT(pw,i)->plotter.useY2Axis) {  /* autoscale y axis */
      if (firsty) {
	parent(ymin) = child(ymin);
	parent(ymax) = child(ymax);
	firsty = False;
      }
      else {
	if (child(ymin) < parent(ymin)) parent(ymin) = child(ymin);
	if (child(ymax) > parent(ymax)) parent(ymax) = child(ymax);
      }
    }
    else {   /* autoscale y2 axis */
      if (firsty2) {
	parent(y2min) = child(ymin);
	parent(y2max) = child(ymax);
	firsty2 = False;
      }
      else {
	if (child(ymin)<parent(y2min)) parent(y2min) = child(ymin);
	if (child(ymax)>parent(y2max)) parent(y2max) = child(ymax);
      }
    }
  }

  /* if any autoscaling actually occured, increase the bounds by 5% */
  if (!firstx) {
    tmp = (parent(xmax) - parent(xmin))*.05;
    parent(xmin) -= tmp;
    parent(xmax) += tmp;
  }
  if (!firsty) {
    tmp = (parent(ymax) - parent(ymin)) *.05;
    parent(ymin) -= tmp;
    parent(ymax) += tmp;
  }
  if (!firsty2) {
    tmp = (parent(y2max) - parent(y2min)) *.05;
    parent(y2min) -= tmp;
    parent(y2max) += tmp;
  }

  if (parent(xmin) == parent(xmax))
    if (parent(xmin) < 0)
      parent(xmax) = 0.0;
    else
      parent(xmin) = 0.0;

  if (parent(ymin) == parent(ymax))
    if (parent(ymin) < 0)
      parent(ymax) = 0.0;
    else
      parent(ymin) = 0.0;
    
  if (parent(y2min) == parent(y2max))
    if (parent(y2min) < 0)
      parent(y2max) = 0.0;
    else
      parent(y2min) = 0.0;

  ChangePlotterBounds(pw, parent(xmin), parent(xmax),
		      parent(ymin), parent(ymax),
		      parent(y2min), parent(y2max));
#undef child
#undef parent			
}



/***********************************************************
 ********************     RedrawPlotter   ******************
 ***********************************************************
 This function is provided as convenience to redraw the 
 widget from application programs.  It calls the redraw
 procedure that would normally be called on an Expose
 event.
 ***********************************************************/
void RedrawPlotter(AtPlotterWidget pw)
{
  if (XtIsRealized((Widget)pw))
    Redraw(pw, (Region) NULL);
  else PlotterError(pw, "RedrawPlotter: Widget not realized.");
}

/*************************************************************
 **************            PlotterError          *************
 *************************************************************/
static void PlotterError(AtPlotterWidget pw, char *string)
{
  if (pw->plotter.errorCallback)
    XtCallCallbacks(pw, XtNerrorCallback, (caddr_t)string);
  else
    XtWarning(string);
}
    
static void AxisErrorCB(AtAxisObject a, AtPlotterWidget pw, String errtext)
{
  PlotterError(pw, errtext);
}

static void RecalcLegend(AtPlotterWidget w)
{
  int i;
  AtPlotterConstraints c;
  int h = 0;

  if (w->plotter.showLegend == False) return;

  h = AtTextHeight(w->plotter.legendTitleText);
  h += w->plotter.marginHeight;
    
  for(i = FIRST_PUBLIC_CHILD; i < NUMCHILDREN(w); i++) {
    if (!NTHCHILDISDISPLAYED(w, i)) continue; 
    c = CONSTRAINT(w,i);
    if (i == w->plotter.legendItem)
      w->plotter.legendItemY = h;
    if (c->plotter.legendText != NULL)
      h += AtTextHeight(c->plotter.legendText)+w->plotter.legendSpacing;
  }

  w->plotter.layout.legendHeight = h;
  w->plotter.layout.legendY = w->plotter.layout.y1 +
    (w->plotter.layout.height - h)/2;
  w->plotter.legendItemY += w->plotter.layout.legendY;
}

static void ResizeLegend(AtPlotterWidget w)
{
  if (w->plotter.showLegend) {
    w->plotter.legendItemY -= w->plotter.layout.legendY;
    w->plotter.layout.legendY = w->plotter.layout.y1 + 
      (w->plotter.layout.height - w->plotter.layout.legendHeight) / 2;
    w->plotter.legendItemY += w->plotter.layout.legendY;
  }
}

static void HighlightSelectedLegendItem(AtPlotterWidget pw)
{
  AtText *t;

  if (pw->plotter.legendItem != -1) {
    t = CONSTRAINT(pw,pw->plotter.legendItem)->plotter.legendText;
    XFillRectangle(XtDisplay(pw), XtWindow(pw), pw->plotter.dragGC,
		   pw->plotter.layout.legendX, pw->plotter.legendItemY,
		   pw->plotter.legendWidth,
		   AtTextHeight(t) + pw->plotter.legendSpacing/2);
  }
}

static void RedrawLegend(AtPlotterWidget w)
{
  int i;
  AtText *t;
  int y;

  if (w->plotter.showLegend == False) return;

  XClearArea(XtDisplay(w), XtWindow(w),
	     w->plotter.layout.legendX, w->plotter.layout.y1,
	     0, 0, False);
    
  y = w->plotter.layout.legendY;
  AtTextDrawJustified(XtDisplay(w), XtWindow(w), w->plotter.legendGC,
		      w->plotter.legendTitleText,
		      AtTextJUSTIFY_CENTER, AtTextJUSTIFY_CENTER,
		      w->plotter.layout.legendX, y,
		      w->plotter.legendWidth,
		      AtTextHeight(w->plotter.legendTitleText));

  y += AtTextHeight(w->plotter.legendTitleText) + w->plotter.marginWidth;
  for(i = FIRST_PUBLIC_CHILD; i < NUMCHILDREN(w); i++ ) {
    if (!NTHCHILDISDISPLAYED(w, i)) continue; 
    t = CONSTRAINT(w,i)->plotter.legendText;
    if (t != NULL) {
      AtTextDraw(XtDisplay(w), XtWindow(w), w->plotter.legendGC, t,
		 w->plotter.layout.legendX+2*w->plotter.marginWidth+16,
		 y + AtTextAscent(t));
      LEGENDICON(w,i,w->plotter.layout.legendX, y, 16, AtTextHeight(t));
      y += AtTextHeight(t) + w->plotter.legendSpacing;
    }
  }
  HighlightSelectedLegendItem(w);
}


/*
 *                         Actions
 *
 * start-selection:
 *   store x,y coords
 *   if in plotting region
 *     selectionMode = plot
 *   if in legend region
 *     selectionMode = legend
 *     unhighlight any previous highlight legend entry
 *     highlight the appropriate legend entry, remember it
 *   else selectionMode = none
 *     unselect any highlight legend entries
 *
 * drag:
 *   if selectionMode = plot && still in plotting region
 *     if dragging
 *       erase old rectangle
 *     draw new rectangle
 *     dragging = True
 *   else if selectionMode = legend
 *     if no longer over the same entry
 *       unhighlight it, forget it
 *       if over a new entry
 *         highlight it, remember it
 *
 * end-selection:
 *   if selectionMode = plot
 *      if dragging
 *        erase rectangle
 *      if still inside plotting region
 *        if dragging && dragged > 4 pixels
 *          call dragCallback
 *        else
 *          call clickCallback
 *      dragging = False
 *   else if selectionMode = legend
 *      if there is a selected entry
 *         call selectCallback
 *         make it the primary selection
 *         leave the entry highlighted
 *   selectionMode = none
 *  
 * cancel-selection:
 *   if selectionMode == plot && dragging
 *     erase rectangle
 *   else if selectionMode = legend
 *     unhighlight any highlighted item
 *   selectionMode = none
 * 
 * motion-notify
 *   invoke motionCallback
 *
 */

#define InPlottingRegion(pw, event) \
    ((event->x >= pw->plotter.layout.x1) &&\
     (event->x <= pw->plotter.layout.x2) &&\
     (event->y >= pw->plotter.layout.y1) &&\
     (event->y <= pw->plotter.layout.y2))

#define InLegendRegion(pw,event) \
    ((pw->plotter.showLegend) &&\
     (event->x >= pw->plotter.layout.legendX) &&\
     (event->x <= pw->plotter.layout.legendX + pw->plotter.legendWidth) &&\
     (event->y >= pw->plotter.layout.legendY) &&\
     (event->y <= pw->plotter.layout.legendY+pw->plotter.layout.legendHeight))

#define DrawDragRect(pw) \
  XDrawRectangle(XtDisplay(pw), XtWindow(pw), pw->plotter.dragGC,\
		 pw->plotter.clickx, pw->plotter.clicky,\
		 pw->plotter.dragwidth, pw->plotter.dragheight)

#define EraseDragRect(pw) DrawDragRect(pw)

#define xscale(pw,x) AtScalePixelToUser(pw->plotter.xaxis->axis.scale, x)
#define yscale(pw,x) AtScalePixelToUser(pw->plotter.yaxis->axis.scale, x)
#define y2scale(pw,x) AtScalePixelToUser(pw->plotter.y2axis->axis.scale, x)


static void UnselectAllInLegend(AtPlotterWidget pw)
{
  HighlightSelectedLegendItem(pw);
  pw->plotter.legendItem = -1;
}

static void SelectInLegend(AtPlotterWidget pw, XButtonPressedEvent *event)
{
  int i;
  short h; 
  AtText *t;

  if (!InLegendRegion(pw,event)) return;

  h = pw->plotter.layout.legendY
    + AtTextHeight(pw->plotter.legendTitleText)
      + pw->plotter.marginHeight
	- pw->plotter.legendSpacing/2;
    
  /* check for click in title -- deselect selected items */
  if (event->y < h) {
    HighlightSelectedLegendItem(pw);  /* erase old one */
    pw->plotter.legendItem = -1;
    return;
  }
    
  for(i=FIRST_PUBLIC_CHILD; i < NUMCHILDREN(pw); i++) {
    if (!NTHCHILDISDISPLAYED(pw, i)) continue; 
    t = CONSTRAINT(pw,i)->plotter.legendText;
    if (t != NULL) {
      if ((event->y >= h) &&
	  (event->y <= h+AtTextHeight(t) + pw->plotter.legendSpacing))
	break;
      h += AtTextHeight(t) + pw->plotter.legendSpacing;
    }
  }

  if (i < NUMCHILDREN(pw)) {
    if ((event->type == ButtonPress) &&
	(i == pw->plotter.legendItem) &&
	(pw->plotter.lastclick != CurrentTime) &&
	(event->time - pw->plotter.lastclick <
	 pw->plotter.doubleClickInterval)) {   /* if a double click */
      XtCallCallbacks(pw, XtNdoubleSelectCallback,(caddr_t)CHILD(pw, i));
      pw->plotter.lastclick = CurrentTime;
      pw->plotter.selectionMode = SelectNONE;
    }
    else if ((event->type == ButtonPress) ||
	     (i != pw->plotter.legendItem)) {
      HighlightSelectedLegendItem(pw);  /* erase old one */
      pw->plotter.legendItem = i;
      pw->plotter.legendItemY = h;
      HighlightSelectedLegendItem(pw);  /* draw new one */
      pw->plotter.lastclick = event->time;
    }
  }
}


static void StartSelection(AtPlotterWidget pw, XButtonPressedEvent *event)
{
    /*
     * start-selection:
     *   store x,y coords
     *   if in plotting region
     *     selectionMode = plot
     *   if in legend region
     *     selectionMode = legend
     *     unhighlight any previous highlight legend entry
     *     highlight the appropriate legend entry, remember it
     *   else selectionMode = none
     *     unselect any highlight legend entries
     */
    
  pw->plotter.clickx = event->x;
  pw->plotter.clicky = event->y;
    
  if (InPlottingRegion(pw,event)) {
    pw->plotter.selectionMode = SelectREGION;
  }
  else if (InLegendRegion(pw,event)) {
    pw->plotter.selectionMode = SelectLEGEND;
    SelectInLegend(pw,event);
  }
  else {
    pw->plotter.selectionMode = SelectNONE;
    UnselectAllInLegend(pw);
  }
}


static void Drag(AtPlotterWidget pw, XMotionEvent *event)
{
    /*
     * drag:
     *   if selectionMode = plot
     *     if still in plotting region
     *       if dragging
     *         erase old rectangle
     *       draw new rectangle
     *       dragging = True
     *     else
     *       cancel selection
     *   else if selectionMode = legend
     *     if no longer over the same entry
     *       unhighlight it, forget it
     *       if over a new entry
     *         highlight it, remember it
     */

  if (pw->plotter.selectionMode == SelectREGION) {
    if (InPlottingRegion(pw,event)) {
      if (pw->plotter.dragging) {
	EraseDragRect(pw);
      }
      pw->plotter.dragwidth = event->x - pw->plotter.clickx;
      pw->plotter.dragheight = event->y - pw->plotter.clicky;
      DrawDragRect(pw);
      pw->plotter.dragging = True;
    }
    else {
      if (pw->plotter.dragging) {
	EraseDragRect(pw);
	pw->plotter.dragging = False;
      }
      pw->plotter.selectionMode = SelectNONE;
    }
  }
  else if (pw->plotter.selectionMode == SelectLEGEND)
    SelectInLegend(pw,(XButtonPressedEvent *)event);
}


static void EndSelection(AtPlotterWidget pw, XButtonReleasedEvent *event)
{
    /* end-selection:
     *   if selectionMode = plot
     *      if dragging
     *        erase rectangle
     *      if still inside plotting region
     *        if dragging && dragged > 4 pixels
     *          call dragCallback
     *        else
     *          call clickCallback
     *      dragging = False
     *   else if selectionMode = legend
     *      if there is a selected entry
     *         call selectCallback
     *         make it the primary selection
     *         leave the entry highlighted
     *   selectionMode = none
     */

  if (pw->plotter.selectionMode == SelectREGION) {
    if (pw->plotter.dragging) EraseDragRect(pw);
	    
    if (InPlottingRegion(pw,event)) {
      if (pw->plotter.dragging &&
	  (pw->plotter.dragwidth*pw->plotter.dragwidth +
	   pw->plotter.dragheight*pw->plotter.dragheight > 16)) {
	RectangleStruct r;
#ifdef SYSV
	memset (&r, 0, sizeof r);
#else
	bzero(&r, sizeof r); 
#endif /* SYSV */
	if (NTHCHILDISDISPLAYED(pw, CHILD_XAXIS)) {
	  r[0].x = xscale(pw, pw->plotter.clickx);
	  r[1].x = xscale(pw, pw->plotter.clickx+pw->plotter.dragwidth);
	}
	if (NTHCHILDISDISPLAYED(pw, CHILD_YAXIS)) {
 	  r[0].y = yscale(pw, pw->plotter.clicky);
	  r[1].y = yscale(pw, pw->plotter.clicky+pw->plotter.dragheight);
	}
	if (NTHCHILDISDISPLAYED(pw, CHILD_Y2AXIS)) {
	  r[0].y2= y2scale(pw, pw->plotter.clicky);
	  r[1].y2=y2scale(pw, pw->plotter.clicky+pw->plotter.dragheight);
	}
	r[0].pixelx = pw->plotter.clickx;
	r[0].pixely = pw->plotter.clicky;
	r[1].pixelx = event->x;
	r[1].pixely = event->y;
	XtCallCallbacks(pw, XtNdragCallback, (caddr_t)r);
      }
      else {
	PointStruct point;
#ifdef SYSV
	memset (&point, 0, sizeof point);
#else
	bzero(&point, sizeof point); 
#endif /* SYSV */
	point.pixelx = event->x;
	point.pixely = event->y;
	if (NTHCHILDISDISPLAYED(pw, CHILD_XAXIS)) point.x = xscale(pw, event->x);
	if (NTHCHILDISDISPLAYED(pw, CHILD_YAXIS)) point.y = yscale(pw, event->y);
	if (NTHCHILDISDISPLAYED(pw, CHILD_Y2AXIS)) point.y2 = y2scale(pw, event->y);
	XtCallCallbacks(pw, XtNclickCallback, (caddr_t)&point);
      }
    }
    pw->plotter.dragging = False;
  }
  else if (pw->plotter.selectionMode == SelectLEGEND) {
    SelectInLegend(pw,(XButtonPressedEvent *)event);
    if ((pw->plotter.legendItem != -1) &&  /* if an item is selected */
	(pw->plotter.lastclick != CurrentTime)) /* and not a dbl-click */
      XtCallCallbacks(pw, XtNselectCallback,
		      (caddr_t)CHILD(pw,pw->plotter.legendItem));
  }

  pw->plotter.selectionMode = SelectNONE;
}


static void CancelSelection(AtPlotterWidget pw, XEvent *event)
{
  /*  
   * cancel-selection:
   *   if selectionMode == plot && dragging
   *     erase rectangle
   *   else if selectionMode = legend
   *     unhighlight any highlighted item
   *   selectionMode = none
   */
  
  if ((pw->plotter.selectionMode == SelectREGION) && (pw->plotter.dragging)){
    EraseDragRect(pw);
    pw->plotter.dragging = False;
  }
  else if (pw->plotter.selectionMode == SelectLEGEND)
    UnselectAllInLegend(pw);
  
  pw->plotter.selectionMode = SelectNONE;
}

static void HandleMotion(AtPlotterWidget pw, XMotionEvent *event)
{
  PointStruct point;
    
  if (InPlottingRegion(pw,event)) {
#ifdef SYSV
	memset (&point, 0, sizeof point);
#else
	bzero(&point, sizeof point); 
#endif /* SYSV */
    point.pixelx = event->x;
    point.pixely = event->y;
    if (NTHCHILDISDISPLAYED(pw, CHILD_XAXIS)) point.x = xscale(pw,event->x);
    if (NTHCHILDISDISPLAYED(pw, CHILD_YAXIS)) point.y = yscale(pw, event->y);
    if (NTHCHILDISDISPLAYED(pw, CHILD_Y2AXIS)) point.y2 = y2scale(pw,event->y);
    XtCallCallbacks((Widget)pw, XtNmotionCallback, (caddr_t)&point);
  }
}

/*
 * proc registered on the needsRedraw list of each axis in the plotter 
 * would be more efficient if we made a distinction between the cases where 
 * the plotter's color has changed and just needs a redraw and the case where
 * the size of the axis may have changed and the widget needs to be relaid out
 */
static void AxisNeedsRedraw(AtAxisObject axis, AtPlotterWidget pw)
{
  if (XtIsRealized(pw)) {
    AtPlotterLayout(pw);
    Redraw(pw, (Region) NULL);
  }
}


AtPlotWidget AtPlotterGetSelectedPlot(AtPlotterWidget w)
{
  if (w->plotter.legendItem == -1)
    return NULL;
  else
    return (AtPlotWidget)CHILD(w, w->plotter.legendItem);
}

AtPlotWidget AtPlotterGetLastPlot(AtPlotterWidget w)
{
  if (NUMCHILDREN(w) > FIRST_PUBLIC_CHILD)
    return (AtPlotWidget)CHILD(w, NUMCHILDREN(w) - 1);
  else
    return NULL;
}

AtAxisObject AtPlotterGetXAxis(AtPlotterWidget w)
{
  return w->plotter.xaxis;
}

AtAxisObject AtPlotterGetYAxis(AtPlotterWidget w)
{
  return w->plotter.yaxis;
}

AtAxisObject AtPlotterGetY2Axis(AtPlotterWidget w)
{
  return w->plotter.y2axis;
}

AtAxisObject AtPlotterGetXFrameAxis(AtPlotterWidget w)
{
  return w->plotter.xframeAxis;
}

AtAxisObject AtPlotterGetYFrameAxis(AtPlotterWidget w)
{
  return w->plotter.yframeAxis;
}

AtAxisObject AtPlotterGetY2FrameAxis(AtPlotterWidget w)
{
  return w->plotter.y2frameAxis;
}

void AtPlotterGetPlotExtents(AtPlotterWidget w, int *xmin, int *xmax,
			     int *ymin, int *ymax)
{
  *xmin = w->plotter.layout.x1;
  *xmax = w->plotter.layout.x2;
  *ymin = w->plotter.layout.y1;
  *ymax = w->plotter.layout.y2;
}

void AtPlotterGetAxisBounds(AtPlotterWidget w,
			    double *xmin, double *xmax,
			    double *ymin, double *ymax)
{
  *xmin = w->plotter.xaxis->axis.axis_min;
  *xmax = w->plotter.xaxis->axis.axis_max;
  *ymin = w->plotter.yaxis->axis.axis_min;
  *ymax = w->plotter.yaxis->axis.axis_max;
}

void AtPlotterGetAllAxisBounds(AtPlotterWidget w,
			       double *xmin, double *xmax,
			       double *ymin, double *ymax,
			       double *y2min, double *y2max)
{
  *xmin = w->plotter.xaxis->axis.axis_min;
  *xmax = w->plotter.xaxis->axis.axis_max;
  *ymin = w->plotter.yaxis->axis.axis_min;
  *ymax = w->plotter.yaxis->axis.axis_max;
  *y2min = w->plotter.y2axis->axis.axis_min;
  *y2max = w->plotter.y2axis->axis.axis_max;
}

void AtPlotterSetAxisBounds(AtPlotterWidget w,
			    double xmin, double xmax,
			    double ymin, double ymax)
{
  int i;
    
  AtAxisChangeBounds(w->plotter.xaxis, xmin, xmax);
  AtAxisChangeBounds(w->plotter.yaxis, ymin, ymax);
  
  if(XtIsRealized(w))
    AtPlotterLayout(w);
    
  for(i=FIRST_PUBLIC_CHILD; i<NUMCHILDREN(w); i++)
    if (NTHCHILDISDISPLAYED(w, i)) 
      RESCALECHILD(w, i);

  Redraw(w, (Region) NULL);
}    

void AtPlotterSetAllAxisBounds(AtPlotterWidget w,
			       double xmin, double xmax,
			       double ymin, double ymax,
			       double y2min, double y2max)
{
  int i;
    
  AtAxisChangeBounds(w->plotter.xaxis, xmin, xmax);
  AtAxisChangeBounds(w->plotter.yaxis, ymin, ymax);
  AtAxisChangeBounds(w->plotter.y2axis, y2min, y2max);

  if(XtIsRealized(w))  {
    AtPlotterLayout(w);
    for(i=FIRST_PUBLIC_CHILD; i<NUMCHILDREN(w); i++)
      if (NTHCHILDISDISPLAYED(w, i)) 
	RESCALECHILD(w, i);
    Redraw(w, (Region) NULL);
  }
}

void AtPlotterRemoveAllPlots(AtPlotterWidget pw)
{
/* There is a slight subtlity in this procedure.  When XtDestroyWidget
 * is called, it destroys the child and updates the number of children.
 * Because of this reordering, you can't just loop down the list of
 * children and destroy them, because the list is being reordered as
 * you loop down it.  
 *
 * The way around this is to destroy the first public child.  XtDestroyWidget
 * will then make another object the first public child.  You can destroy
 * that, and keep going until all the children are destroyed
 */

  while (NUMCHILDREN(pw) > FIRST_PUBLIC_CHILD)
    XtDestroyWidget(CHILD(pw, FIRST_PUBLIC_CHILD));
}


AtPlotWidget AtPlotterCheckHit(AtPlotterWidget pw, int x, int y)
{
  int i;

  for(i = FIRST_PUBLIC_CHILD; i < NUMCHILDREN(pw); i++)
    if (NTHCHILDISDISPLAYED(pw, i)) 
      if (CHECKHIT(pw,i,x,y)) return (AtPlotWidget)CHILD(pw,i);
  
  return NULL;
}   


/*
 ******************************************************************
 *
 * RankOrderChildren()
 * Sorts the children of Plotter widget on a list according to their
 * Ranking rather than their birth order.  The lowest ranking child is
 * drawn first, whereas, the highest ranking one is drawn last. The
 * highest ranking plot is therfore always visible (never covered by
 * its siblings, if they overlap).  This is useful, for example, if
 * you have several sets of Barcharts that overlap and you want to
 * control which set should be completely visible (in the foreground)
 * at a given time, and in what order the others should cover each
 * other.
 *
 ********************************************************************
*/

#define ORDLIST parent->plotter.ordered_children

Rank *getnode(void);

static void RankOrderChildren(Widget w)
{
  AtPlotterWidget parent = (AtPlotterWidget)XtParent(w);
  AtPlotterConstraints pcons = (AtPlotterConstraints) w->core.constraints;
  Rank *locate, *newnode;
  Rank *getnode(void);
  Boolean found = False;

  if (!XtIsSubclass(w, atPlotWidgetClass)) return; /* Don't rank axes */
  
  if (ORDLIST == NULL) {
    ORDLIST = getnode();
    ORDLIST->prev = NULL;
    ORDLIST->next = NULL;
    ORDLIST->child = w;  
    ORDLIST->rank_order = pcons->plotter.rankOrder; 
    /* higher rank children go on top of lower rank ones.
     * rankOrder is a constraint resource of Plotter. */

    return;
  }
  for (locate = ORDLIST; locate != NULL; locate = locate->next)
    if (pcons->plotter.rankOrder < locate->rank_order) { 
      /* should be inserted right before locate */
      newnode = getnode(); /* get a new node */ 
      newnode->child = w;
      newnode->rank_order = pcons->plotter.rankOrder;
      newnode->prev = locate->prev;
      newnode->next = locate;
      locate->prev = newnode;
      if (newnode->prev == NULL) /* first on the list */
	ORDLIST = newnode;
      else
	(newnode->prev)->next = newnode;
      found = True;
      break;
    }
  if (!found) { /* Highest order so far, insert at end of list */
    for (locate=ORDLIST; locate->next != NULL; locate=locate->next)
      ;
    newnode = getnode(); /* get a new node */ 
    newnode->child = w;
    newnode->rank_order = pcons->plotter.rankOrder;
	newnode->prev = locate;
    newnode->next = locate->next;
    locate->next = newnode;
  }
}

/*
 ***************************************************************
 * 
 *   ReRankOrderChildren
 *   Remove the child whose ranking changed form the 
 *   ordered_children list. Then it will reinsert the removed
 *   child into the list according to its new rankOrder.
 *
 ***************************************************************
 */

void ReRankOrderChildren(Widget w)
{
  RankOrderRemove(w); 
  RankOrderChildren(w); 
}
 
static void RankOrderRemove(Widget w)
{
  AtPlotterWidget parent = (AtPlotterWidget)XtParent(w);
  Rank *locate;
  int i, position;

  if (!XtIsSubclass(w, atPlotWidgetClass)) return; /* Don't rank axes */
  
  for (locate = ORDLIST; locate != NULL; locate = locate->next)  {
    if (locate->child == w) { 
      if (locate->next)
	(locate->next)->prev = locate->prev;
      if (locate->prev == NULL) /* head of list */
	ORDLIST = locate->next;
      else
	(locate->prev)->next = locate->next;
      XtFree (locate);
      break;
    }
  }
}

#undef ORDLIST

Rank* getnode (void)
{
  return ((Rank*) XtMalloc(sizeof(Rank)));
}

/*
 * Need to use eval rather than just c-style as hack-local-variables
 * is called after the c-mode-hook. 
 *
 * Local Variables:
 * eval: (set-c-style 'GNU)
 * End:
 */
