/* $Header: /afs/athena.mit.edu/astaff/project/atdev/src/fmax/RCS/interface.c,v 1.16 91/03/05 09:13:33 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 "fmax.h"
#include "fmaxplot.h"
#include "interface.h"

extern void add_converter ();

static double origXmin, origXmax,	/* axis boundaries when first */
     	      origYmin, origYmax,	/* plotted		      */
     	      origY2min, origY2max;

int setOptFlag = (1 << DISPLAYVAL);	/* Flag current set of options */

/* ARGSUSED */
static void AxisHelp(Widget w)
{
     XtPopup(helpShell);
     AtHelpShowSection((AtHelpWidget)help, "Setup Axes");

}

/* ARGSUSED */
static void PlotHelp(Widget w)
{
     XtPopup(helpShell);
     AtHelpShowSection((AtHelpWidget)help, "Plot Styles");

}

/* ARGSUSED */
static void PlotterHelp(Widget w)
{
     XtPopup(helpShell);
     AtHelpShowSection((AtHelpWidget)help, "Customize Plotter");

}

/* ARGSUSED */
static void PrintHelp(Widget w)
{
     XtPopup(helpShell);
     AtHelpShowSection((AtHelpWidget)help, "Printing Your Plot");

}

void quit()
{
    exit(0);
}

/* ARGSUSED */
void setmarker(Widget w, int marker)
{
    AtPlotWidget plot;
    Arg a;

    plot = AtPlotterGetSelectedPlot((AtPlotterWidget)plotter);
    if (plot == NULL) return;
    XtSetArg(a, XtNmarker, marker);
    XtSetValues(plot, &a, 1);
}
	
/* ARGSUSED */
void setline(Widget w, int linestyle)
{
    AtPlotWidget plot;
    Arg a;

    plot = AtPlotterGetSelectedPlot((AtPlotterWidget)plotter);
    if (plot == NULL) return;
    XtSetArg(a, XtNlineStyle, linestyle);
    XtSetValues(plot, &a, 1);
}

void SetOption(int which, Boolean onoff)
{
     if (onoff)
	  setOptFlag |= (1 << which);
     else
	  setOptFlag &= ~(1 << which);
}


extern AtPlotWidget AtPlotterGetLastPlot();

void delete()
{
    AtPlotWidget plot;
    Arg a;

    plot = AtPlotterGetSelectedPlot((AtPlotterWidget)plotter);
    if ((plot != NULL) || 
	(plot = AtPlotterGetLastPlot((AtPlotterWidget)plotter))) 
	 removePlot (&plot);
    else {
	 XtSetArg (a, XtNlabelString, "No Plot to Delete.");
	 XtSetValues (msgline, &a, 1);
    }
}

void readfile()
{
    char filename[200];
    Boolean status;
     extern void PlotFile();

    filename[0] = '\0';
    status = MuGetString("Filename:", filename, 200, NULL);
    if (!status) return;
    PlotFile(filename);
}

void rescale(double xmin, double xmax, double ymin, double ymax,
	    double y2min, double y2max)
{
     Fmaxplot *p;
     double dx, nextX;
     int i;
     Value v, result;
     Arg args[2];
     
     dx = (xmax - xmin)/(numsamples-1);
     nextX = xmin;
     for (p = plotList; p != (Fmaxplot *)NULL; p=p->next) {
	  if (p->funcTree != (Tree *)NULL) {
	       for (i=0; i < numsamples; i++) {
		    p->xpts[i] = nextX;
		    nextX += dx;
	       }

	       v.type = real;
	       /* get the y values */  /* XXX This is very inefficient! */
	       for (i=0; i < numsamples; i++) {
		    v.r.r = p->xpts[i];
		    EvalTree(p->funcTree, &result, &v);
		    switch(result.type) {
		       case integer:
			 p->ypts[i] = (double)result.i.i;
			 break;
		       case real:
			 p->ypts[i] = result.r.r;
			 break;
		       case complex:
			 p->ypts[i] = result.c.c.r;
			 break;
		       default:
			 XtFree(p->xpts);
			 XtFree(p->ypts);
			 runerr("expression must evaluate to a numeric type.");
			 break;
		    }
	       }
	       XtSetArg (args[0], XtNxPoints, (XtArgVal)p->xpts);
	       XtSetArg (args[1], XtNyPoints, (XtArgVal)p->ypts);
	       XtSetValues (p->theplot, args, 2);
	  }
     }

     AtPlotterSetAllAxisBounds(plotter, xmin, xmax, ymin, ymax, y2min,
			       y2max);

}

void zoomout()
{
    double xmin, xmax;
    double ymin, ymax;
    double y2min, y2max;
 
    AtPlotterGetAllAxisBounds(plotter,&xmin,&xmax,&ymin,&ymax,&y2min,&y2max);

    xmin -= (xmax-xmin)/2;
    xmax += (xmax-xmin)/2;
    ymin -= (ymax-ymin)/2;
    ymax += (ymax-ymin)/2;
    y2min -= (y2max-y2min)/2;
    y2max += (y2max-y2min)/2;

    rescale (xmin, xmax, ymin, ymax, y2min, y2max);

}

