/*
 *****************************************************
 *
 *  SaVi by Robert Thurman (thurman@geom.umn.edu) and
 *          Patrick Worfolk (worfolk@alum.mit.edu).
 *
 *  Copyright (c) 1997 by The Geometry Center.
 *  This file is part of SaVi.  SaVi is free software;
 *  you can redistribute it and/or modify it only under
 *  the terms given in the file COPYRIGHT which you should
 *  have received along with this file.  SaVi may be
 *  obtained from:
 *  http://savi.sourceforge.net/
 *  http://www.geom.uiuc.edu/locate/SaVi
 *
 *****************************************************
 *
 * tkPlot.c
 *
 * Routines for implementing the new tk plot widget.
 *
 * $Id: tkPlot.c,v 1.5 2004/01/06 03:57:56 lloydwood Exp $
 */
#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "tkPlot.h"
#include "stats_utils.h"


/*
 * Prototypes for procedures defined later in this file:
 */
static int ConfigurePlot _ANSI_ARGS_((Tcl_Interp * interp,
				      TkPlot * plotPtr, int argc, char **argv,
				      int flags));
static void PlotBindProc _ANSI_ARGS_((ClientData clientData,
				      XEvent * eventPtr));
static void PlotCmdDeletedProc _ANSI_ARGS_((ClientData clientData));
static void PlotEventProc _ANSI_ARGS_((ClientData clientData,
				       XEvent * eventPtr));
static int PlotWidgetCmd _ANSI_ARGS_((ClientData clientData,
				      Tcl_Interp * interp, int argc,
				      char **argv));
static int Tk_PlotCmd _ANSI_ARGS_((ClientData clientData,
				   Tcl_Interp * interp, int argc,
				   char **argv));
static void PlotDisplay(ClientData clientData);
static void PlotDestroy(ClientData clientData);

static void colormap_init();

static unsigned short hex_to_int(char *h);

static char *color_names[] = {
  "#ff0000",			/* red */
  "#00ff00",			/* green */
  "#0000ff",			/* blue */
  "#ee82ee",			/* violet */
  "#ffa500",			/* orange */
  "#ffff00",			/* yellow */
  "#a020f0",			/* purple */
  "#ffc0cb",			/* pink */
  "#add8e6",			/* light blue */
  "#ffd700",			/* gold */
  "#00ffff",			/* cyan */
  "#20b257",			/* light sea green */
  "#c0c0c0",			/* grey */
  "#ff00ff",			/* magenta */
/* REMOVE THESE COLORS FOR DSTOOL - worfolk */
  "red",
  "green",
  "blue",
  "violet",
  "orange",
  "yellow",
  "purple",
  "pink",
  "light blue",
  "gold",
  "cyan",
  "light sea green",
  "grey",
  "magenta",
  "#acf",
  "white",
  "black",
  "#ffff00",
  "#ff7f00",
  "#ff3f00",
  "#ff1f00"
};

#define NCOLORS (sizeof(color_names)/sizeof(char *))

static XColor **colormap = NULL;

/*
 * Create the colors array for all plot widgets
 */
static void
colormap_init(Tcl_Interp * interp, TkPlot * plotPtr)
{
  unsigned int i;

  if (!colormap) {
    colormap = (XColor **) ckalloc(sizeof(XColor *) * NCOLORS);
    for (i = 0; i < NCOLORS; i++)
      colormap[i] = Tk_GetColor(interp, plotPtr->tkwin, color_names[i]);
  }
}

int
Tk_PlotColorLookup(const char color[])
{
  unsigned int i;

  for (i = 0; i < NCOLORS; i++) {
    if (strcmp(color, color_names[i]) == 0)
      return i;
  }

  return 0;			/* default color is first color in colormap! */
}

XColor *
Tk_PlotGetColor(unsigned int i)
{
  if (!colormap)
    return (XColor *) NULL;

  i = i % NCOLORS;
  return colormap[i];
}

XColor *
Tk_PlotGetRGB(unsigned int i)
{
  static XColor color;
  char *c;

  i = i % NCOLORS;

  if (!colormap) {
    c = color_names[i] + 2;
    color.red = hex_to_int(c) * USHRT_MAX / 255;
    c += 2;
    color.green = hex_to_int(c) * USHRT_MAX / 255;
    c += 2;
    color.blue = hex_to_int(c) * USHRT_MAX / 255;
    return &color;
  } else {
    return colormap[i];
  }
}

static unsigned short
hex_to_int(char *h)
{
  static char hex[] = "0xff";

  hex[2] = *(h++);
  hex[3] = *h;

  return ((unsigned short) atoi(hex));
}

int
Tk_PlotGetNColors()
{
  return NCOLORS;
}

/*
 * Information used for argv parsing.  */

