/*
 *      XYLinePlot.c
 *
 *      The AthenaTools Plotter Widget Set - Version 6.0
 *
 *      klin, Tue Jul  7 13:59:47 1992
 *      klin, Mon Jul 27 14:19:31 1992, patchlevel 2
 *                                      Bugs in DrawPS() and DrawStylePS() fixed.
 *                                      Draw() changed for drawing
 *                                      to a pixmap instead of a window.
 *                                      Plot types steps and bars added.
 *                                      Shorter procedure names.
 *      klin, Fri Aug  7 10:07:59 1992, Cast type converters to keep
 *                                      ANSI C compilers quiet.
 *      klin, Sat Aug 15 10:31:50 1992, patchlevel 4
 *                                      Minor changes in PS output
 *                                      Changed <At/..> to <X11/At/..>.
 */
static char SCCSid[] = "@(#) Plotter V6.0  92/08/15  XYLinePlot.c";

/*

Copyright 1992 by University of Paderborn

All rights reserved.

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 the firms, institutes
or employers of the authors not be used in advertising or publicity
pertaining to distribution of the software without specific, written
prior permission.

THE AUTHORS AND THEIR FIRMS, INSTITUTES OR EMPLOYERS DISCLAIM ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE AUTHORS AND THEIR FIRMS,
INSTITUTES OR EMPLOYERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

*/

/*
 *   The line plot widget
 */

#include <X11/At/PlotterP.h>
#include <X11/At/AxisCoreP.h>
#include <X11/At/XYLinePlotP.h>
#include <X11/At/Plot.h>

/*
 *   Forward declare all the private widgetclass routines
 */

static void Draw P((AtXYLinePlotWidget, Display *, Drawable, Region, int));
static void DrawIcon P((AtXYLinePlotWidget, Display *, Drawable, int, int, int, int, Region));
static void DrawPS P((AtXYLinePlotWidget, FILE *, AtScale *, AtScale *));
static void DrawIconPS P((AtXYLinePlotWidget, FILE *, int, int, int, int));
static void Recalc P((AtXYLinePlotWidget, AtScale *, AtScale *, int, int));
static void Attach P((AtXYLinePlotWidget, BoundingBox *, int));
static void ClassInitialize P((void));
static void Initialize P((AtXYLinePlotWidget, AtXYLinePlotWidget));
static void Destroy P((AtXYLinePlotWidget));
static Boolean SetValues P((AtXYLinePlotWidget, AtXYLinePlotWidget, AtXYLinePlotWidget));

static int CalcMarkOffset P((AtXYLinePlotWidget));
static void DrawOneMark P((AtXYLinePlotWidget, Display *, Drawable, int, int, int));
static void DrawMarks P((AtXYLinePlotWidget, Display *, Drawable));
static void DrawImpulses P((AtXYLinePlotWidget, Display *, Drawable));
static void DrawSteps P((AtXYLinePlotWidget, Display *, Drawable));
static void DrawBars P((AtXYLinePlotWidget, Display *, Drawable));

/*
 *   The resources
 */

#define off(field) XtOffsetOf (AtXYLinePlotRec, lineplot.field)
static XtResource resources[] = {
  {
     XtNplotLineType, XtCPlotLineType,
     XtRPlotLineType, sizeof(AtPlotLineType),
     off(line_type), XtRImmediate, (XtPointer) AtPlotLINES
  },
  {
     XtNplotLineStyle, XtCPlotLineStyle,
     XtRPlotLineStyle, sizeof(AtPlotLineStyle),
     off(line_style), XtRImmediate, (XtPointer) AtLineSOLID
  },
  {
     XtNplotMarkType, XtCPlotMarkType,
     XtRPlotMarkType, sizeof(AtPlotMarkType),
     off(mark_type), XtRImmediate, (XtPointer) AtMarkRECTANGLE
  }
};
#undef  off

AtXYLinePlotClassRec atXYLinePlotClassRec = {
  { /* core fields */
    /* superclass               */      (WidgetClass) &atXYPlotClassRec,
    /* class_name               */      "AtXYLinePlot",
    /* widget_size              */      sizeof(AtXYLinePlotRec),
    /* class_initialize         */      (XtProc) ClassInitialize,
    /* class_part_initialize    */      NULL,
    /* class_inited             */      FALSE,
    /* initialize               */      (XtInitProc) Initialize,
    /* initialize_hook          */      NULL,
    /* pad                      */      NULL,
    /* pad                      */      NULL,
    /* pad                      */      0,
    /* resources                */      resources,
    /* num_resources            */      XtNumber(resources),
    /* xrm_class                */      NULLQUARK,
    /* pad                      */      FALSE,
    /* pad                      */      FALSE,
    /* pad                      */      FALSE,
    /* pad                      */      FALSE,
    /* destroy                  */      (XtWidgetProc) Destroy,
    /* pad                      */      NULL,
    /* pad                      */      NULL,
    /* set_values               */      (XtSetValuesFunc) SetValues,
    /* set_values_hook          */      NULL,
    /* pad                      */      NULL,
    /* get_values_hook          */      NULL,
    /* pad                      */      NULL,
    /* version                  */      XtVersion,
    /* callback_private         */      NULL,
    /* pad                      */      NULL,
    /* pad                      */      NULL,
    /* pad                      */      NULL,
    /* pad                      */      NULL
  },
  { /* atPlot fields */
    /* draw                     */      (AtPlotDrawProc) Draw,
    /* draw_icon                */      (AtPlotDrawIconProc) DrawIcon,
    /* drawPS                   */      (AtPlotDrawPSProc) DrawPS,
    /* draw_iconPS              */      (AtPlotDrawIconPSProc) DrawIconPS,
    /* recalc                   */      (AtPlotRecalcProc) Recalc
  },
  { /* atXYPlot fields */
    /* attach_data              */      (AtXYPlotAttachProc) Attach,
  },
  { /* atXYLinePlot fields */
    /* empty                    */      0
  }
};