void m_zoomin()
{
    double xmin, xmax;
    double ymin, ymax;
    double y2min, y2max;
    double mid, wid;

    AtPlotterGetAllAxisBounds(plotter,&xmin,&xmax,&ymin,&ymax,&y2min,&y2max);
    mid = (xmin + xmax) / 2;
    wid = xmax - xmin;
    xmin = mid - wid/4;
    xmax = mid + wid/4;
    mid = (ymax + ymin) / 2;
    wid = ymax - ymin;
    ymin = mid - wid/4;
    ymax = mid + wid/4;
    y2min = y2min/2;
    y2max = y2max/2;

    AtPlotterSetAllAxisBounds(plotter, xmin, xmax, ymin, ymax, y2min, y2max);
}

/* ARGSUSED */
void zoomin(AtPlotterWidget w, caddr_t tag, RectangleStruct rect)
{
    double xmin, xmax;
    double ymin, ymax;
    double y2min, y2max;
    double tmp;
    Boolean showy2;
    Arg a;
#define swap(x,y) {tmp = x; x = y; y = tmp;}

    xmin = rect[0].x;
    xmax = rect[1].x;
    if (xmin > xmax) swap(xmin, xmax);
    ymin = rect[0].y;
    ymax = rect[1].y;
    if (ymin > ymax) swap(ymin, ymax);
    y2min = rect[0].y2;
    y2max = rect[1].y2;
    if (y2min > y2max) swap(y2min, y2max);
    
    XtSetArg (a, XtNuseY2Axis, &showy2);
    XtGetValues (plotter, &a, 1);

    if ((abs(xmax-xmin)<TOLERANCE) || (abs(ymax-ymin)<TOLERANCE) ||
	(showy2 && (abs(y2max - y2min) < TOLERANCE))) {
	 XtSetArg (a, XtNlabelString, 
		   "Boundaries are too close.  Cannot zoom in.");
	 XtSetValues (msgline, &a, 1);
    }
    else {
	 if (showy2)
	      AtPlotterSetAllAxisBounds(w, xmin, xmax, ymin, 
					ymax, y2min, y2max);
	 else
	      AtPlotterSetAxisBounds(w, xmin, xmax, ymin, ymax);
    }
#undef swap    
}

	       
void panright()
{
    double xmin, xmax;
    double ymin, ymax;
    double y2min, y2max;
    double panInc;
    double dx, nextX;
    int new, i, j;
    Fmaxplot *p;
    Value v, result;
    Arg args[2];

    AtPlotterGetAllAxisBounds(plotter,&xmin,&xmax,&ymin,&ymax,&y2min,&y2max);
    panInc = (xmax - xmin)/4;
    xmin += panInc;
    xmax += panInc;

    for (p = plotList; p != (Fmaxplot *)NULL; p=p->next) {
	  if (p->funcTree != (Tree *)NULL) {
	       new = 0;
	       while (p->xpts[new] < xmin) {
		    new++;
	       }
	       for (i = new; i < p->numpts; i++) {
		    p->xpts[i - new] = p->xpts[i];
		    p->ypts[i - new] = p->ypts[i];
	       }
	       dx = (xmax - xmin)/(p->numpts - 1);
	       nextX = p->xpts[p->numpts - 1] + dx;
	       v.type = real;
	       for (i = p->numpts - new; i < p->numpts; i++) {
		    p->xpts[i] = nextX;
		    v.r.r = p->xpts[i];
		    EvalTree(p->funcTree, &result, &v);
		    switch(result.type) {
		       case integer:
			 p->ypts[i] = (double)result.i.i;
			 break;
		       case real:
			 p->ypts[i] = result.r.r;
			 break;
		       case complex:
			 p->ypts[i] = result.c.c.r;
			 break;
		       default:
			 XtFree(p->xpts);
			 XtFree(p->ypts);
			 runerr("expression must evaluate to a numeric type.");
			 break;
		    }
		    nextX += dx;
	       }
	       j = 0;
	       XtSetArg (args[j], XtNxPoints, (XtArgVal)p->xpts); j++;
	       XtSetArg (args[j], XtNyPoints, (XtArgVal)p->ypts); j++;
	       XtSetValues (p->theplot, args, j);
	  }
     }
    AtPlotterSetAllAxisBounds(plotter, xmin, xmax, ymin, ymax, y2min, y2max);

}

void pandown()
{
    double xmin, xmax;
    double ymin, ymax;
    double y2min, y2max;
    double panInc;

    AtPlotterGetAllAxisBounds(plotter,&xmin,&xmax,&ymin,&ymax,&y2min,&y2max);
    panInc = (ymax - ymin)/4;
    ymin -= panInc;
    y2min -= panInc;
    ymax -= panInc;
    y2max -= panInc;

    AtPlotterSetAllAxisBounds(plotter, xmin, xmax, ymin, ymax, y2min, y2max);
}