static Tk_ConfigSpec configSpecs[] = {
  {TK_CONFIG_BORDER, "-background", "background", "Background",
   DEF_PLOT_BG_COLOR, Tk_Offset(TkPlot, bgBorder),
   TK_CONFIG_COLOR_ONLY},
  {TK_CONFIG_BORDER, "-background", "background", "Background",
   DEF_PLOT_BG_MONO, Tk_Offset(TkPlot, bgBorder),
   TK_CONFIG_MONO_ONLY},
  {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
   (char *) NULL, 0, 0},
  {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
   (char *) NULL, 0, 0},
  {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
   DEF_PLOT_BORDER_WIDTH, Tk_Offset(TkPlot, borderWidth), 0},

/*
    {TK_CONFIG_DOUBLE, "-closeenough", "closeEnough", "CloseEnough",
	DEF_PLOT_CLOSE_ENOUGH, Tk_Offset(TkPlot, closeEnough), 0},
*/

  {TK_CONFIG_BOOLEAN, "-confine", "confine", "Confine",
   DEF_PLOT_CONFINE, Tk_Offset(TkPlot, confine), 0},
  {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
   DEF_PLOT_CURSOR, Tk_Offset(TkPlot, cursor), TK_CONFIG_NULL_OK},
  {TK_CONFIG_PIXELS, "-height", "height", "Height",
   DEF_PLOT_HEIGHT, Tk_Offset(TkPlot, height), 0},
  {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
   "HighlightBackground", DEF_PLOT_HIGHLIGHT_BG,
   Tk_Offset(TkPlot, highlightBgColorPtr), 0},
  {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
   DEF_PLOT_HIGHLIGHT, Tk_Offset(TkPlot, highlightColorPtr), 0},
  {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
   "HighlightThickness",
   DEF_PLOT_HIGHLIGHT_WIDTH, Tk_Offset(TkPlot, highlightWidth), 0},

/*
    {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
	DEF_PLOT_INSERT_BG, Tk_Offset(TkPlot, textInfo.insertBorder), 0},
    {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
	DEF_PLOT_INSERT_BD_COLOR,
	Tk_Offset(TkPlot, textInfo.insertBorderWidth), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
	DEF_PLOT_INSERT_BD_MONO,
	Tk_Offset(TkPlot, textInfo.insertBorderWidth), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
	DEF_PLOT_INSERT_OFF_TIME, Tk_Offset(TkPlot, insertOffTime), 0},
    {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
	DEF_PLOT_INSERT_ON_TIME, Tk_Offset(TkPlot, insertOnTime), 0},
    {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
	DEF_PLOT_INSERT_WIDTH, Tk_Offset(TkPlot, textInfo.insertWidth), 0},
*/

  {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
   DEF_PLOT_RELIEF, Tk_Offset(TkPlot,relief), 0},

/*
    {TK_CONFIG_STRING, "-scrollregion", "scrollRegion", "ScrollRegion",
	DEF_PLOT_SCROLL_REGION, Tk_Offset(TkPlot, regionString),
	TK_CONFIG_NULL_OK},
    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
	DEF_PLOT_SELECT_COLOR, Tk_Offset(TkPlot, textInfo.selBorder),
	TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
	DEF_PLOT_SELECT_MONO, Tk_Offset(TkPlot, textInfo.selBorder),
	TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
	DEF_PLOT_SELECT_BD_COLOR,
	Tk_Offset(TkPlot, textInfo.selBorderWidth), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
	DEF_PLOT_SELECT_BD_MONO, Tk_Offset(TkPlot, textInfo.selBorderWidth),
	TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
	DEF_PLOT_SELECT_FG_COLOR, Tk_Offset(TkPlot, textInfo.selFgColorPtr),
	TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
	DEF_PLOT_SELECT_FG_MONO, Tk_Offset(TkPlot, textInfo.selFgColorPtr),
	TK_CONFIG_MONO_ONLY},
*/

  {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
   DEF_PLOT_TAKE_FOCUS, Tk_Offset(TkPlot, takeFocus), TK_CONFIG_NULL_OK},
  {TK_CONFIG_PIXELS, "-width", "width", "Width",
   DEF_PLOT_WIDTH, Tk_Offset(TkPlot, width), 0},

/*
    {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
	DEF_PLOT_X_SCROLL_CMD, Tk_Offset(TkPlot, xScrollCmd),
	TK_CONFIG_NULL_OK},
    {TK_CONFIG_PIXELS, "-xscrollincrement", "xScrollIncrement",
	"ScrollIncrement",
	DEF_PLOT_X_SCROLL_INCREMENT, Tk_Offset(TkPlot, xScrollIncrement),
	0},
    {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
	DEF_PLOT_Y_SCROLL_CMD, Tk_Offset(TkPlot, yScrollCmd),
	TK_CONFIG_NULL_OK},
    {TK_CONFIG_PIXELS, "-yscrollincrement", "yScrollIncrement",
	"ScrollIncrement",
	DEF_PLOT_Y_SCROLL_INCREMENT, Tk_Offset(TkPlot, yScrollIncrement),
	0},
*/

  {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
   (char *) NULL, 0, 0}
};


/*
 * Plot_Init
 *
 * Creates the plot command.
 * Should be called from TclAppInit()
 *
 */
int
Plot_Init(Tcl_Interp * interpreter)
{
  static char plot_widget_name[] = "plot";

  Tcl_CreateCommand(interpreter, plot_widget_name, Tk_PlotCmd,
		    (ClientData) Tk_MainWindow(interpreter),
		    (Tcl_CmdDeleteProc *) NULL);

  return TCL_OK;
}