WidgetClass atXYLinePlotWidgetClass = (WidgetClass)&atXYLinePlotClassRec;

/*
 *   The core member procs
 */

static void ClassInitialize()
{
     AtRegisterPlotLineStyleConverter();
     AtRegisterPlotLineTypeConverter();
     AtRegisterPlotMarkTypeConverter();
     *SCCSid = *SCCSid;       /* Keep gcc quiet */
}

/*
 *   Some helper functions for GC management
 */

#define DOTTED      0         /* the line style dashes */
static char dotted[]     = { 1, 1 };
#define DASHED      1
static char dashed[]     = { 2, 2 };
#define DOTDASHED   2
static char dotdashed[]  = { 3, 2, 1, 2 };
#define DOTTED2     3
static char dotted2[]    = { 1, 2 };
#define DOTTED3     4
static char dotted3[]    = { 1, 3 };
#define DOTTED4     5
static char dotted4[]    = { 1, 4 };
#define DOTTED5     6
static char dotted5[]    = { 1, 5 };
#define DASHED3     7
static char dashed3[]    = { 3, 3 };
#define DASHED4     8
static char dashed4[]    = { 4, 4 };
#define DASHED5     9
static char dashed5[]    = { 5, 5 };
#define DOTDASHED2  10
static char dotdashed2[] = { 4, 4, 1, 4 };

static struct {               /* The dashes list */
     char *dashes;
     int  length;
} dashlist[] = {
     { dotted,      2 },
     { dashed,      2 },
     { dotdashed,   4 },
     { dotted2,     2 },
     { dotted3,     2 },
     { dotted4,     2 },
     { dotted5,     2 },
     { dashed3,     2 },
     { dashed4,     2 },
     { dashed5,     2 },
     { dotdashed2,  4 }
};
#define HUGE_DASH   200

static void GetLineGC P((AtXYLinePlotWidget w));
static void GetLineGC(w)
AtXYLinePlotWidget w;
{
     XGCValues gcv;
     XtGCMask gcmask;
     int dash;

     if (w->plot.fast_update)  {
	  gcv.foreground = w->plot.background ^ w->plot.foreground;
	  gcv.function = GXxor;
	  gcmask = GCForeground | GCFunction;
     } else {
	  gcv.foreground = w->plot.foreground;
	  gcv.background = w->plot.background;
	  gcmask = GCForeground | GCBackground;
     }

     /*
      *   We have to create a unique GC with the appropriate dash list
      *   for each line type. XtGetGC() shares same GCs between different
      *   widgets and we had to create a new GC for each line using the
      *   Xlib function XCreateGC(). Setting the dashes member in the GC
      *   to a unlikely value (>200) allows us to use XtGetGC() and so
      *   to share same GCs between line widgets with the same line type.
      */
     switch(w->lineplot.line_style) {
	  case AtLineDOTTED:
	       gcv.line_style = LineOnOffDash;
	       dash = DOTTED;
	       gcv.dashes     = (char) HUGE_DASH+DOTTED;
	       gcmask |= GCDashList;
	       break;
	  case AtLineDASHED:
	       gcv.line_style = LineOnOffDash;
	       dash = DASHED;
	       gcv.dashes     = (char) HUGE_DASH+DASHED;
	       gcmask |= GCDashList;
	       break;
	  case AtLineDOTDASHED:
	       gcv.line_style = LineOnOffDash;
	       dash = DOTDASHED;
	       gcv.dashes     = (char) HUGE_DASH+DOTDASHED;
	       gcmask |= GCDashList;
	       break;
	  case AtLineDOTTED2:
	       gcv.line_style = LineOnOffDash;
	       dash = DOTTED2;
	       gcv.dashes     = (char) HUGE_DASH+DOTTED2;
	       gcmask |= GCDashList;
	       break;
	  case AtLineDOTTED3:
	       gcv.line_style = LineOnOffDash;
	       dash = DOTTED3;
	       gcv.dashes     = (char) HUGE_DASH+DOTTED3;
	       gcmask |= GCDashList;
	       break;
	  case AtLineDOTTED4:
	       gcv.line_style = LineOnOffDash;
	       dash = DOTTED4;
	       gcv.dashes     = (char) HUGE_DASH+DOTTED4;
	       gcmask |= GCDashList;
	       break;
	  case AtLineDOTTED5:
	       gcv.line_style = LineOnOffDash;
	       dash = DOTTED5;
	       gcv.dashes     = (char) HUGE_DASH+DOTTED5;
	       gcmask |= GCDashList;
	       break;
	  case AtLineDASHED3:
	       gcv.line_style = LineOnOffDash;
	       dash = DASHED3;
	       gcv.dashes     = (char) HUGE_DASH+DASHED3;
	       gcmask |= GCDashList;
	       break;
	  case AtLineDASHED4:
	       gcv.line_style = LineOnOffDash;
	       dash = DASHED4;
	       gcv.dashes     = (char) HUGE_DASH+DASHED4;
	       gcmask |= GCDashList;
	       break;
	  case AtLineDASHED5:
	       gcv.line_style = LineOnOffDash;
	       dash = DASHED5;
	       gcv.dashes     = (char) HUGE_DASH+DASHED5;
	       gcmask |= GCDashList;
	       break;
	  case AtLineDOTDASHED2:
	       gcv.line_style = LineOnOffDash;
	       dash = DOTDASHED2;
	       gcv.dashes     = (char) HUGE_DASH+DOTDASHED2;
	       gcmask |= GCDashList;
	       break;
	  case AtLineSOLID:
	  default:
	       gcv.line_style = LineSolid;
	       dash = -1;
	       break;
     }
     gcv.line_width = w->plot.line_width;
     gcmask |= GCLineStyle | GCLineWidth;

     w->lineplot.line_gc = XtGetGC(XtParent((Widget)w), gcmask, &gcv);

     if (dash >= 0) {         /* now set the dashlist */
	  XSetDashes(XtDisplay(XtParent((Widget)w)), w->lineplot.line_gc,
		     0, dashlist[dash].dashes, dashlist[dash].length);
     }
}