void panleft()
{
    double xmin, xmax;
    double ymin, ymax;
    double y2min, y2max;
    double panInc;
    double dx, nextX;
    int new, i, j;
    Fmaxplot *p;
    Value v, result;
    Arg args[10];

    AtPlotterGetAllAxisBounds(plotter,&xmin,&xmax,&ymin,&ymax,&y2min,&y2max);
    panInc = (xmax - xmin)/4;
    xmin -= panInc;
    xmax -= panInc;

    for (p = plotList; p != (Fmaxplot *)NULL; p=p->next) {
	  if (p->funcTree != (Tree *)NULL) {
	       new = 0;
	       while (p->xpts[p->numpts - new - 1] > xmax) {
		    new++;
	       }
	       i = p->numpts - 1;
	       while (i >=  new) {
		    p->xpts[i] = p->xpts[i - new];
		    p->ypts[i] = p->ypts[i - new];
		    i--;
	       }
	       dx = (xmax - xmin)/(p->numpts - 1);
	       nextX = p->xpts[0] - dx;
	       v.type = real;
	       i = new - 1;
	       while (i >= 0 ) {
		    p->xpts[i] = nextX;
		    v.r.r = p->xpts[i];
		    EvalTree(p->funcTree, &result, &v);
		    switch(result.type) {
		       case integer:
			 p->ypts[i] = (double)result.i.i;
			 break;
		       case real:
			 p->ypts[i] = result.r.r;
			 break;
		       case complex:
			 p->ypts[i] = result.c.c.r;
			 break;
		       default:
			 XtFree(p->xpts);
			 XtFree(p->ypts);
			 runerr("expression must evaluate to a numeric type.");
			 break;
		    }
		    nextX -= dx; i--;
	       }
	       j = 0;
	       XtSetArg (args[j], XtNxPoints, (XtArgVal)p->xpts); j++;
	       XtSetArg (args[j], XtNyPoints, (XtArgVal)p->ypts); j++;
	       XtSetValues (p->theplot, args, j);
	  }
     }
    AtPlotterSetAllAxisBounds(plotter, xmin, xmax, ymin, ymax, y2min, y2max);
}

void panup()
{
    double xmin, xmax;
    double ymin, ymax;
    double y2min, y2max;
    double panInc;

    AtPlotterGetAllAxisBounds(plotter,&xmin,&xmax,&ymin,&ymax,&y2min,&y2max);
    panInc = (ymax - ymin)/4;
    ymin += panInc;
    y2min += panInc;
    ymax += panInc;
    y2max += panInc;

    AtPlotterSetAllAxisBounds(plotter, xmin, xmax, ymin, ymax, y2min, y2max);
}

void resetView()
{
     int j, i;
     Fmaxplot *p;
     Arg args[2];
     double xmin, dx;

     AtPlotterSetAllAxisBounds (plotter, origXmin, origXmax, origYmin, 
				origYmax, origY2min, origY2max);
     
     dx = (origXmax - origXmin)/(numsamples-1);
     for (p = plotList; p != (Fmaxplot *)NULL; p=p->next) {
	  if (p->funcTree != (Tree *)NULL) {
	       xmin = origXmin;
	       for(i=0; i < numsamples; i++) {
		    p->xpts[i] = xmin;
		    xmin += dx;
	       }
	       CalcYValues (p->funcTree, p);
	       j = 0;
	       XtSetArg (args[j], XtNxPoints, (XtArgVal)p->xpts); j++;
	       XtSetArg (args[j], XtNyPoints, (XtArgVal)p->ypts); j++;
	       XtSetValues (p->theplot, args, j);
	  }

     }
}
				
/* ARGSUSED */
static void options(Widget w, int arg, XmToggleButtonCallbackStruct *data)
{
    Arg a;

    switch(arg) {
    case 0: a.name = XtNfloatingX; break;
    case 1: a.name = XtNfloatingY; break;
    case 2: a.name = XtNframedAxes; break;
    case 3: a.name = XtNuseY2Axis; break;
    case 4: a.name = XtNshowLegend; break;
    case 5: a.name = XtNautoScale; break;
    case 6: a.name = XtNforceSquare; break;
    }

    a.value = data->set;
    XtSetValues(plotter, &a, 1);
}

/* Must match the paramters passed to this routine in fmax.ad */
static short nextPlotType;
#define XY       0
#define BARCHART 1
static char *plottypes[] =
{"xy",
"barchart"};


/* ARGSUSED */
static void setPlotType(Widget w, int arg, XmToggleButtonCallbackStruct *data)
{
     Widget widg;
     int i;

     if (data->set) {
	  nextPlotType = arg;
	  for (i = 0; i < NUMPLOTTYPES; i++) {
	       if ((widg = MuLookupWidget(plottypes[i])) != w)
			 XmToggleButtonGadgetSetState (widg, False, True);
	  }
     }
}

void settitle()
{
    char buf[100];
    Boolean status;
    Arg a;

    buf[0] = '\0';
    status = MuGetString("Enter New Title:", buf, 99, NULL);
    if (status == False) 
	 return;

    XtSetArg(a, XtNtitle, buf);
    XtSetValues(plotter, &a, 1);
}

/* ARGSUSED */
void editaxis(Widget w, int tag)
{
    void PopupAxisDialog();
    void SetupAxisDialog();
    
    switch(tag) {
    case 1:
	AtDialogDo(axisDialog, (Widget)AtPlotterGetXAxis(plotter));
	break;
    case 2:
	AtDialogDo(axisDialog, (Widget)AtPlotterGetYAxis(plotter));
	break;
    case 3:
	AtDialogDo(axisDialog, (Widget)AtPlotterGetY2Axis(plotter));
	break;
    }
}

void print()
{
    AtDialogDo(printDialog, NULL);
}

void editplotter()
{
     AtDialogDo(plotterDialog, (Widget)plotter);
}

void editplot()
{
    AtPlotWidget plot;
    Fmaxplot *fplot;
    Arg a;

    plot = AtPlotterGetSelectedPlot((AtPlotterWidget)plotter);
    if ((plot != NULL) || 
	((plot = AtPlotterGetLastPlot((AtPlotterWidget)plotter)) != NULL)) {
	 for (fplot = plotList; 
	      fplot->theplot != plot && fplot != (Fmaxplot *)NULL; 
	      fplot = fplot->next)
	      ;
	 if (fplot != (Fmaxplot *)NULL) {
	      switch (fplot->plottype) {
		 case Barchart:
		   AtDialogDo(barplotDialog, (Widget)plot);
		   break;
		 default:
		   AtDialogDo(plotDialog, (Widget)plot);
	      }
	 }
	 else {
	      error ("Can't find plot structure\n");
	 }
    }
    else {
	 XtSetArg (a, XtNlabelString, 
		   "No Plot Selected.");
	 XtSetValues (msgline, &a, 1);
    }
}