/*
 * Tk_PlotCmd
 *
 * The plot class command procedure.  Invoked to process the "plot"
 * Tcl command.
 *
 */
static int
Tk_PlotCmd(clientData, interp, argc, argv)
     ClientData clientData;	/* Main window associated with
				 * interpreter. */
     Tcl_Interp *interp;	/* Current interpreter. */
     int argc;			/* Number of arguments. */
     char **argv;		/* Argument strings. */
{
  static char Plot_class[] = "Plot";

  Tk_Window tkwin = (Tk_Window) clientData;
  TkPlot *plotPtr;
  Tk_Window new;

  if (argc < 2) {
    Tcl_AppendResult(interp, "wrong # args:  should be \"",
		     argv[0], " pathName ?options?\"", (char *) NULL);
    return TCL_ERROR;
  }

  new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  if (!new) {
    return TCL_ERROR;
  }

  plotPtr = (TkPlot *) ckalloc(sizeof(TkPlot));
  plotPtr->tkwin = new;
  plotPtr->display = Tk_Display(new);
  plotPtr->interp = interp;
  plotPtr->widgetCmd = Tcl_CreateCommand(interp,
					 Tk_PathName(plotPtr->tkwin),
					 PlotWidgetCmd,
					 (ClientData) plotPtr,
					 PlotCmdDeletedProc);

  plotPtr->borderWidth = 0;
  plotPtr->bgBorder = NULL;
  plotPtr->relief = TK_RELIEF_FLAT;
  plotPtr->highlightWidth = 0;
  plotPtr->highlightBgColorPtr = NULL;
  plotPtr->highlightColorPtr = NULL;
  plotPtr->inset = 0;

  plotPtr->pixmapGC = None;
  plotPtr->plotGC = None;
  plotPtr->width = None;
  plotPtr->height = None;
  plotPtr->confine = 0;
  plotPtr->pixmap = None;

/*
     plotPtr->firstItemPtr = NULL;
     plotPtr->lastItemPtr = NULL;

     plotPtr->textInfo.selBorder = NULL;
     plotPtr->textInfo.selBorderWidth = 0;
     plotPtr->textInfo.selFgColorPtr = NULL;
     plotPtr->textInfo.selItemPtr = NULL;
     plotPtr->textInfo.selectFirst = -1;
     plotPtr->textInfo.selectLast = -1;
     plotPtr->textInfo.anchorItemPtr = NULL;
     plotPtr->textInfo.selectAnchor = 0;
     plotPtr->textInfo.insertBorder = NULL;
     plotPtr->textInfo.insertWidth = 0;
     plotPtr->textInfo.insertBorderWidth = 0;
     plotPtr->textInfo.focusItemPtr = NULL;
     plotPtr->textInfo.gotFocus = 0;
     plotPtr->textInfo.cursorOn = 0;

     plotPtr->insertOnTime = 0;
     plotPtr->insertOffTime = 0;
     plotPtr->insertBlinkHandler = (Tk_TimerToken) NULL;
*/

  plotPtr->xOrigin = plotPtr->yOrigin = 0;
  plotPtr->drawableXOrigin = plotPtr->drawableYOrigin = 0;

/*
    plotPtr->bindingTable = NULL;
    plotPtr->currentItemPtr = NULL;
    plotPtr->closeEnough = 0.0;
    plotPtr->pickEvent.type = LeaveNotify;
    plotPtr->pickEvent.xcrossing.x = 0;
    plotPtr->pickEvent.xcrossing.y = 0;
    plotPtr->state = 0;
    plotPtr->xScrollCmd = NULL;
    plotPtr->yScrollCmd = NULL;
    plotPtr->scrollX1 = 0;
    plotPtr->scrollY1 = 0;
    plotPtr->scrollX2 = 0;
    plotPtr->scrollY2 = 0;
    plotPtr->regionString = NULL;
    plotPtr->xScrollIncrement = 0;
    plotPtr->yScrollIncrement = 0;
*/
/*
    plotPtr->scanX = 0;
    plotPtr->scanXOrigin = 0;
    plotPtr->scanY = 0;
    plotPtr->scanYOrigin = 0;
    plotPtr->hotPtr = NULL;
    plotPtr->hotPrevPtr = NULL;
*/
  plotPtr->cursor = None;
  plotPtr->takeFocus = NULL;
  plotPtr->pixelsPerMM = WidthOfScreen(Tk_Screen(new));
  plotPtr->pixelsPerMM /= WidthMMOfScreen(Tk_Screen(new));
  plotPtr->flags = 0;
  plotPtr->nextId = 1;
  /*
     plotPtr->psInfoPtr = NULL;
   */

  /* initialize colormap */
  colormap_init(interp, plotPtr);

  Tk_SetClass(plotPtr->tkwin, Plot_class);
  Tk_CreateEventHandler(plotPtr->tkwin,
/*			ExposureMask|StructureNotifyMask|FocusChangeMask, */
			ExposureMask | StructureNotifyMask,
			PlotEventProc, (ClientData) plotPtr);
  Tk_CreateEventHandler(plotPtr->tkwin, KeyPressMask | KeyReleaseMask
			| ButtonPressMask | ButtonReleaseMask |
			EnterWindowMask | LeaveWindowMask | PointerMotionMask,
			PlotBindProc, (ClientData) plotPtr);

/*  Tk_CreateSelHandler(plotPtr->tkwin, XA_PRIMARY, XA_STRING,
		      PlotFetchSelection, (ClientData) plotPtr, XA_STRING);
*/
  if (ConfigurePlot(interp, plotPtr, argc - 2, argv + 2, 0) != TCL_OK) {
    Tk_DestroyWindow(plotPtr->tkwin);
    return TCL_ERROR;
  }

  interp->result = Tk_PathName(plotPtr->tkwin);
  return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * ConfigurePlot --
 *
 *	This procedure is called to process an argv/argc list, plus
 *	the Tk option database, in order to configure (or
 *	reconfigure) a plot widget.
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then interp->result contains an error message.
 *
 * Side effects:
 *	Configuration information, such as colors, border width,
 *	etc. get set for plotPtr;  old resources get freed,
 *	if there were any.
 *
 *----------------------------------------------------------------------
 */

static int
ConfigurePlot(interp, plotPtr, argc, argv, flags)
     Tcl_Interp *interp;	/* Used for error reporting. */
     TkPlot *plotPtr;		/* Information about widget;  may or may
				   * not already have values for some fields. */
     int argc;			/* Number of valid entries in argv. */
     char **argv;		/* Arguments. */
     int flags;			/* Flags to pass to Tk_ConfigureWidget. */
{
  XGCValues gcValues;
  GC new;
  Tk_Window tkwin = plotPtr->tkwin;

  if (Tk_ConfigureWidget(interp, tkwin, configSpecs,
			 argc, argv, (char *) plotPtr, flags) != TCL_OK) {
    return TCL_ERROR;
  }

  /*
   * A few options need special processing, such as setting the
   * background from a 3-D border and creating a GC for copying
   * bits to the screen.
   */

  Tk_SetBackgroundFromBorder(tkwin, plotPtr->bgBorder);

  if (plotPtr->highlightWidth < 0) {
    plotPtr->highlightWidth = 0;
  }
  plotPtr->inset = plotPtr->borderWidth + plotPtr->highlightWidth;

  gcValues.function = GXcopy;
  gcValues.foreground = Tk_3DBorderColor(plotPtr->bgBorder)->pixel;
  gcValues.graphics_exposures = False;
  new = Tk_GetGC(tkwin,
		 GCFunction | GCForeground | GCGraphicsExposures, &gcValues);
  if (plotPtr->pixmapGC != None) {
    Tk_FreeGC(plotPtr->display, plotPtr->pixmapGC);
  }
  plotPtr->pixmapGC = new;

  gcValues.foreground = colormap[0]->pixel;
  new = XCreateGC(Tk_Display(tkwin),
		  RootWindow(Tk_Display(tkwin), 0), GCForeground, &gcValues);
  if (plotPtr->plotGC != None) {
    XFreeGC(Tk_Display(plotPtr->tkwin), plotPtr->plotGC);
  }
  plotPtr->plotGC = new;

  /*
   * Reset the desired dimensions for the window.
   */

  Tk_GeometryRequest(plotPtr->tkwin, plotPtr->width + 2 * plotPtr->inset,
		     plotPtr->height + 2 * plotPtr->inset);

  return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * PlotCmdDeletedProc --
 *
 *	This procedure is invoked when a widget command is deleted.  If
 *	the widget isn't already in the process of being destroyed,
 *	this command destroys it.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The widget is destroyed.
 *
 *----------------------------------------------------------------------
 */

static void
PlotCmdDeletedProc(clientData)
     ClientData clientData;	/* Pointer to widget record for widget. */
{
  TkPlot *plotPtr = (TkPlot *) clientData;
  Tk_Window tkwin = plotPtr->tkwin;

  /*
   * This procedure could be invoked either because the window was
   * destroyed and the command was then deleted (in which case tkwin
   * is NULL) or because the command was deleted, and then this procedure
   * destroys the widget.
   */


  /* NOT SURE IF THIS IS RIGHT - worfolk */

  if (tkwin) {
    plotPtr->tkwin = NULL;
    Tk_DestroyWindow(tkwin);
  }
}


/*
 *--------------------------------------------------------------
 *
 * PlotWidgetCmd --
 *
 *	This procedure is invoked to process the Tcl command
 *	that corresponds to a widget managed by this module.
 *	See the user documentation for details on what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */

static int
PlotWidgetCmd(clientData, interp, argc, argv)
     ClientData clientData;	/* Information about plot
				 * widget. */
     Tcl_Interp *interp;	/* Current interpreter. */
     int argc;			/* Number of arguments. */
     char **argv;		/* Argument strings. */
{
  TkPlot *plotPtr = (TkPlot *) clientData;
/*   size_t length; */
  int result = TCL_OK;
/*  Tk_Item *itemPtr = NULL;	*//* Initialization needed only to
 * prevent compiler warning. */

  static char def_plot_color[] = "red";	/* Default or arg plot settings */
  static char *plot_color = def_plot_color;
  static int plot_symbol = POINT;
  static unsigned int plot_size = MEDIUM;
  int plot_x;
  int plot_y;
  int plot_width;
  int plot_height;
  int c;


  if (argc < 2) {
    Tcl_AppendResult(interp, "wrong # args: should be \"",
		     argv[0], " option ?arg arg ...?\"", (char *) NULL);
    return TCL_ERROR;
  }

  Tk_Preserve((ClientData) plotPtr);

  /*
   * Process the configure command
   */
  if (strcmp(argv[1], "configure") == 0) {
    if (argc == 2) {
      result = Tk_ConfigureInfo(interp, plotPtr->tkwin, configSpecs,
				(char *) plotPtr, (char *) NULL, 0);
    } else if (argc == 3) {
      result = Tk_ConfigureInfo(interp, plotPtr->tkwin, configSpecs,
				(char *) plotPtr, argv[2], 0);
    } else {
      result = ConfigurePlot(interp, plotPtr, argc - 2, argv + 2,
			     TK_CONFIG_ARGV_ONLY);
    }
  }

  /*
   * Process the plot command
   */
  else if (strcmp(argv[1], "plot") == 0) {
    if (argc < 4) {
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], "plot x y -color [color] -symbol [symbol]\"",
		       (char *) NULL);
      return TCL_ERROR;
    }

    /*
     * fprintf(stderr,"plot  args: ");
     * for (i=0; i<argc; i++)
     *   fprintf(stderr,"%s ", argv[i]);
     * fprintf(stderr,"\n");
     */

    plot_x = atoi(argv[2]);
    plot_y = atoi(argv[3]);

    c = 4;

    while ((c < argc) && (c + 1 < argc)) {

      if (strcmp(argv[c], "-color") == 0) {
	plot_color = argv[c + 1];
	c++;
      }

      else if (strcmp(argv[c], "-symbol") == 0) {

	if (strcmp(argv[c + 1], "point") == 0)
	  plot_symbol = POINT;
	else if (strcmp(argv[c + 1], "cross") == 0)
	  plot_symbol = CROSS;
	else if (strcmp(argv[c + 1], "box") == 0)
	  plot_symbol = BOX;
	else if (strcmp(argv[c + 1], "triangle") == 0)
	  plot_symbol = TRIANGLE;
	else if (strcmp(argv[c + 1], "crosshair") == 0)
	  plot_symbol = CROSSHAIR;
	else if (strcmp(argv[c + 1], "circle") == 0)
	  plot_symbol = CIRCLE;
	else if (strcmp(argv[c + 1], "disk") == 0)
	  plot_symbol = DISK;
	else if (strcmp(argv[c + 1], "test") == 0)
	  plot_symbol = TEST_SYMBOL;
	c++;
      } else if (strcmp(argv[c], "-size") == 0) {

	if (strcmp(argv[c + 1], "small") == 0)
	  plot_size = SMALL;
	else if (strcmp(argv[c + 1], "medium") == 0)
	  plot_size = MEDIUM;
	else if (strcmp(argv[c + 1], "large") == 0)
	  plot_size = LARGE;
	else
	  plot_size = atoi(argv[c + 1]);
	c++;
      }

      else {
	c++;
      }
    }

    Tk_PlotSymbol(plotPtr,
		  plot_x,
		  plot_y,
		  Tk_PlotColorLookup(plot_color), plot_symbol, plot_size);
  }

  /*
   * Process the rubber band box command
   */
  else if (strcmp(argv[1], "rbox") == 0) {
    plot_x = atoi(argv[2]);
    plot_y = atoi(argv[3]);

    plot_width = atoi(argv[4]);
    plot_height = atoi(argv[5]);

    Tk_PlotRubberBox(plotPtr, plot_x, plot_y, plot_width, plot_height);
  }

  /*
   * Process the clear command
   */
  else if (strcmp(argv[1], "clear") == 0) {
    Tk_PlotClear(plotPtr);
  }

  /*
   * Invalid option message
   */
  else {
    Tcl_AppendResult(interp, "invalid option \"", argv[1],
		     "\":  must be clear, configure, or plot", (char *) NULL);
    goto msg_error;
  }
  Tk_Release((ClientData) plotPtr);
  return result;

msg_error:
  Tk_Release((ClientData) plotPtr);
  return TCL_ERROR;
}