static void GetMarkGC P((AtXYLinePlotWidget w));
static void GetMarkGC(w)
AtXYLinePlotWidget w;
{
     XGCValues gcv;
     XtGCMask gcmask;

     gcv.line_style = LineSolid;
     if (w->plot.fast_update)  {
	  gcv.foreground = w->plot.background ^ w->plot.foreground;
	  gcv.function = GXxor;
	  gcmask = GCForeground | GCFunction | GCLineStyle;
     } else {
	  gcv.foreground = w->plot.foreground;
	  gcv.background = w->plot.background;
	  gcmask = GCForeground | GCBackground | GCLineStyle;
     }

     w->lineplot.mark_gc = XtGetGC(XtParent((Widget)w), gcmask, &gcv);
}

#define FreeLineGC(w) XtReleaseGC((Widget)w, w->lineplot.line_gc)
#define FreeMarkGC(w) XtReleaseGC((Widget)w, w->lineplot.mark_gc)

/*
 *   The initialize/destroy/setvalues procs
 */

static void Initialize(request, new)
AtXYLinePlotWidget request, new;
{
     GetLineGC(new);
     GetMarkGC(new);
}

static void Destroy(w)
AtXYLinePlotWidget w;
{
     FreeLineGC(w);
     FreeMarkGC(w);
}

static Boolean SetValues(current, request, new)
AtXYLinePlotWidget current, request, new;
{
#define Changed(field) (new->lineplot.field != current->lineplot.field)
     Boolean redraw = False;

     if ((new->plot.foreground != current->plot.foreground) ||
	 (new->plot.background != current->plot.background) ||
	 (new->plot.line_width != current->plot.line_width)) {
	  FreeLineGC(new);
	  FreeMarkGC(new);
	  GetLineGC(new);
	  GetMarkGC(new);
	  redraw = True;
     }
     else if (Changed(line_style)) {
	  FreeLineGC(new);
	  GetLineGC(new);
	  redraw = True;
     }
     if (Changed(line_type) || Changed(mark_type)) {
	  redraw = True;
     }
     if (redraw) {
	  AtPlotterRedrawRequired((AtPlotWidget) new);
     }
     /* Nothing to redisplay */
     return False;
#undef Changed
}

/*
 *   These routines are the ones called by the parent plot widget
 */

#define lp ((AtXYLinePlotWidget)self)
#define PIX ((XPoint *) lp->lplot.pix)
/* NB: PIX is NOT an lvalue (on some very picky compilers!!!) */

/*
 *   Don't need to adjust the bbox, only to allocate the memory.
 */

static void Attach(self, bbp, extending)
AtXYLinePlotWidget self;
BoundingBox *bbp;
int extending;
{
     if (extending)
	  lp->lplot.pix = XtRealloc((char *) PIX,
				    lp->lplot.num_points * sizeof (XPoint));
     else
	  lp->lplot.pix = XtMalloc(lp->lplot.num_points * sizeof (XPoint));
}

/*
 *   Internal procs for drawing marks/impulses/steps/bars
 */

static int CalcMarkOffset(self)
AtXYLinePlotWidget self;
{
     int w, off;

     if (lp->plot.line_width < 2)
	  w = 1;
     else if( lp->plot.line_width < 4)
	  w = 2;
     else
	  w = 3;
     switch (lp->lineplot.mark_type) {
	  case AtMarkPLUS:
	  case AtMarkXMARK:
	  case AtMarkSTAR:
	  case AtMarkTRIANGLE1:
	  case AtMarkTRIANGLE2:
	  case AtMarkTRIANGLE3:
	  case AtMarkTRIANGLE4:
	  case AtMarkDIAMOND:
	       off = 2 * w;
	       break;
	  case AtMarkRECTANGLE:
	  default:
	       off = w;
	       break;
     }
     return off;
}