void ClearAllPlots()
{
     Fmaxplot *p;

     AtPlotterRemoveAllPlots(plotter);
     for (p = plotList; p != (Fmaxplot *)NULL; p = p->next)
	  removePlot(p);

}

void doneHelp()
{
     XtPopdown(helpShell);
}

#include <setjmp.h>

/* ARGSUSED */
void HandleKeyboardInput(Widget cli, caddr_t tag, char *line)
{
    extern jmp_buf errenv;
    extern char *yystring;

    strcat(line, "\n"); /* XXX a hack for now.  makes parser happy */
    
    yystring = line;
    if (setjmp(errenv) == 0) {
	yyparse();
    }
    fputs("fmax> ", stdout);
    fflush(stdout);
}

/* ARGSUSED */
void annotate(Widget w, int arg)
{
    char *msg;
    Arg a;

    XDefineCursor(XtDisplay(plotter), XtWindow(plotter), crosshair2);
    
    switch(arg) {
    case 1:  /* create */
	clickmode = clickmode_create;
	msg = "Click to position annotation.";
	break;
    case 2:  /* edit */
	clickmode= clickmode_edit;
	msg = "Click on annotation to edit.";
	break;
    case 3:  /* move */
	clickmode = clickmode_movefrom;
	msg = "Click on annotation to move.";
	break;
    case 4:  /* delete */
	clickmode = clickmode_delete;
	msg = "Click on annotation to delete.";
	break;
    }
    XtSetArg (a, XtNlabelString, msg);
    XtSetValues (msgline, &a, 1);
}
	
/* ARGSUSED */
void click(Widget p, caddr_t tag, PointStruct *point)
{
    char buf[100];
    Arg al[5];
    int ac;
    AtPlotWidget plot;
    static AtPlotWidget being_moved = NULL;
    Widget w;

    switch(clickmode) {
    case clickmode_normal:
	sprintf(buf, "x: %.3g;   y: %.3g", point->x, point->y);
	XtSetArg (al[0], XtNlabelString, buf);
	XtSetValues (msgline, al, 1);
	break;

    case clickmode_create:
	XDefineCursor(XtDisplay(plotter), XtWindow(plotter), crosshair);
	ac = 0;
	XtSetArg(al[ac], XtNx, point->pixelx);  ac++;
	XtSetArg(al[ac], XtNy, point->pixely);  ac++;
	XtSetArg(al[ac], XtNfloatingX, &point->x); ac++;
	XtSetArg(al[ac], XtNfloatingY, &point->y); ac++;
	w = XtCreateWidget("annotation", atTextPlotWidgetClass,plotter,al,ac);
    	AtDialogDo(labelDialog, w);
	clickmode = clickmode_normal;
	break;

    case clickmode_edit:
	XDefineCursor(XtDisplay(plotter), XtWindow(plotter), crosshair);
	plot = AtPlotterCheckHit(plotter, point->pixelx, point->pixely);
	if (plot != NULL) 
	     AtDialogDo(labelDialog, (Widget)plot);
	else {
	     XtSetArg (al[0], XtNlabelString, 
		       "No annotation selected.");
	     XtSetValues (msgline, al, 1);
	}
	clickmode = clickmode_normal;
	break;
	
    case clickmode_delete:
	plot = AtPlotterCheckHit(plotter, point->pixelx, point->pixely);
	if (plot != NULL) {
	    XtDestroyWidget(plot);
	    XtSetArg (al[0], XtNlabelString, 
		   "Done.");
	    XtSetValues (msgline, al, 1);
	}
	else {
	     XtSetArg (al[0], XtNlabelString, 
		   "No annotation selected.");
	     XtSetValues (msgline, al, 1);
	}
	XDefineCursor(XtDisplay(plotter), XtWindow(plotter), crosshair);
	clickmode = clickmode_normal;
	break;

    case clickmode_movefrom:
	plot = AtPlotterCheckHit(plotter, point->pixelx, point->pixely);
	if (plot != NULL) {
	     XtSetArg (al[0], XtNlabelString, 
		       "Click on new position.");
	     XtSetValues (msgline, al, 1);
	     clickmode = clickmode_moveto;
	     being_moved = plot;
	}
	else {
	     XtSetArg (al[0], XtNlabelString, 
		   "No annotation selected.");
	     XtSetValues (msgline, al, 1);
	     XDefineCursor(XtDisplay(plotter), XtWindow(plotter), crosshair);
	     clickmode = clickmode_normal;
	}
	break;

    case clickmode_moveto:
	XDefineCursor(XtDisplay(plotter), XtWindow(plotter), crosshair);
	if (being_moved != NULL) {
	    ac = 0;
	    XtSetArg(al[ac], XtNx, point->pixelx);  ac++;
	    XtSetArg(al[ac], XtNy, point->pixely);  ac++;
	    XtSetArg(al[ac], XtNfloatingX, &point->x); ac++;
	    XtSetArg(al[ac], XtNfloatingY, &point->y); ac++;
	    XtSetValues(being_moved, al, ac);
	    being_moved = NULL;
	    XtSetArg (al[0], XtNlabelString, "Done.");
	    XtSetValues (msgline, al, 1);
	}
	clickmode = clickmode_normal;
	break;
    }
}