/*
 *--------------------------------------------------------------
 *
 * PlotEventProc --
 *
 *	This procedure is invoked by the Tk dispatcher for various
 *	events on plotes.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	When the window gets deleted, internal structures get
 *	cleaned up.  When it gets exposed, it is redisplayed.
 *
 *--------------------------------------------------------------
 */

static void
PlotEventProc(clientData, eventPtr)
     ClientData clientData;	/* Information about window. */
     XEvent *eventPtr;		/* Information about event. */
{
  TkPlot *plotPtr = (TkPlot *) clientData;

  if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
    goto redraw;
  } else if (eventPtr->type == DestroyNotify) {
    Tcl_DeleteCommand(plotPtr->interp, Tk_PathName(plotPtr->tkwin));
    plotPtr->tkwin = NULL;
    if (plotPtr->flags & REDRAW_PENDING) {
      Tk_CancelIdleCall(PlotDisplay, (ClientData) plotPtr);
      plotPtr->flags &= ~REDRAW_PENDING;
    }
    /* Tk_EventuallyFree((ClientData) plotPtr, PlotDestroy); */
    /* hack to remove compiler warnings; should be upgraded */

#if ( (TK_MAJOR_VERSION > 4) || ( (TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION > 0)) )
    Tcl_EventuallyFree((ClientData) plotPtr, (Tcl_FreeProc *) PlotDestroy);
#else
    Tk_EventuallyFree((ClientData) plotPtr, PlotDestroy);
#endif
  }
  return;