static void DrawOneMark(self, dpy, drw, x, y, off)
AtXYLinePlotWidget self;
Display *dpy;
Drawable drw;
int x, y, off;
{
     int len;

     switch (lp->lineplot.mark_type) {
	  case AtMarkPLUS:
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x, y - off, x, y + off);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y, x + off, y);
	       break;
	  case AtMarkXMARK:
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y - off, x + off, y + off);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y + off, x + off, y - off);
	       break;
	  case AtMarkSTAR:
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x, y - off, x, y + off);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y, x + off, y);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y - off, x + off, y + off);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y + off, x + off, y - off);
	       break;
	  case AtMarkTRIANGLE1:
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y + off, x + off, y + off);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x + off, y + off, x, y - off);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x, y - off, x - off, y + off);
	       break;
	  case AtMarkTRIANGLE2:
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y - off, x + off, y - off);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x + off, y - off, x, y + off);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x, y + off, x - off, y - off);
	       break;
	  case AtMarkTRIANGLE3:
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y - off, x - off, y + off);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y + off, x + off, y);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x + off, y, x - off, y - off);
	       break;
	  case AtMarkTRIANGLE4:
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x + off, y - off, x + off, y + off);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x + off, y + off, x - off, y);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y, x + off, y - off);
	       break;
	  case AtMarkDIAMOND:
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x, y - off, x - off, y);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x - off, y, x, y + off);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x, y + off, x + off, y);
	       XDrawLine(dpy, drw, lp->lineplot.mark_gc,
			 x + off, y, x, y - off);
	       break;
	  case AtMarkRECTANGLE:
	  default:
	       len = 1 + off + off;
	       XDrawRectangle(dpy, drw, lp->lineplot.mark_gc,
			      x - off, y - off, len, len);
     }

}

static void DrawMarks(self, dpy, drw)
AtXYLinePlotWidget self;
Display *dpy;
Drawable drw;
{
     int i, off;

     off = CalcMarkOffset(self);
     for (i = 0; i < lp->lplot.num_points; i++) {
	  DrawOneMark(self, dpy, drw, PIX[i].x, PIX[i].y, off);
     }
}

static void DrawImpulses(self, dpy, drw)
AtXYLinePlotWidget self;
Display *dpy;
Drawable drw;
{
     int i;

     for (i = 0; i < lp->lplot.num_points; i++) {
	  XDrawLine(dpy, drw, lp->lineplot.line_gc,
		    PIX[i].x, PIX[i].y, PIX[i].x, lp->lineplot.impulse_y);
     }
}

static void DrawSteps(self, dpy, drw)
AtXYLinePlotWidget self;
Display *dpy;
Drawable drw;
{
     int i;

     for (i = 1; i < lp->lplot.num_points; i++) {
	  XDrawLine(dpy, drw, lp->lineplot.line_gc,
		    PIX[i-1].x, PIX[i-1].y, PIX[i].x, PIX[i-1].y);
	  XDrawLine(dpy, drw, lp->lineplot.line_gc,
		    PIX[i].x, PIX[i-1].y, PIX[i].x, PIX[i].y);
     }
}

static void DrawBars(self, dpy, drw)
AtXYLinePlotWidget self;
Display *dpy;
Drawable drw;
{
     int i;

     if (lp->lplot.num_points > 1) {
	  XDrawLine(dpy, drw, lp->lineplot.line_gc,
		    PIX[0].x, PIX[0].y, PIX[0].x, lp->lineplot.impulse_y);
     }
     for (i = 1; i < lp->lplot.num_points; i++) {
	  XDrawLine(dpy, drw, lp->lineplot.line_gc,
		    PIX[i-1].x, PIX[i-1].y, PIX[i].x, PIX[i-1].y);
	  XDrawLine(dpy, drw, lp->lineplot.line_gc,
		    PIX[i].x, PIX[i-1].y, PIX[i].x, PIX[i].y);
	  XDrawLine(dpy, drw, lp->lineplot.line_gc,
		    PIX[i].x, PIX[i].y, PIX[i].x, lp->lineplot.impulse_y);
     }
}

/*
 *   Draw the line clipped by the given region.
 */