/* ARGSUSED */
static void AnnotateOk(Widget w, caddr_t tag, caddr_t call_data)
{
    AtDialogApplyCallback(w, tag, call_data);
    AtDialogDoneCallback(w, tag, call_data);
}


/* ARGSUSED */
void doubleclick(Widget p, caddr_t tag, PointStruct *point)
{
    Widget w;
    Arg al[10];
    int ac = 0;

    XtSetArg(al[ac], XtNx, point->pixelx);  ac++;
    XtSetArg(al[ac], XtNy, point->pixely);  ac++;
    XtSetArg(al[ac], XtNfloatingX, &point->x); ac++;
    XtSetArg(al[ac], XtNfloatingY, &point->y); ac++;
    XtSetArg(al[ac], XtNlabel, "testlabel"); ac++;
    w = XtCreateWidget("annotation", atTextPlotWidgetClass, plotter, al, ac);
    
    AtDialogDo(labelDialog, w);
}

static void Help()
{
    XtPopup(helpShell, XtGrabNone);
}

static void About()
{
    MuHelp("Fmax was written by David Flanagan and Dorothy Bowe.\n\
Copyright (c) 1990 Massachusetts Institute of Technology");
}

static void Helphelp()
{

     MuHelp("The fmax help text presents you with two scrolled windows,\n\
one containing topics and the other help text.  You may use the scrollbar\n\
to pan through either of these windows or click the left mouse button on \n\
a particular topic to view it directly.");
}

static MuCallbacksRec callbacks[] = {
  {"read", readfile},
  {"quit", quit},
  {"delete", delete},
  {"setmarker", setmarker},
  {"setline", setline},
  {"options", options},
  {"settitle", settitle},
  {"zoomout", zoomout},
  {"m_zoomin", m_zoomin},
  {"panright", panright},
  {"panleft", panleft},
  {"panup", panup},
  {"pandown", pandown},
  {"resetView", resetView},
  {"editaxis", editaxis},
  {"editplot", editplot},
  {"editplotter", editplotter},
  {"ClearAllPlots", ClearAllPlots},
  {"print", print},
  {"annotate", annotate},
  {"setPlotType", setPlotType},
  {"help", Help},
  {"helphelp", Helphelp},
  {"about", About},
  {"doneHelp", doneHelp},
};

void main(int argc, char **argv)
{

    Widget form, menubar, paned, scrolled;
    void _EditResCheckMessages();
    extern void InitSymbolTable();
    Arg al[1];
    int ac = 0;

    toplevel = XtInitialize("fmax", "FMax", NULL, 0, &argc, argv);
    MuInitialize(toplevel);
#if (XmREVISION > 0)
    add_converter ();
#endif
    /* AtTeX doesn't work with menubar accelerators, so use form */
    form = XmCreateForm(toplevel, "form", NULL, 0);
    XtManageChild(form);
    MuRegisterMenuCallbacks(callbacks, XtNumber(callbacks));
    menubar = MuCreateMenuBar(form, "menubar", NULL, 0);
    XtManageChild(menubar);
    XtSetArg(al[ac], XtNlabelString, "Welcome to Fmax, version 1.1"); ac++;
    msgline = (AtLabelWidget)XtCreateWidget("msgline", 
						  atLabelWidgetClass, 
						  form, al, ac);
    XtManageChild(msgline);
    paned = XmCreatePanedWindow(form, "paned", NULL, 0);
    XtManageChild(paned);
    plotter=(AtPlotterWidget)XtCreateManagedWidget("plotter",
						   atPlotterWidgetClass,
						   paned,NULL,0);
    scrolled = XmCreateScrolledWindow(paned, "scrolled", NULL, 0);
    XtManageChild(scrolled);
    cli = XtCreateManagedWidget("cli", atCliWidgetClass, scrolled, NULL, 0);
    XtAddCallback(cli, XmNinputCallback, HandleKeyboardInput, NULL, 0);
    fputs("fmax> ", stdout);
    fflush(stdout);
    InitSymbolTable();

    XtAddCallback(plotter, XtNclickCallback, click, NULL);
    XtAddCallback(plotter, XtNdragCallback, zoomin, NULL);
    XtAddCallback(plotter, XtNdoubleSelectCallback, editplot, NULL);

    XtRealizeWidget(toplevel);

    crosshair = XCreateFontCursor(XtDisplay(toplevel), XC_crosshair);
    crosshair2 = XCreateFontCursor(XtDisplay(toplevel), XC_cross);
    waitcursor = XCreateFontCursor(XtDisplay(toplevel), XC_watch);

    XDefineCursor(XtDisplay(plotter), XtWindow(plotter), crosshair);


    helpShell = XtCreatePopupShell("helpShell", transientShellWidgetClass,
				   toplevel, NULL, 0);
    help = XtCreateWidget("helpDialog", atHelpWidgetClass, helpShell,
			  NULL, 0);
    XtAddCallback(help, XtNdoneCallback, doneHelp, NULL);
    XtManageChild(help);

    axisDialog = AtDialogCreate(toplevel, "axisDialogShell", "axisDialog",
				AxisDialogInfo, XtNumber(AxisDialogInfo));
    plotDialog = AtDialogCreate(toplevel, "plotDialogShell", "plotDialog",
				PlotDialogInfo,
				XtNumber(PlotDialogInfo));
    barplotDialog = AtDialogCreate(toplevel, "barplotDialogShell", 
				   "barplotDialog", BarPlotDialogInfo,
				   XtNumber(BarPlotDialogInfo));
    printDialog = AtDialogCreate(toplevel, "printDialogShell", "printDialog",
				 PrintDialogInfo, XtNumber(PrintDialogInfo));
    labelDialog = AtDialogCreate(toplevel, "labelDialogShell", "labelDialog",
				 LabelDialogInfo, XtNumber(LabelDialogInfo));
    plotterDialog = AtDialogCreate(toplevel, "plotterDialogShell", "plotterDialog",
				 PlotterDialogInfo, XtNumber(PlotterDialogInfo));
    plotList  = (Fmaxplot *)NULL;
    XtMainLoop();
}