redraw:
  if (plotPtr->tkwin && !(plotPtr->flags & REDRAW_PENDING)) {
    Tk_DoWhenIdle(PlotDisplay, (ClientData) plotPtr);
    plotPtr->flags |= REDRAW_PENDING;
  }
}



/*
 *--------------------------------------------------------------
 *
 * PlotBindProc --
 *
 *	This procedure is invoked by the Tk dispatcher to handle
 *	events associated with bindings on items.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Depends on the command invoked as part of the binding
 *	(if there was any).
 *
 *--------------------------------------------------------------
 */

static void
PlotBindProc(clientData, eventPtr)
     ClientData clientData;	/* Pointer to plot structure. */
     XEvent *eventPtr;		/* Pointer to X event that just
				 * happened. */
{
/*
   XPoint bbox[2];

  switch( eventPtr->type ) {
    case ButtonPress:
        bbox[0].x = eventPtr->xbutton.x;
        bbox[0].y = eventPtr->xbutton.y;
        fprintf( stderr, "\nPlotBindProc: ButtonPress [%d,%d]",
                 bbox[0].x, bbox[0].y );
	break;
    case ButtonRelease:
        bbox[0].x = eventPtr->xbutton.x;
        bbox[0].y = eventPtr->xbutton.y;
        fprintf( stderr, "\nPlotBindProc: ButtonRelease [%d,%d]",
                 bbox[0].x, bbox[0].y );
	break;
    case MotionNotify:
        bbox[0].x = eventPtr->xbutton.x;
        bbox[0].y = eventPtr->xbutton.y;
	break;
    default:
	break;
  }
*/
}