static void Draw(self, dpy, drw, region, refresh)
AtXYLinePlotWidget self;
Display *dpy;
Drawable drw;
Region region;
int refresh;
{
     AtPlotterWidget pw = (AtPlotterWidget) XtParent((Widget) self);
     AtPlotterLayout *pwl = &pw->plotter.layout;
     XRectangle linerectangle, markrectangle;
     Region lineregion, markregion;
     int x1, x2, y1, y2, off;

     /* Get clip regions for lines and marks from the plotter layout */
     linerectangle.x = pwl->x1;
     linerectangle.y = pwl->y1;
     linerectangle.width  = pwl->width;
     linerectangle.height = pwl->height;
     off = CalcMarkOffset(self);
     markrectangle.x = pwl->x1 - off;
     markrectangle.y = pwl->y1 - off;
     markrectangle.width  = pwl->width  + 2*off;
     markrectangle.height = pwl->height + 2*off;

     /* Now create and set the regions */
     lineregion = XCreateRegion();
     markregion = XCreateRegion();
     XUnionRectWithRegion(&linerectangle, lineregion, lineregion);
     XUnionRectWithRegion(&markrectangle, markregion, markregion);
     XSetRegion(dpy, lp->lineplot.line_gc, lineregion);
     XSetRegion(dpy, lp->lineplot.mark_gc, markregion);

#ifdef TRACE
     fprintf(stderr, "Draw %d lines/points\n",
	     lp->lplot.num_points);
#endif

     if (lp->lplot.old_pix) {
	  if (lp->plot.fast_update && refresh) {
	       /*
		* We are in fast update mode, doing a refresh and have old
		* pixpoints, so draw them to "erase" the old s first
		*/
	       switch (lp->lineplot.line_type) {
		    case AtPlotPOINTS:
			 DrawMarks(self, dpy, drw);
			 break;
		    case AtPlotLINEPOINTS:
			 XDrawLines(dpy, drw, lp->lineplot.line_gc,
				    (XPoint *) lp->lplot.old_pix,
				    lp->lplot.old_num_points, CoordModeOrigin);
			 DrawMarks(self, dpy, drw);
			 break;
		    case AtPlotIMPULSES:
			 DrawImpulses(self, dpy, drw);
			 break;
		    case AtPlotLINEIMPULSES:
			 DrawImpulses(self, dpy, drw);
			 XDrawLines(dpy, drw, lp->lineplot.line_gc,
				    (XPoint *) lp->lplot.old_pix,
				    lp->lplot.old_num_points, CoordModeOrigin);
			 break;
		    case AtPlotSTEPS:
			 DrawSteps(self, dpy, drw);
			 break;
		    case AtPlotBARS:
			 DrawBars(self, dpy, drw);
		    case AtPlotLINES:
		    default:
			 XDrawLines(dpy, drw, lp->lineplot.line_gc,
				    (XPoint *) lp->lplot.old_pix,
				    lp->lplot.old_num_points, CoordModeOrigin);
			 break;
	       }

	  }
	  XtFree((char *) lp->lplot.old_pix);
	  lp->lplot.old_pix = NULL;
	  lp->lplot.old_num_points = 0;
     }

     switch (lp->lineplot.line_type) {
	  case AtPlotPOINTS:
	       DrawMarks(self, dpy, drw);
	       break;
	  case AtPlotLINEPOINTS:
	       XDrawLines(dpy, drw, lp->lineplot.line_gc, PIX,
			  lp->lplot.num_points, CoordModeOrigin);
	       DrawMarks(self, dpy, drw);
	       break;
	  case AtPlotIMPULSES:
	       DrawImpulses(self, dpy, drw);
	       break;
	  case AtPlotLINEIMPULSES:
	       DrawImpulses(self, dpy, drw);
	       XDrawLines(dpy, drw, lp->lineplot.line_gc, PIX,
			  lp->lplot.num_points, CoordModeOrigin);
	       break;
	  case AtPlotSTEPS:
	       DrawSteps(self, dpy, drw);
	       break;
	  case AtPlotBARS:
	       DrawBars(self, dpy, drw);
	       break;
	  case AtPlotLINES:
	  default:
	       XDrawLines(dpy, drw, lp->lineplot.line_gc, PIX,
			  lp->lplot.num_points, CoordModeOrigin);
	       break;
     }

     /* Unset and destroy the regions */
     XSetClipMask(dpy, lp->lineplot.line_gc, None);
     XSetClipMask(dpy, lp->lineplot.mark_gc, None);
     XDestroyRegion(lineregion);
     XDestroyRegion(markregion);
}

/*
 *   Draw the "icon" in the given place.
 */

static void DrawIcon(self, dpy, drw, x1, y1, width, height, region)
AtXYLinePlotWidget self;
Display *dpy;
Drawable drw;
int x1, y1, width, height;
Region region;
{
     int off;

     y1 += height >> 1;
     switch (lp->lineplot.line_type) {
	  case AtPlotPOINTS:
	       off = CalcMarkOffset(self);
	       x1 += width >> 1;
	       DrawOneMark(self, dpy, drw, x1, y1, off);
	       break;
	  case AtPlotLINEPOINTS:
	       XDrawLine(dpy, drw, lp->lineplot.line_gc,
			 x1, y1, x1 + width, y1);
	       off = CalcMarkOffset(self);
	       x1 += width >> 1;
	       DrawOneMark(self, dpy, drw, x1, y1, off);
	       break;
	  case AtPlotLINEIMPULSES:
	  case AtPlotIMPULSES:
	  case AtPlotSTEPS:
	  case AtPlotBARS:
	  case AtPlotLINES:
	  default:
	       XDrawLine(dpy, drw, lp->lineplot.line_gc,
			 x1, y1, x1 + width, y1);
	       break;
     }
}

/*
 *   PostScript stuff
 */

static void DrawStylePS P((AtXYLinePlotWidget, FILE *));
static void DrawMarkPS P((AtXYLinePlotWidget, FILE *, int, int));