/* Save the boundaries when the plot is first created so we can reset */

void SetAxisBoundaries()
{
     double newXmin, newXmax, newYmin,
     	    newYmax, newY2max, newY2min;

    AtPlotterGetAllAxisBounds(plotter, &newXmin, &newXmax, &newYmin,
			      &newYmax,&newY2min, &newY2max);
     origXmin = newXmin;
     origXmax = newXmax;
     origYmin = newYmin;
     origYmax = newYmax;
     origY2min = newY2min;
     origY2max = newY2max;

}

void PlotArrays(Value *x, Value *y)
{
    double *xpts, *ypts;
    int i, j;
    Arg al[6];
    Fmaxplot *newPlot;

    if (((x != NULL) && (x->type != array)) || (y->type != array)) 
	runerr("array(s) expected.");
    if ((x != NULL) && (x->a.N != y->a.N))
	runerr("arrays must have same number of elements.");

    xpts = (double *)XtMalloc(y->a.N * sizeof(double));
    ypts = (double *)XtMalloc(y->a.N * sizeof(double));

    newPlot = addPlot ((Tree *)NULL, y->a.N, TRUE);
    for(i=0; i<y->a.N; i++) {
	if (x) 
	    switch(x->a.vals[i].type) {
	    case integer:
		xpts[i] = (double) x->a.vals[i].i.i;
		break;
	    case real:
		xpts[i] = x->a.vals[i].r.r;
		break;
	    default:
		XtFree(xpts);
		XtFree(ypts);
		runerr("arrays must contain only integers and real numbers.");
	    }
	else
	    xpts[i] = (double) i;

	switch(y->a.vals[i].type) {
	case integer:
	    ypts[i] = (double) y->a.vals[i].i.i;
	    break;
	case real:
	    ypts[i] = y->a.vals[i].r.r;
	    break;
	default:
	    XtFree(xpts);
	    XtFree(ypts);
	    runerr("arrays must contain only integers and real numbers.");
	}
    }

    j = 0;
    switch (nextPlotType) {
       case BARCHART:
	 XtSetArg (al[j], XtNxMin, &xpts[0]); j++;
	 XtSetArg (al[j], XtNxMax, &xpts[i-1]); j++;
	 XtSetArg (al[j], XtNshading, False); j++;
	 XtSetArg(al[j], XtNyPoints, ypts); j++;
	 XtSetArg(al[j], XtNnumPoints, i); j++;
	 XtSetArg(al[j], XtNlegendName, "unnamed"); j++;
	 newPlot->theplot = (AtPlotWidget) XtCreateWidget("barplot",
				  atBarchartWidgetClass,plotter,al,j);
	 newPlot->plottype = Barchart;
	 break;
       default:
	 XtSetArg(al[j], XtNxPoints, xpts); j++;
	 XtSetArg(al[j], XtNyPoints, ypts); j++;
	 XtSetArg(al[j], XtNnumPoints, i); j++;
	 XtSetArg(al[j], XtNlegendName, "unnamed"); j++;
	 newPlot->theplot = (AtPlotWidget)XtCreateWidget("plot", 
							 atXYPlotWidgetClass, 
							 plotter, al, 4);
	 newPlot->plottype = Xy;
    }
    SetAxisBoundaries();
}


Fmaxplot *PlotExpression(Tree *t)
{
    double xmin, xmax, dx;
    int i;
    Arg args[10];
    Fmaxplot *newPlot;

    /* make a partition */
    AtAxisGetBounds(AtPlotterGetXAxis(plotter), &xmin, &xmax);

    newPlot = addPlot(t, numsamples, TRUE);
    dx = (xmax - xmin)/(numsamples-1);
    for(i=0; i < numsamples; i++) {
	newPlot->xpts[i] = xmin;
	xmin += dx;
    }

    CalcYValues (t, newPlot);

    XtSetArg(args[0], XtNnumPoints, numsamples);
    XtSetArg(args[1], XtNxPoints, newPlot->xpts);
    XtSetArg(args[2], XtNyPoints, newPlot->ypts);
    XtSetArg(args[3], XtNmarkPoints, False);
    XtSetArg(args[4], XtNconnectPoints, True);
    XtSetArg(args[5], XtNlegendName, "unnamed");
    newPlot->theplot =
	 (AtPlotWidget)XtCreateWidget("plot",atXYPlotWidgetClass,plotter,args,6); 
    newPlot->plottype = Xy;
    SetAxisBoundaries();
    return(newPlot);
}