static void
PlotDisplay(ClientData clientData)
{
  TkPlot *plotPtr = (TkPlot *) clientData;
  Tk_Window tkwin = plotPtr->tkwin;

  if (!tkwin)
    return;
  if (!Tk_IsMapped(tkwin))
    return;

  /* make sure the plot widget still exists and is mapped */
  plotPtr->flags &= ~(REDRAW_PENDING);

  /* copy the pixmap to the screen */
  if (plotPtr->pixmap != None) {
    XCopyArea(plotPtr->display, plotPtr->pixmap, Tk_WindowId(tkwin),
	      plotPtr->pixmapGC, 0, 0,
	      Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  }

}



static void
PlotDestroy(ClientData clientData)
{
  register TkPlot *plotPtr = (TkPlot *) clientData;

  if (plotPtr->pixmapGC != None) {
    Tk_FreeGC(plotPtr->display, plotPtr->pixmapGC);
  }

  if (plotPtr->plotGC != None) {
    XFreeGC(plotPtr->display, plotPtr->plotGC);
  }

  if (plotPtr->pixmap != None) {
    Tk_FreePixmap(plotPtr->display, plotPtr->pixmap);
  }

  if (plotPtr->flags & REDRAW_PENDING) {
    Tk_CancelIdleCall(PlotDisplay, (ClientData) plotPtr);
  }

  Tk_FreeOptions(configSpecs, (char *) plotPtr, plotPtr->display, 0);
  ckfree((char *) plotPtr);

}

void
Tk_PlotClear(TkPlot * plotPtr)
{
  Tk_Window tkwin = plotPtr->tkwin;

  /* check if mapped! */
  if (!tkwin)
    return;
  if (!Tk_IsMapped(tkwin))
    return;

  /* build pixmap if necessary */
  if (plotPtr->pixmap == None) {
    build_pixmap(plotPtr);
  } else {
    /* otherwise clear the pixmap */
    XSetForeground(Tk_Display(tkwin), plotPtr->plotGC,
		   Tk_3DBorderColor(plotPtr->bgBorder)->pixel);
    XFillRectangle(Tk_Display(tkwin), plotPtr->pixmap, plotPtr->plotGC,
		   0, 0, Tk_Width(tkwin), Tk_Height(tkwin));
  }

  /* set up a redisplay event */
  if (plotPtr->tkwin && !(plotPtr->flags & REDRAW_PENDING)) {
    Tk_DoWhenIdle(PlotDisplay, (ClientData) plotPtr);
    plotPtr->flags |= REDRAW_PENDING;
  }

}

void
Tk_PlotQueueRefresh(TkPlot * plotPtr)
{
  /* set up a redisplay event */
  if (plotPtr->tkwin && !(plotPtr->flags & REDRAW_PENDING)) {
    Tk_DoWhenIdle(PlotDisplay, (ClientData) plotPtr);
    plotPtr->flags |= REDRAW_PENDING;
  }
}

void
Tk_PlotRubberBox(plotPtr, x, y, w, h)
     TkPlot *plotPtr;
     int x;
     int y;
     int w;
     int h;
{
  Tk_Window tkwin = plotPtr->tkwin;

  /* check if mapped! */
  if (!tkwin)
    return;
  if (!Tk_IsMapped(tkwin))
    return;

  /* build pixmap if necessary */
  if (plotPtr->pixmap == None)
    build_pixmap(plotPtr);

  /*
   * set the "best" foreground color for the rubber box
   * set the xor function to "erase" the box when it's drawn twice
   * draw the rectangle
   */
  XSetForeground(Tk_Display(tkwin), plotPtr->plotGC, colormap[3]->pixel);
  XSetFunction(Tk_Display(tkwin), plotPtr->plotGC, GXxor);

  XDrawRectangle(Tk_Display(tkwin),
		 Tk_WindowId(tkwin), plotPtr->plotGC, x, y, w, h);
  XDrawRectangle(Tk_Display(tkwin),
		 plotPtr->pixmap, plotPtr->plotGC, x, y, w, h);

  /* reset the GC to copy drawing */
  XSetFunction(Tk_Display(tkwin), plotPtr->plotGC, GXcopy);
}


void
build_pixmap(TkPlot * plotPtr)
{
  Tk_Window tkwin = plotPtr->tkwin;

  /* check if mapped! */
  if (!tkwin)
    return;
  if (!Tk_IsMapped(tkwin))
    return;

  /* if one is already built, destroy and rebuild */
  if (plotPtr->pixmap != None) {
    Tk_FreePixmap(plotPtr->display, plotPtr->pixmap);
  }
  plotPtr->pixmap = Tk_GetPixmap(plotPtr->display, Tk_WindowId(tkwin),
				 Tk_Width(tkwin), Tk_Height(tkwin),
				 Tk_Depth(tkwin));
  XSetForeground(Tk_Display(tkwin), plotPtr->plotGC,
		 Tk_3DBorderColor(plotPtr->bgBorder)->pixel);
  XFillRectangle(Tk_Display(tkwin), plotPtr->pixmap, plotPtr->plotGC,
		 0, 0, Tk_Width(tkwin), Tk_Height(tkwin));
}

void
Tk_PlotString(plotPtr, x, y, color, pstr, psize)
     TkPlot *plotPtr;
     int x;
     int y;
     int color;
     char *pstr;
     unsigned int psize;
{
  Tk_Window tkwin = plotPtr->tkwin;
  int s, len = strlen(pstr);

  /* check if mapped! */
  if (!tkwin)
    return;
  if (!Tk_IsMapped(tkwin))
    return;

  /* build pixmap if necessary */
  if (plotPtr->pixmap == None)
    build_pixmap(plotPtr);

  /* make sure it is in valid range! */
  color = color % NCOLORS;

  XSetForeground(Tk_Display(tkwin), plotPtr->plotGC, colormap[color]->pixel);

  psize++;
  s = psize / 2;
  XDrawString(Tk_Display(tkwin),
	      Tk_WindowId(tkwin), plotPtr->plotGC, x + s, y + s, pstr, len);
  XDrawString(Tk_Display(tkwin),
	      plotPtr->pixmap, plotPtr->plotGC, x + s, y + s, pstr, len);
}

void
Tk_PlotSymbol(plotPtr, x, y, color, psymbol, psize)
     TkPlot *plotPtr;
     int x;
     int y;
     int color;
     int psymbol;
     unsigned int psize;
{
  Tk_Window tkwin = plotPtr->tkwin;
  int i, u, v, s;
  XPoint tri[4];

  /* check if mapped! */
  if (!tkwin)
    return;
  if (!Tk_IsMapped(tkwin))
    return;


  /* build pixmap if necessary */
  if (plotPtr->pixmap == None)
    build_pixmap(plotPtr);

  /* make sure it is in valid range! */
  color = color % NCOLORS;

  XSetForeground(Tk_Display(tkwin), plotPtr->plotGC, colormap[color]->pixel);


  /*
   * plot the specified symbol
   */
  switch (psymbol) {

  case POINT:
    psize++;
    s = psize / 2;
    XFillRectangle(Tk_Display(tkwin),
		   Tk_WindowId(tkwin),
		   plotPtr->plotGC, x - s, y - s, psize, psize);
    XFillRectangle(Tk_Display(tkwin),
		   plotPtr->pixmap,
		   plotPtr->plotGC, x - s, y - s, psize, psize);
    break;

  case CROSS:
    s = psize + 1;
    psize = 2 * s;
    XFillRectangle(Tk_Display(tkwin),
		   Tk_WindowId(tkwin),
		   plotPtr->plotGC, x - s, y - 3 * s, psize, 3 * psize);
    XFillRectangle(Tk_Display(tkwin),
		   Tk_WindowId(tkwin),
		   plotPtr->plotGC, x - 3 * s, y - s, 3 * psize, psize);
    XFillRectangle(Tk_Display(tkwin),
		   plotPtr->pixmap,
		   plotPtr->plotGC, x - s, y - 3 * s, psize, 3 * psize);
    XFillRectangle(Tk_Display(tkwin),
		   plotPtr->pixmap,
		   plotPtr->plotGC, x - 3 * s, y - s, 3 * psize, psize);
    break;

  case BOX:
    s = psize + 2;
    XDrawRectangle(Tk_Display(tkwin),
		   Tk_WindowId(tkwin),
		   plotPtr->plotGC, x - s, y - s, 2 * s, 2 * s);
    XDrawRectangle(Tk_Display(tkwin),
		   plotPtr->pixmap,
		   plotPtr->plotGC, x - s, y - s, 2 * s, 2 * s);
    break;

  case TRIANGLE:
    s = psize + 2;
    tri[0].x = x;
    tri[0].y = y - s;
    tri[1].x = x - s;
    tri[1].y = y + s;
    tri[2].x = x + s;
    tri[2].y = y + s;
    tri[3].x = x;
    tri[3].y = y - s;

    XDrawLines(Tk_Display(tkwin),
	       Tk_WindowId(tkwin), plotPtr->plotGC, tri, 4, CoordModeOrigin);
    XDrawLines(Tk_Display(tkwin),
	       plotPtr->pixmap, plotPtr->plotGC, tri, 4, CoordModeOrigin);
    break;

  case CROSSHAIR:
    /* draw on window */
/*      XDrawLine(Tk_Display(tkwin),
		Tk_WindowId(tkwin),
		plotPtr->plotGC,
		x-psize,y,x+psize,y);
      XDrawLine(Tk_Display(tkwin),
		Tk_WindowId(tkwin),
		plotPtr->plotGC,
		x,y-psize,x,y+psize); */
    /* and on pixmap */
    XDrawLine(Tk_Display(tkwin),
	      plotPtr->pixmap, plotPtr->plotGC, x - psize, y, x + psize, y);
    XDrawLine(Tk_Display(tkwin),
	      plotPtr->pixmap, plotPtr->plotGC, x, y - psize, x, y + psize);
    break;

  case CIRCLE:
    s = psize * 2;
    /* draw on window */
/*      XDrawArc(Tk_Display(tkwin),
	       Tk_WindowId(tkwin),
	       plotPtr->plotGC,
	       x-psize,y-psize, s, s, 0, 360*64); */
    /* and on pixmap */
    XDrawArc(Tk_Display(tkwin),
	     plotPtr->pixmap,
	     plotPtr->plotGC, x - psize, y - psize, s, s, 0, 360 * 64);
    break;

  case DISK:
    s = psize * 2;
    /* draw on window */
/*      XFillArc(Tk_Display(tkwin),
	       Tk_WindowId(tkwin),
	       plotPtr->plotGC,
	       x-psize,y-psize, s, s, 0, 360*64); */
    /* and on pixmap */
    XFillArc(Tk_Display(tkwin),
	     plotPtr->pixmap,
	     plotPtr->plotGC, x - psize, y - psize, s, s, 0, 360 * 64);
    break;

  case TEST_SYMBOL:
    for (i = 0; i < 5000; i = i + 1) {
      u = (int) (i * sin(i / 10.0) / 50.0) + x;
      v = (int) (i * cos(i / 10.0) / 50.0) + y;

      XDrawPoint(Tk_Display(tkwin),
		 Tk_WindowId(tkwin), plotPtr->plotGC, u, v);
      XDrawPoint(Tk_Display(tkwin), plotPtr->pixmap, plotPtr->plotGC, u, v);
    }
    break;

  default:
    break;
  }

}


void
Tk_PlotGrid(TkPlot * plotPtr, grid * g, int *color, int max_color)
{
  unsigned int i, j;
  int ci;
  int *data = g->data;

  Tk_Window tkwin = plotPtr->tkwin;

  /* check if mapped! */
  if (!tkwin)
    return;
  if (!Tk_IsMapped(tkwin))
    return;

  /* build pixmap if necessary */
  if (plotPtr->pixmap == None)
    build_pixmap(plotPtr);


  for (i = 0; i < g->height; i++) {
    for (j = 0; j < g->width; j++) {
      ci = *(data++);
      if (ci > max_color)
	ci = max_color;
      XSetForeground(Tk_Display(tkwin),
		     plotPtr->plotGC, colormap[color[*(data++)]]->pixel);
      XDrawPoint(Tk_Display(tkwin), plotPtr->pixmap, plotPtr->plotGC, j, i);
    }
  }
}

void
Tk_PlotGridForeground(TkPlot * plotPtr, grid * g, unsigned char *foreground,
		      int *color, int foreground_color)
{
  unsigned int i, j;
  int ci;
  int *data = g->data;

  Tk_Window tkwin = plotPtr->tkwin;

  /* check if mapped! */
  if (!tkwin)
    return;
  if (!Tk_IsMapped(tkwin))
    return;

  /* build pixmap if necessary */
  if (plotPtr->pixmap == None)
    build_pixmap(plotPtr);


  for (i = 0; i < g->height; i++) {
    for (j = 0; j < g->width; j++) {
      if (*(foreground++) > 0) {
	XSetForeground(Tk_Display(tkwin),
		       plotPtr->plotGC,
		       colormap[color[foreground_color]]->pixel);
      } else {
	ci = *data;
	if (ci >= foreground_color)
	  ci = foreground_color - 1;
	XSetForeground(Tk_Display(tkwin),
		       plotPtr->plotGC, colormap[color[ci]]->pixel);
      }
      data++;
      XDrawPoint(Tk_Display(tkwin), plotPtr->pixmap, plotPtr->plotGC, j, i);
    }
  }
}