static void DrawStylePS(self, fp)
AtXYLinePlotWidget self;
FILE *fp;
{
     double w;

     fprintf(fp, "[");
     switch(self->lineplot.line_style) {
	  case AtLineDOTTED:
	       fprintf(fp, " 1 ");
	       break;
	  case AtLineDASHED:
	       fprintf(fp, " 2 ");
	       break;
	  case AtLineDOTDASHED:
	       fprintf(fp, " 3 2 1 2");
	       break;
	  case AtLineDOTTED2:
	       fprintf(fp, " 1 2 ");
	       break;
	  case AtLineDOTTED3:
	       fprintf(fp, " 1 3 ");
	       break;
	  case AtLineDOTTED4:
	       fprintf(fp, " 1 4 ");
	       break;
	  case AtLineDOTTED5:
	       fprintf(fp, " 1 5 ");
	       break;
	  case AtLineDASHED3:
	       fprintf(fp, " 3 ");
	       break;
	  case AtLineDASHED4:
	       fprintf(fp, " 4 ");
	       break;
	  case AtLineDASHED5:
	       fprintf(fp, " 5 ");
	       break;
	  case AtLineDOTDASHED2:
	       fprintf(fp, " 4 4 1 4 ");
	       break;
	  case AtLineSOLID:
	  default:
	       fprintf(fp, " ");
	       break;
     }
     fprintf(fp, "] 0 setdash ");
     if (self->plot.line_width == 0)
	  w = 0.5;
     else
	  w = 0.5 * (double) self->plot.line_width;
     fprintf(fp, "%.1lf setlinewidth\n", w);
}

static void DrawMarkPS(self, fp, x1, y1)
AtXYLinePlotWidget self;
FILE *fp;
int x1, y1;
{
     char *psf;

     switch (lp->lineplot.mark_type) {
	  case AtMarkPLUS:
	       psf = "PLUS"; break;
	  case AtMarkXMARK:
	       psf = "XMRK"; break;
	  case AtMarkSTAR:
	       psf = "STAR"; break;
	  case AtMarkTRIANGLE1:
	       psf = "TRI1"; break;
	  case AtMarkTRIANGLE2:
	       psf = "TRI2"; break;
	  case AtMarkTRIANGLE3:
	       psf = "TRI3"; break;
	  case AtMarkTRIANGLE4:
	       psf = "TRI4"; break;
	  case AtMarkDIAMOND:
	       psf = "DIAM"; break;
	  case AtMarkRECTANGLE:
	  default:
	       psf = "RECT"; break;
     }
     fprintf(fp, "%d %d %s\n", x1, y1, psf);
}

static void DrawPS(self, fp, xs, ys)
AtXYLinePlotWidget self;
FILE *fp;
AtScale *xs, *ys;
{
     double x, y, l;
     int xp, yp, yo, y0;
     int i, count;

     fprintf(fp, "%%%%BeginObject: AtXYLinePlot\nGS\n");
     /* Set the clip region */
     fprintf(fp, "%d %d M %d %d L %d %d L %d %d L CP clip newpath\n",
	     xs->lowpix,  ys->lowpix,  xs->lowpix,  ys->highpix,
	     xs->highpix, ys->highpix, xs->highpix, ys->lowpix);

     /* First the points if needed */
     if (lp->lineplot.line_type == AtPlotPOINTS ||
	 lp->lineplot.line_type == AtPlotLINEPOINTS) {
	  fprintf(fp, "[ ] 0 setdash 0.5 setlinewidth\n");
	  count = 0;
	  for (i = 1; i < lp->lplot.num_points; i++) {
	       x = AtXYPlotGetXValue((AtXYPlotWidget) lp, i);
	       y = AtXYPlotGetYValue((AtXYPlotWidget) lp, i);
	       if (xs->transform == AtTransformLOGARITHMIC && x <= 0.0) {
		    l = log10(xs->low) - 5.0;
		    x = pow(10.0, l);
	       }
	       if (ys->transform == AtTransformLOGARITHMIC && y <= 0.0) {
		    l = log10(ys->low) - 5.0;
		    y = pow(10.0, l);
	       }
	       xp = AtScaleUserToPixel(xs, x);
	       yp = AtScaleUserToPixel(ys, y);
	       DrawMarkPS(self, fp, xp, yp);
	       if (++count > 25) {
		    fprintf(fp, "ST\n");
		    count = 0;
	       }
	  }
	  fprintf(fp, "ST\n");
     }

     /* Now the lines if needed */
     if (lp->lineplot.line_type != AtPlotPOINTS) {
	  DrawStylePS(self, fp);
	  if (lp->lineplot.line_type == AtPlotIMPULSES ||
	      lp->lineplot.line_type == AtPlotLINEIMPULSES ||
	      lp->lineplot.line_type == AtPlotBARS) {
	       y0 = ys->low > 0.0 ? AtScaleUserToPixel(ys, ys->low) :
				    AtScaleUserToPixel(ys, 0.0);
	  }
	  count = 0;
	  for (i = 1; i < lp->lplot.num_points; i++) {
	       if ( !count) {
		    x = AtXYPlotGetXValue((AtXYPlotWidget) lp, i-1);
		    y = AtXYPlotGetYValue((AtXYPlotWidget) lp, i-1);
		    if (xs->transform == AtTransformLOGARITHMIC && x <= 0.0) {
			 l = log10(xs->low) - 5.0;
			 x = pow(10.0, l);
		    }
		    if (ys->transform == AtTransformLOGARITHMIC && y <= 0.0) {
			 l = log10(ys->low) - 5.0;
			 y = pow(10.0, l);
		    }
		    xp = AtScaleUserToPixel(xs, x);
		    yp = AtScaleUserToPixel(ys, y);
		    if (i == 1 && (lp->lineplot.line_type == AtPlotIMPULSES ||
			lp->lineplot.line_type == AtPlotLINEIMPULSES ||
			lp->lineplot.line_type == AtPlotBARS)) {
			 fprintf(fp, "%d %d M %d %d L ", xp, y0, xp, yp);
		    }
		    else
			 fprintf(fp, "%d %d M ", xp, yp);
		    yo = yp;
	       }
	       x = AtXYPlotGetXValue((AtXYPlotWidget) lp, i);
	       y = AtXYPlotGetYValue((AtXYPlotWidget) lp, i);
	       if (xs->transform == AtTransformLOGARITHMIC && x <= 0.0) {
		    l = log10(xs->low) - 5.0;
		    x = pow(10.0, l);
	       }
	       if (ys->transform == AtTransformLOGARITHMIC && y <= 0.0) {
		    l = log10(ys->low) - 5.0;
		    y = pow(10.0, l);
	       }
	       xp = AtScaleUserToPixel(xs, x);
	       yp = AtScaleUserToPixel(ys, y);
	       switch (lp->lineplot.line_type) {
		    case AtPlotIMPULSES:
			 fprintf(fp, "%d %d M %d %d L\n", xp, yp, xp, y0);
			 break;
		    case AtPlotLINEIMPULSES:
			 fprintf(fp, "%d %d L %d %d L %d %d M\n", xp, yp, xp, y0, xp, yp);
			 break;
		    case AtPlotSTEPS:
			 fprintf(fp, "%d %d L %d %d L\n", xp, yo, xp, yp);
			 yo = yp;
			 break;
		    case AtPlotBARS:
			 fprintf(fp, "%d %d L %d %d L %d %d L\n", xp, yo, xp, y0, xp, yp);
			 yo = yp;
			 break;
		    default:       /* All others */
			 fprintf(fp, "%d %d L\n", xp, yp);
			 break;
	       }
	       if (++count > 50) {
		    fprintf(fp, "ST\n");
		    count = 0;
	       }
	  }
     }

     fprintf(fp, "%sGR\n%%%%EndObject: AtXYLinePlot\n", count ? "ST\n" : "");
}