/* get the y values */  /* XXX This is very inefficient! */
static void CalcYValues (Tree *t, Fmaxplot *newPlot)
{
     int i;
    Value v, result;

    v.type = real;
     
    for(i=0; i < numsamples; i++) {
	v.r.r = newPlot->xpts[i];
	EvalTree(t, &result, &v);
	switch(result.type) {
	case integer:
	    newPlot->ypts[i] = (double)result.i.i;
	    break;
	case real:
	    newPlot->ypts[i] = result.r.r;
	    break;
	case complex:
	    newPlot->ypts[i] = result.c.c.r;
	    break;
	default:
	    XtFree(newPlot->xpts);
	    XtFree(newPlot->ypts);
	    runerr("expression must evaluate to a numeric type.");
	    break;
	}
    }
}

#define MAXY 5
#define NUMPATTERNS 7
#define MAXPOINTS 100

extern char *sys_errlist[];
static long numpoints = MAXPOINTS;

void PlotFile(char *fileSpec)
{
    FILE *f;
    char *filename = fileSpec, 
         *formatPtr;
    double *plotData[MAXY+1], *tmp;
    double *x, *y[MAXY];
    char line[100];
    int i, 
        nextY, 
        fillPattern = 0,
        skipRows = 0;
    int numY = 0, j;
    Arg args[12];
    char legendName[MAXFILENAME];
    Fmaxplot *newPlot;

    if ((formatPtr = index(fileSpec, ','))) {
	 *formatPtr = '\0';
	 formatPtr++; 
	 skipRows = fetchColumn(&formatPtr);
    }

    for (i = 0; i < MAXY+1; i++) 
	 plotData[i] = (double *)malloc(MAXPOINTS * sizeof(double));

    f = fopen(filename, "r");
    if (f == NULL) {
	 MuError(sys_errlist[errno]);
	 return;
    }

    /* Skip some rows in the input file ? */
    for (i = 0; i < skipRows; i++)
	fgets (line, MAX_LINE_LEN, f);

    MuSetWaitCursor ((Widget)plotter);
    i = 0;
    while (fgets (line, MAX_LINE_LEN, f)) {
	 if (is_comment(line[0] || !line[1]))
	      continue;
	 switch (numY = sscanf (line, "%lf %lf %lf %lf %lf %lf", 
				&plotData[0][i], &plotData[1][i], 
				&plotData[2][i], &plotData[3][i], 
				&plotData[4][i], &plotData[5][i])) {
	    case 1:				/* only one value so make */
	      plotData[1][i] = plotData[0][i];	/* it the y and set x to  */
	      plotData[0][i] = i;		/* integers		  */
	      break;
	    default:
	      /* Null for now */
	      break;
	 }
	 i++;
	 if (i >= numpoints) {
	      numpoints += MAXPOINTS;
	      for (j=0; j < numY; j++) {
		   if ((tmp = (double *)realloc((char *)plotData[j], 
			   numpoints * sizeof(double)))!=(double *)NULL) {
			plotData[j] = tmp;
		   }
		   else {
			fprintf (stderr, 
				 "fmax: Memory allocation failed after\
%d points. Exiting with errno = %d\n", i, errno);
			exit (errno);
		   }
	      }
	 }
    }
    fclose(f);
    
    if (formatPtr) {
	 x = plotData[fetchColumn(&formatPtr) - 1];
	 j = 0;
	 while (formatPtr) {
	      y[j++] = plotData[fetchColumn(&formatPtr) - 1];
	 }
    }
    else {
	 x = plotData[0];
	 for (j = 1; j < MAXY; j++)
	      y[j-1] = plotData[j];
    }
	 
    MuSetStandardCursor((Widget)plotter);
    if (numY > 1)
	 numY--;			/* One's an X */
    nextY = numY - 1;
    do {
	 j = 0;
	 newPlot = addPlot((Tree *)NULL, i, FALSE);
	 XtSetArg(args[j], XtNnumPoints, i); j++;
	 if (numY > 1) {
	      sprintf (legendName, "%s.%d", filename, nextY+1);
	 }
	 else {
	      sprintf (legendName, "%s", filename);
	 }
	 XtSetArg(args[j], XtNlegendName, legendName); j++;
	 switch (nextPlotType) {
	    case BARCHART:
	      XtSetArg (args[j], XtNxMin, &x[0]); j++;
	      XtSetArg (args[j], XtNxMax, &x[i-1]); j++;
	      XtSetArg (args[j], XtNshading, True); j++;
	      XtSetArg (args[j], XtNpattern, fillPattern);  j++;
	      XtSetArg(args[j], XtNyPoints, y[nextY]); j++;
	      newPlot->theplot = (AtPlotWidget)XtCreateWidget("barplot",
			     atBarchartWidgetClass,plotter,args,j);
	      newPlot->plottype = Barchart;
	      fillPattern = (fillPattern + 1) % NUMPATTERNS;
	      break;
	    default:
	      XtSetArg(args[j], XtNxPoints, x); j++;
	      XtSetArg(args[j], XtNyPoints, y[nextY]); j++;
	      newPlot->theplot = (AtPlotWidget)XtCreateWidget("plot",
						atXYPlotWidgetClass,plotter,
						args,j);
	      newPlot->plottype = Xy;
	 }
	 SetAxisBoundaries();
	 nextY--; 
	 newPlot->funcTree = (Tree *)NULL;
	 
    } while (nextY >= 0);
}
    
/* return the next number in a string of comma separated numbers and set */
/* the char pointer to the next character after comma.  If there is no */
/*  next comma, set the pointer to NULL and return last number */