static void DrawIconPS(self, fp, x1, y1, width, height)
AtXYLinePlotWidget self;
FILE *fp;
int x1, y1, width, height;
{
     fprintf(fp, "GS ");
     if (lp->lineplot.line_type == AtPlotPOINTS ||
	 lp->lineplot.line_type == AtPlotLINEPOINTS) {
	  fprintf(fp, "[ ] 0 setdash 0.5 setlinewidth\n");
	  DrawMarkPS(self, fp, x1 + (width >> 1), y1 - (height >> 1));
	  fprintf(fp, "ST\n");
     }
     if (lp->lineplot.line_type != AtPlotPOINTS) {
	  DrawStylePS(self, fp);
	  fprintf(fp, "%d %d M %d 0 RL\n", x1, y1 - (height >> 1), width);
	  fprintf(fp, "ST\n");
     }
     fprintf(fp, "GR\n");
}


/*
 *   Recalc the data according to the passed x and y scales
 */

static void Recalc(self, xs, ys, from, to)
AtXYLinePlotWidget self;
AtScale *xs, *ys;
int from, to;
{
     double x, y, l;
     int i;

#ifdef TRACE
     fprintf(stderr, " -- Recalc from %d to %d\n", from, to);
#endif
     if (from > to) {
	  from = 0;
	  to = lp->lplot.num_points - 1;
     }
     for (i = from; i <= to; i++) {
	  x = AtXYPlotGetXValue((AtXYPlotWidget) lp, i);
	  y = AtXYPlotGetYValue((AtXYPlotWidget) lp, i);
	  if (xs->transform == AtTransformLOGARITHMIC && x <= 0.0) {
	       l = log10(xs->low) - 5.0;
	       x = pow(10.0, l);
	  }
	  if (ys->transform == AtTransformLOGARITHMIC && y <= 0.0) {
	       l = log10(ys->low) - 5.0;
	       y = pow(10.0, l);
	  }
	  PIX[i].x = AtScaleUserToPixel(xs, x);
	  PIX[i].y = AtScaleUserToPixel(ys, y);
     }
     if (ys->low <= 0.0)
	  lp->lineplot.impulse_y = AtScaleUserToPixel(ys, 0.0);
     else
	  lp->lineplot.impulse_y = AtScaleUserToPixel(ys, ys->low);
}
#undef lp

/*
 *   Resource converters
 */