int fetchColumn (char** format)
{
     char *ptr;
     int col;

     if ((ptr = index (*format, ',')) != (char *)NULL) {
	  *ptr='\0';
	  col = atoi(*format);
	  *format = ptr+1;
     }
     else {
	  col = atoi(*format);
	  *format = (char *)NULL;
     }
     return(col);
}

static Fmaxplot *addPlot(Tree *t, int samples, Boolean needPts)
{
     Fmaxplot *newPlot, *p;


     if ((newPlot = (Fmaxplot *)XtMalloc(sizeof (struct fmaxplot))) == NULL) {
	  fprintf (stderr, "fmax: Could not get space for next plot.\n\
Exiting with errno = %d\n", errno);
	  exit(-1);
     }

     for (p = plotList; (p) && (p->next != (Fmaxplot *)NULL); p = p->next)
	  ;
     if (p)
	  p->next  = newPlot;
     else
	  plotList = newPlot;
     
     newPlot->next = (Fmaxplot *)NULL;
     newPlot->funcTree = t;
     newPlot->numpts = samples;

     if (needPts) {
	  newPlot->xpts = (double *) XtMalloc(samples * sizeof(Value));
	  newPlot->ypts = (double *) XtMalloc(samples * sizeof(Value));
     }
     else {
	  newPlot->xpts = (double *)NULL;
	  newPlot->ypts = (double *)NULL;
     }

     return (newPlot);
}

void SetSamples(int num)
{
     numsamples = num;

}

void SetTitle(char *s)
{
    Arg a;
    XtSetArg(a, XtNtitle, s);
    XtSetValues(plotter, &a, 1);
}

void SetXLabel(char *s)
{
    Arg a;
    XtSetArg(a, XtNlabel, s);
    XtSetValues(AtPlotterGetXAxis(plotter), &a, 1);
}
    
void SetYLabel(char *s)
{
    Arg a;
    XtSetArg(a, XtNlabel, s);
    XtSetValues(AtPlotterGetYAxis(plotter), &a, 1);
}
    
void SetY2Label(char *s)
{
    Arg a;
    XtSetArg(a, XtNlabel, s);
    XtSetValues(AtPlotterGetY2Axis(plotter), &a, 1);
}
    
void AutoScale(Boolean on)
{
     Arg a;

     a.name = XtNautoScale;
     if (on)
	  a.value = True;
     else
	  a.value = False;
    XtSetValues(plotter, &a, 1);
}

void SetAxis(int whichAxis, int axisType)
{
     Arg a;
     AtAxisObject axis;

     if (!whichAxis)
	  axis = AtPlotterGetXAxis(plotter);
     else
	  axis = AtPlotterGetYAxis(plotter);

     if (!axisType)
	  XtSetArg (a, XtNtransform, AtTransformLOG);
     else
	  XtSetArg (a, XtNtransform, AtTransformLINEAR);
     XtSetValues(axis, a, 1);
}
	     
static void PrintCallback(Widget w, Dialog *d) {
    int x1, y1, x2, y2;
    int status = 0;
    int width, height;
    Arg a;

    AtDialogGetValues(d, w);  /* get the values from the dialog box */
    AtDialogDoneCallback(w,d);  /* pop down the dialog window */

    XmUpdateDisplay((Widget)plotter);  /* force a redraw */
    
    MuSetWaitCursor((Widget)plotter);

    /* convert size to points */
    if (printunits == 0) {  /* inches */
	width = printwidth * 72;
	height = printheight * 72;
    }
    else if (printunits == 1) { /* centimeters */
	width = printwidth * 72/2.54;
	height = printheight * 72/2.54;
    }
    else {  /* points */
	width = printwidth;
	height = printheight;
    }

    XtSetArg (a, XtNlabelString, "Generating postscript...");
    XtSetValues(msgline, &a, 1);
    
    if (printto == 1) { /* print to page */
      /* now compute bounding box, centering the plot area on the page */
      /* this assumes 8 1/2 by 11 inch paper */
      if (printorientation == 0) { /* portrait mode */
	x1 = (612 - width)/2;
	x2 = x1 + width - 1;
	y1 = (792 - height)/2;
	y2 = y1 + height;
      }
      else {  /* landscape mode */
	x1 = (792 - width)/2;
	x2 = x1 + width - 1;
	y1 = (612 - height)/2;
	y2 = y1 + height;
      }

      AtPlotterGeneratePostscript(printname, plotter, "Fmax Plot",
				    x1, y1, x2, y2, printorientation);
    }

    else {
      char fname[20];
      char sysbuf[100];
      
      x1 = 1;
      x2 = width;
      y1 = 792 - height;
      y2 = 792;

      strcpy(fname, "/tmp/fmax.XXXXXX");
      mktemp(fname);
      AtPlotterGeneratePostscript(fname, plotter, "Fmax Plot",
				  x1, y1, x2, y2, printorientation);
      XtSetArg (a, XtNlabelString, "Spooling...");
      XtSetValues(msgline, &a, 1);
      sprintf(sysbuf, "lpr -P%s %s", printname, fname);
      status = system(sysbuf);
      unlink(fname);
      
    }
    if (!status) {
	 XtSetArg (a, XtNlabelString, "Done.");
	 XtSetValues (msgline, &a, 1);
    }
    else {
	 XtSetArg (a, XtNlabelString, "Failed.");
	 XtSetValues (msgline, &a, 1);
    }

    MuSetStandardCursor((Widget)plotter);
}