static void AtCvtStringToPlotLineType(args, num_args, from, to)
XrmValue *args;
Cardinal num_args;
XrmValue *from, *to;
{
     static AtPlotLineType linetype;

     linetype = AtPlotINVALID;

     if (strcasecmp(from->addr, "lines") == 0)
	  linetype = AtPlotLINES;
     else if (strcasecmp(from->addr, "points") == 0)
	  linetype = AtPlotPOINTS;
     else if (strcasecmp(from->addr, "impulses") == 0)
	  linetype = AtPlotIMPULSES;
     else if (strcasecmp(from->addr, "steps") == 0)
	  linetype = AtPlotSTEPS;
     else if (strcasecmp(from->addr, "bars") == 0)
	  linetype = AtPlotBARS;
     else if (strcasecmp(from->addr, "linepoints") == 0)
	  linetype = AtPlotLINEPOINTS;
     else if (strcasecmp(from->addr, "lineimpulses") == 0)
	  linetype = AtPlotLINEIMPULSES;

     if (linetype == AtPlotINVALID)
	  XtStringConversionWarning(from->addr, XtRPlotLineType);
     else {
	  to->addr = (caddr_t) &linetype;
	  to->size = sizeof(AtPlotLineType);
     }
}

void AtRegisterPlotLineTypeConverter()
{
     static Boolean registered = False;

     if (!registered) {
	  XtAddConverter(XtRString, XtRPlotLineType,
			 (XtConverter) AtCvtStringToPlotLineType, NULL,0);
	  registered = True;
     }
}

static void AtCvtStringToPlotLineStyle(args, num_args, from, to)
XrmValue *args;
Cardinal num_args;
XrmValue *from, *to;
{
     static AtPlotLineStyle linestyle;

     linestyle = AtLineINVALID;

     if (strcasecmp(from->addr, "solid") == 0)
	  linestyle = AtLineSOLID;
     else if (strcasecmp(from->addr, "dotted") == 0)
	  linestyle = AtLineDOTTED;
     else if (strcasecmp(from->addr, "dashed") == 0)
	  linestyle = AtLineDASHED;
     else if (strcasecmp(from->addr, "dotdashed") == 0)
	  linestyle = AtLineDOTDASHED;
     else if (strcasecmp(from->addr, "dotted2") == 0)
	  linestyle = AtLineDOTTED2;
     else if (strcasecmp(from->addr, "dotted3") == 0)
	  linestyle = AtLineDOTTED3;
     else if (strcasecmp(from->addr, "dotted4") == 0)
	  linestyle = AtLineDOTTED4;
     else if (strcasecmp(from->addr, "dotted5") == 0)
	  linestyle = AtLineDOTTED5;
     else if (strcasecmp(from->addr, "dashed3") == 0)
	  linestyle = AtLineDASHED3;
     else if (strcasecmp(from->addr, "dashed4") == 0)
	  linestyle = AtLineDASHED4;
     else if (strcasecmp(from->addr, "dashed5") == 0)
	  linestyle = AtLineDASHED5;
     else if (strcasecmp(from->addr, "dotdashed2") == 0)
	  linestyle = AtLineDOTDASHED2;

     if (linestyle == AtLineINVALID)
	  XtStringConversionWarning(from->addr, XtRPlotLineStyle);
     else {
	  to->addr = (caddr_t) &linestyle;
	  to->size = sizeof(AtPlotLineStyle);
     }
}

void AtRegisterPlotLineStyleConverter()
{
     static Boolean registered = False;

     if (!registered) {
	  XtAddConverter(XtRString, XtRPlotLineStyle,
			 (XtConverter) AtCvtStringToPlotLineStyle, NULL,0);
	  registered = True;
     }
}

static void AtCvtStringToPlotMarkType(args, num_args, from, to)
XrmValue *args;
Cardinal num_args;
XrmValue *from, *to;
{
     static AtPlotMarkType marktype;

     marktype = AtMarkINVALID;

     if (strcasecmp(from->addr, "rectangle") == 0)
	  marktype = AtMarkRECTANGLE;
     else if (strcasecmp(from->addr, "plus") == 0)
	  marktype = AtMarkPLUS;
     else if (strcasecmp(from->addr, "xmark") == 0)
	  marktype = AtMarkXMARK;
     else if (strcasecmp(from->addr, "star") == 0)
	  marktype = AtMarkSTAR;
     else if (strcasecmp(from->addr, "triangle1") == 0)
	  marktype = AtMarkTRIANGLE1;
     else if (strcasecmp(from->addr, "triangle2") == 0)
	  marktype = AtMarkTRIANGLE2;
     else if (strcasecmp(from->addr, "triangle3") == 0)
	  marktype = AtMarkTRIANGLE3;
     else if (strcasecmp(from->addr, "triangle4") == 0)
	  marktype = AtMarkTRIANGLE4;
     else if (strcasecmp(from->addr, "diamond") == 0)
	  marktype = AtMarkDIAMOND;

     if (marktype == AtMarkINVALID)
	  XtStringConversionWarning(from->addr, XtRPlotMarkType);
     else {
	  to->addr = (caddr_t) &marktype;
	  to->size = sizeof(AtPlotMarkType);
     }
}

void AtRegisterPlotMarkTypeConverter()
{
     static Boolean registered = False;

     if (!registered) {
	  XtAddConverter(XtRString, XtRPlotMarkType,
			 (XtConverter) AtCvtStringToPlotMarkType, NULL,0);
	  registered = True;
     }
}
