gt:         int ylabwidth[] = new int[ny];
context: /* MOD_V2.0
  * Copyright (c) 2012 OpenDA Association
  * All rights reserved.
  * 
  * This file is part of OpenDA. 
  * 
  * OpenDA is free software: you can redistribute it and/or modify 
  * it under the terms of the GNU Lesser General Public License as 
  * published by the Free Software Foundation, either version 3 of 
  * the License, or (at your option) any later version. 
  * 
  * OpenDA is distributed in the hope that it will be useful, 
  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  * GNU Lesser General Public License for more details. 
  * 
  * You should have received a copy of the GNU Lesser General Public License
  * along with OpenDA.  If not, see <http://www.gnu.org/licenses/>.
  */
  package org.openda.uncertaintygui.genericplot;
  import java.awt.Color;
  import java.awt.Component;
  import java.awt.Dimension;
  import java.awt.EventQueue;
  import java.awt.Graphics;
  import java.awt.Graphics2D;
  import java.awt.FlowLayout;
  import java.awt.Font;
  import java.awt.FontMetrics;
  import java.awt.Rectangle;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.InputEvent;
  import java.awt.event.KeyEvent;
  import java.awt.event.KeyListener;
  import java.awt.event.MouseEvent;
  import java.awt.event.MouseListener;
  import java.awt.event.MouseMotionListener;
  import java.awt.image.BufferedImage;
  import java.awt.print.PageFormat;
  import java.awt.print.Printable;
  import java.awt.print.PrinterException;
  import java.awt.print.PrinterJob;
  import java.awt.RenderingHints;
  import java.io.BufferedReader;
  import java.io.BufferedWriter;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.OutputStream;
  import java.io.OutputStreamWriter;
  import java.io.PrintWriter;
  import java.io.Writer;
  import java.net.URL;
  import java.util.Enumeration;
  import java.util.Vector;
  import javax.swing.ImageIcon;
  import javax.swing.JButton;
  import javax.swing.JOptionPane;
  import javax.swing.JPanel;
  import javax.swing.SwingUtilities;
  
  /**
  This class provides a labeled box within which to place a data plot.
  A title, X and Y axis labels, tick marks, and a legend are all supported.
  Zooming in and out is supported.  To zoom in, drag the mouse
  downwards to draw a box.  To zoom out, drag the mouse upward.
  <p>
  The box can be configured either through a file with commands or
  through direct invocation of the public methods of the class.
  <p>
  When calling the methods, in most cases the changes will not
  be visible until paintComponent() has been called.  To request that this
  be done, call repaint().
  <p>
  A small set of key bindings are provided for convenience.
  They are:
  <ul>
  <li> Cntrl-c: copy plot to clipboard (EPS format), if permitted.
  <li> D: Dump the plot to standard output (in PlotML).
  <li> E: Export the plot to standard output in EPS format.
  <li> F: Fill the plot.
  <li> H or ?: Display a simple help message.
  <li> Cntrl-D or Q: quit
  </ul>
  These commands are provided in a menu by the PlotFrame class.
  Note that exporting to the clipboard is not allowed in applets
  (it used to be), so this will result in an error message.
  <p>
  At this time, the two export commands produce encapsulated postscript
  tuned for black-and-white printers.  In the future, more formats may
  supported.
  Exporting to the clipboard and to standard output, in theory,
  is allowed for applets, unlike writing to a file. Thus, these
  key bindings provide a simple mechanism to obtain a high-resolution
  image of the plot from an applet, suitable for incorporation in
  a document. However, in some browsers, exporting to standard out
  triggers a security violation.  You can use the JDK appletviewer instead.
  <p>
  To read commands from a file or URL, the preferred technique is
  to use one of the classes in the plotml package.  That package
  supports both PlotML, an XML extension for plots, and a historical
  file format specific to ptplot.  The historical file format is
  understood by the read() method in this class.
  The syntax of the historical format, documented below, is rudimentary,
  and will probably not be extended as ptplot evolves.  Nonetheless,
  we document it here since it is directly supported by this class.
  <p>
  The historical format for the file allows any number
  commands, one per line.  Unrecognized commands and commands with
  syntax errors are ignored.  Comments are denoted by a line starting
  with a pound sign "#".  The recognized commands include:
  <pre>
  TitleText: <i>string</i>
  XLabel: <i>string</i>
  YLabel: <i>string</i>
  </pre>
  These commands provide a title and labels for the X (horizontal) and Y
  (vertical) axes.
  A <i>string</i> is simply a sequence of characters, possibly
  including spaces.  There is no need here to surround them with
  quotation marks, and in fact, if you do, the quotation marks will
  be included in the labels.
  <p>
  The ranges of the X and Y axes can be optionally given by commands like:
  <pre>
  XRange: <i>min</i>, <i>max</i>
  YRange: <i>min</i>, <i>max</i>
  </pre>
  The arguments <i>min</i> and <i>max</i> are numbers, possibly
  including a sign and a decimal point. If they are not specified,
  then the ranges are computed automatically from the data and padded
  slightly so that datapoints are not plotted on the axes.
  <p>
  The tick marks for the axes are usually computed automatically from
  the ranges.  Every attempt is made to choose reasonable positions
  for the tick marks regardless of the data ranges (powers of
  ten multiplied by 1, 2, or 5 are used).  However, they can also be
  specified explicitly using commands like:
  <pre>
  XTicks: <i>label position, label position, ...</i>
  YTicks: <i>label position, label position, ...</i>
  </pre>
  A <i>label</i> is a string that must be surrounded by quotation
  marks if it contains any spaces.  A <i>position</i> is a number
  giving the location of the tick mark along the axis.  For example,
  a horizontal axis for a frequency domain plot might have tick marks
  as follows:
  <pre>
  XTicks: -PI -3.14159, -PI/2 -1.570795, 0 0, PI/2 1.570795, PI 3.14159
  </pre>
  Tick marks could also denote years, months, days of the week, etc.
  <p>
  The X and Y axes can use a logarithmic scale with the following commands:
  <pre>
  XLog: on
  YLog: on
  </pre>
  The grid labels represent powers of 10.  Note that if a logarithmic
  scale is used, then the values must be positive.  Non-positive values
  will be silently dropped.  Note further that when using logarithmic
  axes that the log of input data is taken as the data is added to the plot.
  This means that <pre>XLog: on</pre> or <pre>YLog: on</pre> should
  appear before any data.  Also, the value of the XTicks, YTicks,
  XRange or YRange directives should be in log units.
  So, <pre>XTicks: 1K 3</pre> will display the string <pre>1K</pre>
  at the 1000 mark.
  <p>
  By default, tick marks are connected by a light grey background grid.
  This grid can be turned off with the following command:
  <pre>
  Grid: off
  </pre>
  It can be turned back on with
  <pre>
  Grid: on
  </pre>
  Also, by default, the first ten data sets are shown each in a unique color.
  The use of color can be turned off with the command:
  <pre>
  Color: off
  </pre>
  It can be turned back on with
  <pre>
  Color: on
  </pre>
  Finally, the rather specialized command
  <pre>
  Wrap: on
  </pre>
  enables wrapping of the X (horizontal) axis, which means that if
  a point is added with X out of range, its X value will be modified
  modulo the range so that it lies in range. This command only has an
  effect if the X range has been set explicitly. It is designed specifically
  to support oscilloscope-like behavior, where the X value of points is
  increasing, but the display wraps it around to left. A point that lands
  on the right edge of the X range is repeated on the left edge to give
  a better sense of continuity. The feature works best when points do land
  precisely on the edge, and are plotted from left to right, increasing
  in X.
  <p>
  All of the above commands can also be invoked directly by calling the
  the corresponding public methods from some Java procedure.
  <p>
  This class uses features of JDK 1.2, and hence if used in an applet,
  it can only be viewed by a browser that supports JDK 1.2, or a plugin.
  
  @author Edward A. Lee, Christopher Hylands, Contributors: Jun Wu (jwu@inin.com.au), William Wu, Robert Kroeger
  
  @version $Id: PlotBox.java,v 1.244.2.1 2003/05/16 17:09:15 cxh Exp $
  */
  
  public class PlotBox extends JPanel implements Printable {
  
  /*******************************************************************************
   *                                                                             *
   *                             INSTANCE VARIABLES                              *
   *                                                                             *
   ******************************************************************************/
  
      //Release
      public static final String PTPLOT_RELEASE = "5.3";
  
      // The range of the data to be plotted.
      protected transient double _yMax = 0, _yMin = 0, _xMax = 0, _xMin = 0;
  
      /**
       * The factor we pad by so that we don't plot points on the axes.
       */
  
      protected double _padding = 0.05;
  
      // Whether the ranges have been given.
      protected transient boolean _xRangeGiven = false;
      protected transient boolean _yRangeGiven = false;
      protected transient boolean _rangesGivenByZooming = false;
  
      /**
       * @serial The given X and Y ranges.
       * If they have been given the top and bottom of the x and y ranges.
       * This is different from _xMin and _xMax, which actually represent
       * the range of data that is plotted.  This represents the range
       * specified (which may be different due to zooming).
       */
  
      protected double _xlowgiven, _xhighgiven, _ylowgiven, _yhighgiven;
  
      /**
       * @serial The minimum X value registered so for, for auto ranging.
       */
  
      protected double _xBottom = Double.MAX_VALUE;
  
      /**
       * @serial The maximum X value registered so for, for auto ranging.
       */
  
      protected double _xTop = - Double.MAX_VALUE;
  
      /**
       * @serial The minimum Y value registered so for, for auto ranging.
       */
  
      protected double _yBottom = Double.MAX_VALUE;
  
      /**
       * @serial The maximum Y value registered so for, for auto ranging.
       */
  
      protected double _yTop = - Double.MAX_VALUE;
  
      /**
       * @serial Whether to draw the axes using a logarithmic scale.
       */
  
      protected boolean _xlog = false, _ylog = false;
  
      // For use in calculating log base 10. A log times this is a log base 10.
      protected static final double _LOG10SCALE = 1/Math.log(10);
  
      /**
       * @serial Whether to draw a background grid.
       */
  
      protected boolean _grid = true;
  
      /**
       * @serial Whether to wrap the X axis.
       */
  
      protected boolean _wrap = false;
  
      /**
       * @serial The high range of the X axis for wrapping.
       */
  
      protected double _wrapHigh;
  
      /**
       * @serial The low range of the X axis for wrapping.
       */
  
      protected double _wrapLow;
  
      /**
       * @serial Color of the background, settable from HTML.
       */
  
      protected Color _background = Color.white;
  
      /**
       * @serial Color of the foreground, settable from HTML.
       */
  
      protected Color _foreground = Color.black;
  
      /**
       * @serial Top padding.
       * Derived classes can increment these to make space around the plot.
       */
  
      protected int _topPadding = 5;
  
      /**
       * @serial Bottom padding.
       * Derived classes can increment these to make space around the plot.
       */
  
      protected int _bottomPadding = 5;
  
      /**
       * @serial Right padding.
       * Derived classes can increment these to make space around the plot.
       */
  
      protected int _rightPadding = 5;
  
      /**
       * @serial Left padding.
       * Derived classes can increment these to make space around the plot.
       */
  
      protected int _leftPadding = 5;
  
      // The naming convention is: "_ulx" = "upper left x", where "x" is
      // the horizontal dimension.
  
      /**
       * The x value of the upper left corner of the plot rectangle in pixels.
       */
  
      protected int _ulx = 1;
  
      /**
       * The y value of the upper left corner of the plot rectangle in pixels.
       */
  
      protected int _uly = 1;
  
      /**
       * The x value of the lower right corner of the plot rectangle in pixels.
       */
  
      protected int _lrx = 100;
  
      /**
       * The y value of the lower right corner of the plot rectangle in pixels.
       */
  
      protected int _lry = 100;
  
      /**
       * User specified plot rectangle, null if none specified.
       * @see #setPlotRectangle(Rectangle)
       */
  
      protected Rectangle _specifiedPlotRectangle = null;
  
      /**
       * Scaling used for the vertical axis in plotting points.
       * The units are pixels/unit, where unit is the units of the Y axis.
       */
  
      protected double _yscale = 1.0;
  
      /**
       * Scaling used for the horizontal axis in plotting points.
       * The units are pixels/unit, where unit is the units of the X axis.
       */
  
      protected double _xscale = 1.0;
  
      /**
       * @serial Indicator whether to use _colors.
       */
  
      protected boolean _usecolor = true;
  
      // Default _colors, by data set.
      // There are 11 colors so that combined with the
      // 10 marks of the Plot class, we can distinguish 110
      // distinct data sets.
      static protected Color[] _colors = {
          new Color(0xff0000),   // red
          new Color(0x0000ff),   // blue
          new Color(0x00aaaa),   // cyan-ish
          new Color(0x000000),   // black
          new Color(0xffa500),   // orange
          new Color(0x53868b),   // cadetblue4
          new Color(0xff7f50),   // coral
          new Color(0x45ab1f),   // dark green-ish
          new Color(0x90422d),   // sienna-ish
          new Color(0xa0a0a0),   // grey-ish
          new Color(0x14ff14),   // green-ish
      };
  
      /**
       * @serial Width and height of component in pixels.
       */
  
      protected int _width = 500, _height = 300,
          _preferredWidth = 500, _preferredHeight = 300;
  
      /**
       * @serial Indicator that size has been set.
       */
  
      protected boolean _sizeHasBeenSet = false;
  
      /**
       * @serial Indicator of whether actions are deferred.
       */
  
      private boolean _actionsDeferred = false;
  
      /**
       * @serial List of deferred actions.
       */
  
      private Vector _deferredActions;
  
      /**
       * @serial The file to be opened.
       */
  
      private String _filespec = null;
  
      // Call setXORMode with a hardwired color because
      // _background does not work in an application,
      // and _foreground does not work in an applet.
      // NOTE: For some reason, this comes out blue, which is fine...
  
      private static final Color _boxColor = Color.orange;
  
      /**
       * @serial The range of the plot as labeled
       * (multiply by 10^exp for actual range.
       */
  
      private double _ytickMax = 0.0, _ytickMin = 0.0,
          _xtickMax = 0.0 , _xtickMin = 0.0 ;
  
      /**
       * @serial The power of ten by which the range numbers should
       * be multiplied.
       */
  
      private int _yExp = 0, _xExp = 0;
  
      /**
       * @serial Scaling used in making tick marks.
       */
  
      private double _ytickscale = 0.0, _xtickscale = 0.0;
  
      /**
       * @serial Font information.
       */
      private Font _labelFont = null, _superscriptFont = null,
          _titleFont = null;
  
      /**
       * @serial FontMetric information.
       */
  
      private FontMetrics _labelFontMetrics = null,
          _superscriptFontMetrics = null,
          _titleFontMetrics = null;
  
      // Number format cache used by _formatNum.
      // See the comment in _formatNum for more information.
      // private transient NumberFormat _numberFormat = null;
  
      // Used for log axes. Index into vector of axis labels.
      private transient int _gridCurJuke = 0;
  
      // Used for log axes.  Base of the grid.
      private transient double _gridBase = 0.0;
  
      // An array of strings for reporting errors.
      private transient String _errorMsg[];
  
      /**
       * @serial The title and label strings.
       */
  
      private String _xlabel, _ylabel, _title;
  
      /**
       * @serial Legend information.
       */
  
      private Vector _legendStrings = new Vector(),
          _legendDatasets = new Vector();
  
      /**
       * @serial If XTicks or YTicks are given
       */
  
      private Vector _xticks = null, _xticklabels = null,
          _yticks = null, _yticklabels = null;
  
      // A button for filling the plot
      private transient JButton _fillButton = null;
  
      // A button for formatting the plot
      private transient JButton _formatButton = null;
  
      // Indicator of whether X and Y range has been first specified.
      boolean _originalXRangeGiven = false, _originalYRangeGiven = false;
  
      // First values specified to setXRange() and setYRange().
      double _originalXlow = 0.0, _originalXhigh = 0.0,
          _originalYlow = 0.0, _originalYhigh = 0.0;
  
      // An offscreen buffer for improving plot performance.
      protected transient BufferedImage _plotImage = null;
  
      // A button for printing the plot
      private transient JButton _printButton = null;
  
      // A button for filling the plot
      private transient JButton _resetButton = null;
  
      // Variables keeping track of the interactive zoom box.
      // Initialize to impossible values.
      private transient int _zoomx = -1;
      private transient int _zoomy = -1;
      private transient int _zoomxn = -1;
      private transient int _zoomyn = -1;
  
      // Control whether we are zooming in or out.
      private transient boolean _zoomin = false;
      private transient boolean _zoomout = false;
      private transient boolean _drawn = false;
      private transient boolean _zooming = false;
  
      // NOTE: It is unfortunate to have to include the DTD here, but there
      // seems to be no other way to ensure that the generated data exactly
      // matches the DTD.
      private static final String _DTD =
      "<!-- PlotML DTD, created by Edward A. Lee, eal@eecs.berkeley.edu.\n"
      + "   See http://ptolemy.eecs.berkeley.edu/java/ptplot -->\n"
      + "<!ELEMENT plot (barGraph | bin | dataset | default | noColor | \n"
      + "        noGrid | title | wrap | xLabel | xLog | xRange | xTicks | yLabel | \n"
      + " yLog | yRange | yTicks)*>\n"
      + "  <!ELEMENT barGraph EMPTY>\n"
      + "    <!ATTLIST barGraph width CDATA #IMPLIED>\n"
      + "    <!ATTLIST barGraph offset CDATA #IMPLIED>\n"
      + "  <!ELEMENT bin EMPTY>\n"
      + "    <!ATTLIST bin width CDATA #IMPLIED>\n"
      + "    <!ATTLIST bin offset CDATA #IMPLIED>\n"
      + "  <!ELEMENT dataset (m | move | p | point)*>\n"
      + "    <!ATTLIST dataset connected (yes | no) #IMPLIED>\n"
      + "    <!ATTLIST dataset marks (none | dots | points | various) #IMPLIED>\n"
      + "    <!ATTLIST dataset name CDATA #IMPLIED>\n"
      + "    <!ATTLIST dataset stems (yes | no) #IMPLIED>\n"
      + "  <!ELEMENT default EMPTY>\n"
      + "    <!ATTLIST default connected (yes | no) \"yes\">\n"
      + "    <!ATTLIST default marks (none | dots | points | various) \"none\">\n"
      + "    <!ATTLIST default stems (yes | no) \"no\">\n"
      + "  <!ELEMENT noColor EMPTY>\n"
      + "  <!ELEMENT noGrid EMPTY>\n"
      + "  <!ELEMENT title (#PCDATA)>\n"
      + "  <!ELEMENT wrap EMPTY>\n"
      + "  <!ELEMENT xLabel (#PCDATA)>\n"
      + "  <!ELEMENT xLog EMPTY>\n"
      + "  <!ELEMENT xRange EMPTY>\n"
      + "    <!ATTLIST xRange min CDATA #REQUIRED>\n"
      + "    <!ATTLIST xRange max CDATA #REQUIRED>\n"
      + "  <!ELEMENT xTicks (tick)+>\n"
      + "  <!ELEMENT yLabel (#PCDATA)>\n"
      + "  <!ELEMENT yLog EMPTY>\n"
      + "  <!ELEMENT yRange EMPTY>\n"
      + "    <!ATTLIST yRange min CDATA #REQUIRED>\n"
      + "    <!ATTLIST yRange max CDATA #REQUIRED>\n"
      + "  <!ELEMENT yTicks (tick)+>\n"
      + "    <!ELEMENT tick EMPTY>\n"
      + "      <!ATTLIST tick label CDATA #REQUIRED>\n"
      + "      <!ATTLIST tick position CDATA #REQUIRED>\n"
      + "    <!ELEMENT m EMPTY>\n"
      + "      <!ATTLIST m x CDATA #IMPLIED>\n"
      + "      <!ATTLIST m x CDATA #REQUIRED>\n"
      + "      <!ATTLIST m lowErrorBar CDATA #IMPLIED>\n"
      + "      <!ATTLIST m highErrorBar CDATA #IMPLIED>\n"
      + "    <!ELEMENT move EMPTY>\n"
      + "      <!ATTLIST move x CDATA #IMPLIED>\n"
      + "      <!ATTLIST move x CDATA #REQUIRED>\n"
      + "      <!ATTLIST move lowErrorBar CDATA #IMPLIED>\n"
      + "      <!ATTLIST move highErrorBar CDATA #IMPLIED>\n"
      + "    <!ELEMENT p EMPTY>\n"
      + "      <!ATTLIST p x CDATA #IMPLIED>\n"
      + "      <!ATTLIST p x CDATA #REQUIRED>\n"
      + "      <!ATTLIST p lowErrorBar CDATA #IMPLIED>\n"
      + "      <!ATTLIST p highErrorBar CDATA #IMPLIED>\n"
      + "    <!ELEMENT point EMPTY>\n"
      + "      <!ATTLIST point x CDATA #IMPLIED>\n"
      + "      <!ATTLIST point x CDATA #REQUIRED>\n"
      + "      <!ATTLIST point lowErrorBar CDATA #IMPLIED>\n"
      + "      <!ATTLIST point highErrorBar CDATA #IMPLIED>";
  
  /*******************************************************************************
   *                                                                             *
   *                               CONSTRUCTOR                                   *
   *                                                                             *
   ******************************************************************************/
  
      /**
       * Construct a plot box with a default configuration.
       */
  
      public PlotBox() {
          // If we make this transparent, the background shows through.
          // However, we assume that the user will set the background.
          // NOTE: A component is transparent by default (?).
          // setOpaque(false);
          setOpaque(true);
  
          // Create a right-justified layout with spacing of 2 pixels.
          setLayout(new FlowLayout(FlowLayout.RIGHT, 2, 2));
          addMouseListener(new ZoomListener());
          addKeyListener(new CommandListener());
          addMouseMotionListener(new DragListener());
          // This is something we want to do only once...
          _measureFonts();
          // Request the focus so that key events are heard.
          // NOTE: no longer needed?
          // requestFocus();
      }
  
  /*******************************************************************************
   *                                                                             *
   *                               PUBLIC METHODS                                *
   *                                                                             *
   ******************************************************************************/
  
      /**
       * Add a legend (displayed at the upper right) for the specified
       * data set with the specified string.  Short strings generally
       * fit better than long strings.  If the string is empty, or the
       * argument is null, then no legend is added.
       *
       * @param dataset The dataset index.
       * @param legend The label for the dataset.
       */
  
      public synchronized void addLegend(int dataset, String legend) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          if (legend == null || legend.equals("")) return;
          _legendStrings.addElement(legend);
          _legendDatasets.addElement(new Integer(dataset));
      }
  
      /**
       * Specify a tick mark for the X axis.  The label given is placed
       * on the axis at the position given by <i>position</i>. If this
       * is called once or more, automatic generation of tick marks is
       * disabled.  The tick mark will appear only if it is within the X
       * range.
       *
       * @param label The label for the tick mark.
       * @param position The position on the X axis.
       */
  
      public synchronized void addXTick(String label, double position) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          if (_xticks == null) {
              _xticks = new Vector();
              _xticklabels = new Vector();
          }
          _xticks.addElement(new Double(position));
          _xticklabels.addElement(label);
      }
  
      /**
       * Specify a tick mark for the Y axis.  The label given is placed
       * on the axis at the position given by <i>position</i>. If this
       * is called once or more, automatic generation of tick marks is
       * disabled.  The tick mark will appear only if it is within the Y
       * range.
       *
       * @param label The label for the tick mark.
       * @param position The position on the Y axis.
       */
  
      public synchronized void addYTick(String label, double position) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          if (_yticks == null) {
              _yticks = new Vector();
              _yticklabels = new Vector();
          }
          _yticks.addElement(new Double(position));
          _yticklabels.addElement(label);
      }
  
      /**
       * If the argument is true, clear the axes.  I.e., set all parameters
       * controlling the axes to their initial conditions.
       * For the change to take effect, call repaint().  If the argument
       * is false, do nothing.
       *
       * @param axes If true, clear the axes parameters.
       */
  
      public synchronized void clear(boolean axes) {
          // We need to repaint the offscreen buffer.
          _plotImage = null;
  
          _xBottom = Double.MAX_VALUE;
          _xTop = - Double.MAX_VALUE;
          _yBottom = Double.MAX_VALUE;
          _yTop = - Double.MAX_VALUE;
          if (axes) {
              // Protected members first.
              _yMax = 0;
              _yMin = 0;
              _xMax = 0;
              _xMin = 0;
              _xRangeGiven = false;
              _yRangeGiven = false;
              _originalXRangeGiven = false;
              _originalYRangeGiven = false;
              _rangesGivenByZooming = false;
              _xlog = false;
              _ylog = false;
              _grid = true;
              _wrap = false;
              _usecolor = true;
  
              // Private members next...
              _filespec = null;
              _xlabel = null;
              _ylabel = null;
              _title = null;
              _legendStrings = new Vector();
              _legendDatasets = new Vector();
              _xticks = null;
              _xticklabels = null;
              _yticks = null;
              _yticklabels = null;
          }
      }
  
      /**
       * Clear all legends.  This will show up on the next redraw.
       */
  
      public synchronized void clearLegends() {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _legendStrings = new Vector();
          _legendDatasets = new Vector();
      }
  
      /**
       * If this method is called in the event thread, then simply
       * execute the specified action.  Otherwise,
       * if there are already deferred actions, then add the specified
       * one to the list.  Otherwise, create a list of deferred actions,
       * if necessary, and request that the list be processed in the
       * event dispatch thread.
       *
       * Note that it does not work nearly as well to simply schedule
       * the action yourself on the event thread because if there are a
       * large number of actions, then the event thread will not be able
       * to keep up.  By grouping these actions, we avoid this problem.
       *
       * This method is not synchronized, so the caller should be.
       *
       * @param action The Runnable object to execute.
       */
  
      public void deferIfNecessary(Runnable action) {
          // In swing, updates to showing graphics must be done in the
          // event thread.  If we are in the event thread, then proceed.
          // Otherwise, queue a request or add to a pending request.
          if (EventQueue.isDispatchThread()) {
              action.run();
          } else {
  
              if (_deferredActions == null) {
                  _deferredActions = new Vector();
              }
              // Add the specified action to the list of actions to perform.
              _deferredActions.add(action);
  
              // If it hasn't already been requested, request that actions
              // be performed in the event dispatch thread.
              if (!_actionsDeferred) {
                  Runnable doActions = new Runnable() {
                          public void run() {
                              _executeDeferredActions();
                          }
                      };
                  try {
                      // NOTE: Using invokeAndWait() here risks causing
                      // deadlock.  Don't do it!
                      SwingUtilities.invokeLater(doActions);
                  } catch (Exception ex) {
                      // Ignore InterruptedException.
                      // Other exceptions should not occur.
                  }
                  _actionsDeferred = true;
              }
          }
      }
  
      /**
       * Export a description of the plot.
       * Currently, only EPS is supported.  But in the future, this
       * may cause a dialog box to open to allow the user to select
       * a format.  If the argument is null, then the description goes
       * to the clipboard.  Otherwise, it goes to the specified file.
       * To send it to standard output, use
       * <code>System.out</code> as an argument.
       *
       * @param out An output stream to which to send the description.
       */
  
      public synchronized void export(OutputStream out) {
          try {
              EPSGraphics g = new EPSGraphics(out, _width, _height);
              _drawPlot(g, false);
              g.showpage();
          }
          catch (RuntimeException ex) {
              String message = "Export failed: " + ex.getMessage();
              JOptionPane.showMessageDialog(this, message,
                      "Plot Message",
                      JOptionPane.ERROR_MESSAGE);
              // Rethrow the exception so that we don't report success,
              // and so the stack trace is displayed on standard out.
              throw (RuntimeException)ex.fillInStackTrace();
          }
      }
  
      // CONTRIBUTED CODE.
      // I wanted the ability to use the Plot object in a servlet and to
      // write out the resultant images. The following routines,
      // particularly exportImage(), permit this. I also had to make some
      // minor changes elsewhere. Rob Kroeger, May 2001.
  
      // NOTE: This code has been modified by EAL to conform with Ptolemy II
      // coding style.
  
      /**
       * Create a BufferedImage and draw this plot to it.
       * The size of the returned image matches the current size of the plot.
       * This method can be used, for
       * example, by a servlet to produce an image, rather than
       * requiring an applet to instantiate a PlotBox.
       *
       * @return An image filled by the plot.
       */
  
      public synchronized BufferedImage exportImage() {
          Rectangle rectangle = new Rectangle(_preferredWidth, _preferredHeight);
          return exportImage(
              new BufferedImage(
                      rectangle.width,
                      rectangle.height,
                      BufferedImage.TYPE_INT_ARGB),
              rectangle,
              _defaultImageRenderingHints(),
              false);
      }
  
      /**
       * Create a BufferedImage the size of the given rectangle and draw
       * this plot to it at the position specified by the rectangle.
       * The plot is rendered using anti-aliasing.
       *
       * @param rectangle The size of the plot. This method can be used, for
       * example, by a servlet to produce an image, rather than
       * requiring an applet to instantiate a PlotBox.
       * @return An image containing the plot.
       */
  
      public synchronized BufferedImage exportImage(Rectangle rectangle) {
          return exportImage(new BufferedImage(rectangle.width,rectangle.height,BufferedImage.TYPE_INT_ARGB),rectangle,_defaultImageRenderingHints(),false);
      }
  
      /**
       * Draw this plot onto the specified image at the position of the
       * specified rectangle with the size of the specified rectangle.
       * The plot is rendered using anti-aliasing.
       * This can be used to paint a number of different
       * plots onto a single buffered image.  This method can be used, for
       * example, by a servlet to produce an image, rather than
       * requiring an applet to instantiate a PlotBox.
       *
       * @param bufferedImage Image onto which the plot is drawn.
       * @param rectangle The size and position of the plot in the image.
       * @param hints Rendering hints for this plot.
       * @param transparent Indicator that the background of the plot
       * should not be painted.
       * @return The modified bufferedImage.
       */
  
      public synchronized BufferedImage exportImage(BufferedImage bufferedImage, Rectangle rectangle, RenderingHints hints, boolean transparent) {
          Graphics2D graphics = bufferedImage.createGraphics();
          graphics.addRenderingHints(_defaultImageRenderingHints());
          if ( !transparent ) {
              graphics.setColor(Color.white);        // set the background color
              graphics.fill(rectangle);
          }
          _drawPlot(graphics, false , rectangle);
          return bufferedImage;
      }
  
      /**
       * Draw this plot onto the provided image.
       * This method does not paint the background, so the plot is
       * transparent.  The plot fills the image, and is rendered
       * using anti-aliasing.  This method can be used to overlay
       * multiple plots on the same image, although you must use care
       * to ensure that the axes and other labels are identical.
       * Hence, it is usually better to simply combine data sets into
       * a single plot.
       *
       * @param bufferedImage The image onto which to render the plot.
       * @return The modified bufferedImage.
       */
  
      public synchronized BufferedImage exportImage(BufferedImage bufferedImage) {
          return exportImage(bufferedImage,new Rectangle(bufferedImage.getWidth(),bufferedImage.getHeight()),_defaultImageRenderingHints(),true);
      }
  
      /**
       * Rescale so that the data that is currently plotted just fits.
       * This is with the protected variables _xBottom, _xTop,
       * _yBottom, and _yTop.  It is up to derived classes to ensure that
       * variables are valid.
       * This method calls repaint(), which eventually causes the display
       * to be updated.
       */
  
      public synchronized void fillPlot() {
          // NOTE: These used to be _setXRange() and _setYRange() to avoid
          // confusing this with user-specified ranges.  But we want to treat
          // a fill command as a user specified range.
          // EAL, 6/12/00.
          setXRange(_xBottom, _xTop);
          setYRange(_yBottom, _yTop);
          repaint();
          // Reacquire the focus so that key bindings work.
          // NOTE: no longer needed?
          // requestFocus();
      }
  
      /**
       * Return whether the plot uses color.
       *
       * @return True if the plot uses color.
       */
  
      public boolean getColor() {
          return _usecolor;
      }
  
      /**
       * Get the point colors.
       *
       * @return Array of colors
       * @see #setColors(Color[])
       */
  
      public Color[] getColors() {
          return _colors;
      }
  
      /**
       * Convert a color name into a Color. Currently, only a very limited
       * set of color names is supported: black, white, red, green, and blue.
       *
       * @param name A color name, or null if not found.
       * @return An instance of Color.
       */
  
      public static Color getColorByName(String name) {
          try {
              // Check to see if it is a hexadecimal
              if (name.startsWith("#")) {
                  name = name.substring(1);
              }
              Color col = new Color(Integer.parseInt(name, 16));
              return col;
          }
          catch (NumberFormatException e) {}
          // FIXME: This is a poor excuse for a list of colors and values.
          // We should use a hash table here.
          // Note that Color decode() wants the values to start with 0x.
          String names[][] = {
              {"black", "00000"}, {"white", "ffffff"},
              {"red", "ff0000"}, {"green", "00ff00"}, {"blue", "0000ff"}
          };
          for (int i = 0; i < names.length; i++) {
              if (name.equals(names[i][0])) {
                  try {
                      Color col = new Color(Integer.parseInt(names[i][1], 16));
                      return col;
                  } catch (NumberFormatException e) {}
              }
          }
          return null;
      }
  
      /**
       * Return whether the grid is drawn.
       *
       * @return True if a grid is drawn.
       */
  
      public boolean getGrid() {
          return _grid;
      }
  
      /**
       * Get the legend for a dataset, or null if there is none.
       * The legend would have been set by addLegend().
       *
       * @param dataset The dataset index.
       * @return The legend label, or null if there is none.
       */
  
      public synchronized String getLegend(int dataset) {
          int idx = _legendDatasets.indexOf(new Integer(dataset), 0);
          if (idx != -1) {
              return (String)_legendStrings.elementAt(idx);
          }
          else {
              return null;
          }
      }
  
      /**
       * Given a legend string, return the corresponding dataset or -1 if no
       * legend was added with that legend string
       * The legend would have been set by addLegend().
       *
       * @param legend The String naming the legend
       * @return The legend dataset, or -1 if not found.
       */
  
      public synchronized int getLegendDataset(String legend) {
          int index = _legendStrings.indexOf(legend);
          if (index == -1) {
              return -1;
          }
          return ((Integer)_legendDatasets.get(index)).intValue();
      }
  
  
      /**
       * If the size of the plot has been set by setSize(),
       * then return that size.  Otherwise, return what the superclass
       * returns (which is undocumented, but apparently imposes no maximum size).
       * Currently (JDK 1.3), only BoxLayout pays any attention to this.
       *
       * @return The maximum desired size.
       */
  
      public synchronized Dimension getMaximumSize() {
          if (_sizeHasBeenSet) {
              return new Dimension(_preferredWidth, _preferredHeight);
          }
          else {
              return super.getMaximumSize();
          }
      }
  
      /**
       * Get the minimum size of this component.
       * This is simply the dimensions specified by setSize(),
       * if this has been called.  Otherwise, return whatever the base
       * class returns, which is undocumented.
       *
       * @return The minimum size.
       */
  
      public synchronized Dimension getMinimumSize() {
          if (_sizeHasBeenSet) {
              return new Dimension(_preferredWidth, _preferredHeight);
          }
          else {
              return super.getMinimumSize();
          }
      }
  
      /**
       * Get the current plot rectangle.
       * Note that Rectangle returned by this method is calculated
       * from the values of {@link #_ulx}, {@link #_uly},
       * {@link #_lrx} and {@link #_lry}.  The value passed in by
       * setPlotRectangle() is not directly used, thus calling
       * getPlotRectangle() may not return the same rectangle that
       * was passed in with setPlotRectangle().
       *
       * @return Rectangle
       * @see #setPlotRectangle(Rectangle)
       */
  
      public Rectangle getPlotRectangle() {
          return new Rectangle(_ulx, _uly, _lrx-_ulx, _lry-_uly);
      }
  
      /**
       * Get the preferred size of this component.
       * This is simply the dimensions specified by setSize(),
       * if this has been called, or the default width and height
       * otherwise (500 by 300).
       *
       * @return The preferred size.
       */
  
      public synchronized Dimension getPreferredSize() {
          return new Dimension(_preferredWidth, _preferredHeight);
      }
  
      /**
       * Get the title of the graph, or an empty string if there is none.
       *
       * @return The title.
       */
  
      public synchronized String getTitle() {
          if (_title == null) return "";
          return _title;
      }
  
      /**
       * Get the range for X values of the data points registered so far.
       * Usually, derived classes handle managing the range by checking
       * each new point against the current range.
       *
       * @return An array of two doubles where the first element is the
       * minimum and the second element is the maximum.
       * @see #getXRange()
       */
  
      public synchronized double[] getXAutoRange() {
          double[] result = new double[2];
          result[0] = _xBottom;
          result[1] = _xTop;
          return result;
      }
  
      /**
       * Get the label for the X (horizontal) axis, or null if none has
       * been set.
       *
       * @return The X label.
       */
  
      public synchronized String getXLabel() {
          return _xlabel;
      }
  
      /**
       * Return whether the X axis is drawn with a logarithmic scale.
       *
       * @return True if the X axis is logarithmic.
       */
  
      public boolean getXLog() {
          return _xlog;
      }
  
      /**
       * Get the X range. If {@link #setXRange(double, double)} has been
       *  called, then this method returns the values passed in as
       *  arguments to setXRange(double, double).  If setXRange(double,
       *  double) has not been called, then this method returns the
       *  range of the data to be plotted, which might not be all of the
       *  data due to zooming.
       *
       *  @return An array of two doubles where the first element is the
       *  minimum and the second element is the maximum.
       *  @see #getXAutoRange()
       */
  
      public synchronized double[] getXRange() {
          double[] result = new double[2];
          if (_xRangeGiven) {
              result[0] = _xlowgiven;
              result[1] = _xhighgiven;
          }
          else {
              // Have to first correct for the padding.
              result[0] = _xMin + ((_xMax - _xMin) * _padding);
              result[1] = _xMax - ((_xMax - _xMin) * _padding);;
          }
          return result;
      }
  
      /**
       * Get the X ticks that have been specified, or null if none.
       * The return value is an array with two vectors, the first of
       * which specifies the X tick locations (as instances of Double),
       * and the second of which specifies the corresponding labels.
       *
       * @return The X ticks.
       */
  
      public synchronized Vector[] getXTicks() {
          if (_xticks == null) return null;
          Vector[] result = new Vector[2];
          result[0] = _xticks;
          result[1] = _xticklabels;
          return result;
      }
  
      /**
       * Get the range for Y values of the data points registered so far.
       * Usually, derived classes handle managing the range by checking
       * each new point against the range.
       *
       * @return An array of two doubles where the first element is the
       * minimum and the second element is the maximum.
       * @see #getYRange()
       */
  
      public synchronized double[] getYAutoRange() {
          double[] result = new double[2];
          result[0] = _yBottom;
          result[1] = _yTop;
          return result;
      }
  
      /**
       * Get the label for the Y (vertical) axis, or null if none has
       * been set.
       *
       * @return The Y label.
       */
  
      public String getYLabel() {
          return _ylabel;
      }
  
      /**
       * Return whether the Y axis is drawn with a logarithmic scale.
       *
       * @return True if the Y axis is logarithmic.
       */
  
      public boolean getYLog() {
          return _ylog;
      }
  
      /**
       * Get the Y range. If {@link #setYRange(double, double)} has been
       * called, then this method returns the values passed in as
       * arguments to setYRange(double, double).  If setYRange(double,
       * double) has not been called, then this method returns the
       * range of the data to be plotted, which might not be all of the
       * data due to zooming.
       *
       * @return An array of two doubles where the first element is the
       * minimum and the second element is the maximum.
       * @see #getYAutoRange()
       */
  
      public synchronized double[] getYRange() {
          double[] result = new double[2];
          if (_yRangeGiven) {
              result[0] = _ylowgiven;
              result[1] = _yhighgiven;
          }
          else {
              // Have to first correct for the padding.
              result[0] = _yMin + ((_yMax - _yMin) * _padding);
              result[1] = _yMax - ((_yMax - _yMin) * _padding);;
          }
          return result;
      }
  
      /**
       * Get the Y ticks that have been specified, or null if none.
       * The return value is an array with two vectors, the first of
       * which specifies the Y tick locations (as instances of Double),
       * and the second of which specifies the corresponding labels.
       *
       * @return The Y ticks.
       */
  
      public synchronized Vector[] getYTicks() {
          if (_yticks == null) return null;
          Vector[] result = new Vector[2];
          result[0] = _yticks;
          result[1] = _yticklabels;
          return result;
      }
  
      /**
       * Paint the component contents, which in this base class is
       * only the axes.
       *
       * @param graphics The graphics context.
       */
  
      public synchronized void paintComponent(Graphics graphics) {
          //  super.paintComponent(graphics);
          //         _drawPlot(graphics, true);
          BufferedImage newPlotImage = _plotImage;
          if (newPlotImage == null) {
              Rectangle bounds = getBounds();
              newPlotImage = new BufferedImage(
                      bounds.width, bounds.height,
                      BufferedImage.TYPE_3BYTE_BGR);
              _plotImage = newPlotImage;
              Graphics2D offScreenGraphics = newPlotImage.createGraphics();
              super.paintComponent(offScreenGraphics);
              _drawPlot(offScreenGraphics, true);
          }
          // Blit the offscreen image onto the screen.
          graphics.drawImage(newPlotImage, 0, 0, null);
  
  
          // Acquire the focus so that key bindings work.
          // NOTE: no longer needed?
          // requestFocus();
      }
  
      /**
       * Print the plot to a printer, represented by the specified graphics
       * object.
       *
       * @param graphics The context into which the page is drawn.
       * @param format The size and orientation of the page being drawn.
       * @param index The zero based index of the page to be drawn.
       * @return PAGE_EXISTS if the page is rendered successfully, or
       * NO_SUCH_PAGE if pageIndex specifies a non-existent page.
       * @exception PrinterException If the print job is terminated.
       */
  
      public synchronized int print(Graphics graphics, PageFormat format,
              int index) throws PrinterException {
          if (graphics == null) return Printable.NO_SUCH_PAGE;
          // We only print on one page.
          if (index >= 1) {
              return Printable.NO_SUCH_PAGE;
          }
          Graphics2D graphics2D = (Graphics2D) graphics;
          // Scale the printout to fit the pages.
          // Contributed by Laurent ETUR, Schlumberger Riboud Product Center
          double scalex = format.getImageableWidth() / (double) getWidth();
          double scaley = format.getImageableHeight() / (double) getHeight();
          double scale = Math.min(scalex, scaley);
          graphics2D.translate((int)format.getImageableX(),
                  (int)format.getImageableY());
          graphics2D.scale(scale, scale);
          _drawPlot(graphics, true);
          return Printable.PAGE_EXISTS;
      }
  
      /**
       * Read commands and/or plot data from an input stream in the old
       * (non-XML) file syntax.
       * To update the display, call repaint(), or make the plot visible with
       * setVisible(true).
       * <p>
       * To read from standard input, use:
       * <pre>
       *    read(System.in);
       * </pre>
       * To read from a url, use:
       * <pre>
       *    read(url.openStream());
       * </pre>
       * To read a URL from within an applet, use:
       * <pre>
       *    URL url = new URL(getDocumentBase(), urlSpec);
       *    read(url.openStream());
       * </pre>
       * Within an application, if you have an absolute URL, use:
       * <pre>
       *    URL url = new URL(urlSpec);
       *    read(url.openStream());
       * </pre>
       *  To read from a file, use:
       * <pre>
       *   read(new FileInputStream(filename));
       *  </pre>
       *
       * @param in The input stream.
       * @exception IOException If the stream cannot be read.
       */
  
      public synchronized void read(InputStream in) throws IOException {
          try {
              // NOTE: I tried to use exclusively the jdk 1.1 Reader classes,
              // but they provide no support like DataInputStream, nor
              // support for URL accesses.  So I use the older classes
              // here in a strange mixture.
  
              BufferedReader din = new BufferedReader(new InputStreamReader(in));
  
              try {
                  String line = din.readLine();
                  while (line != null) {
                      _parseLine(line);
                      line = din.readLine();
                  }
              }
              finally {
                  din.close();
              }
          }
          catch (IOException e) {
              _errorMsg = new String [2];
              _errorMsg[0] = "Failure reading input data.";
              _errorMsg[1] = e.getMessage();
              throw e;
          }
      }
  
      /**
       * Read a single line command provided as a string.
       * The commands can be any of those in the ASCII file format.
       *
       * @param command A command.
       */
  
      public synchronized void read(String command) {
          _parseLine(command);
      }
  
      /**
       * Remove the legend (displayed at the upper right) for the specified
       * data set. If the dataset is not found, nothing will occur.
       * The PlotBox must be repainted in order for this to take effect.
       *
       * @param dataset The dataset index.
       */
  
      public synchronized void removeLegend(int dataset) {
          final int len = _legendDatasets.size();
          int foundIndex = -1;
          boolean found = false;
          for (int i = 0; i < len && !found; ++i) {
              if (((Integer)_legendDatasets.get(i)).intValue() == dataset) {
                  foundIndex = i;
                  found = true;
              }
          }
          if (found) {
              _legendDatasets.remove(foundIndex);
              _legendStrings.remove(foundIndex);
          }
      }
  
      /**
       * Reset the X and Y axes to the ranges that were first specified
       * using setXRange() and setYRange(). If these methods have not been
       * called, then reset to the default ranges.
       * This method calls repaint(), which eventually causes the display
       * to be updated.
       */
  
      public synchronized void resetAxes() {
          setXRange(_originalXlow, _originalXhigh);
          setYRange(_originalYlow, _originalYhigh);
          repaint();
      }
  
      /**
       * Set the background color.
       *
       * @param background The background color.
       */
  
      public synchronized void setBackground(Color background) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _background = background;
          super.setBackground(_background);
      }
  
      /**
       * Move and resize this component. The new location of the top-left
       * corner is specified by x and y, and the new size is specified by
       * width and height. This overrides the base class method to make
       * a record of the new size.
       *
       * @param x The new x-coordinate of this component.
       * @param y The new y-coordinate of this component.
       * @param width The new width of this component.
       * @param height The new height of this component.
       */
  
      public synchronized void setBounds(int x, int y, int width, int height) {
          _width = width;
          _height = height;
  
          // Resizing the component means we need to redraw the buffer.
          _plotImage = null;
  
          super.setBounds(x, y, _width, _height);
      }
  
      /**
       * If the argument is true, make a fill button visible at the upper
       * right.  This button auto-scales the plot.
       * NOTE: The button may infringe on the title space,
       * if the title is long.  In an application, it is preferable to provide
       * a menu with the fill command.  This way, when printing the plot,
       * the printed plot will not have a spurious button.  Thus, this method
       * should be used only by applets, which normally do not have menus.
       * This method should only be called from within the event dispatch
       * thread, since it interacts with swing.
       */
  
      public synchronized void setButtons(boolean visible) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          if (_printButton == null) {
              // Load the image by using the absolute path to the gif.
              // Using a relative location should work, but it does not.
              // Use the resource locator of the class.
              // For more information, see
              // file:///C|/jdk1.3/docs/guide/resources/resources.html
              URL img = getClass().getResource("/due/utilities/functionplotting/genericplot/img/print.gif");
              if (img != null) {
                  ImageIcon printIcon = new ImageIcon(img);
                  _printButton = new JButton(printIcon);
                  _printButton.setBorderPainted(false);
              } else {
                  // Backup in case something goes wrong with the
                  // class loader.
                  _printButton = new JButton("P");
              }
              // FIXME: If we failed to get an image, then the letter "P"
              // Is not likely to fit into a 20x20 button.
              _printButton.setPreferredSize(new Dimension(20, 20));
              _printButton.setToolTipText("Print the plot.");
              _printButton.addActionListener(new ButtonListener());
              add(_printButton);
          }
          _printButton.setVisible(visible);
  
          if (_resetButton == null) {
              // Load the image by using the absolute path to the gif.
              // Using a relative location should work, but it does not.
              // Use the resource locator of the class.
              // For more information, see
              // file:///C:/jdk1.3/docs/guide/resources/resources.html
              URL img = getClass().getResource("/due/utilities/functionplotting/genericplot/img/reset.gif");
              if (img != null) {
                  ImageIcon resetIcon = new ImageIcon(img);
                  _resetButton = new JButton(resetIcon);
                  _resetButton.setBorderPainted(false);
              } else {
                  // Backup in case something goes wrong with the
                  // class loader.
                  _resetButton = new JButton("R");
              }
              // FIXME: If we failed to get an image, then the letter "R"
              // Is not likely to fit into a 20x20 button.
              _resetButton.setPreferredSize(new Dimension(20, 20));
              _resetButton.setToolTipText(
                      "Reset X and Y ranges to their original values");
              _resetButton.addActionListener(new ButtonListener());
              add(_resetButton);
          }
          _resetButton.setVisible(visible);
  
          if (_formatButton == null) {
              // Load the image by using the absolute path to the gif.
              // Using a relative location should work, but it does not.
              // Use the resource locator of the class.
              // For more information, see
              // file:///C|/jdk1.3/docs/guide/resources/resources.html
              URL img = getClass().getResource("/due/utilities/functionplotting/genericplot/img/format.gif");
              if (img != null) {
                  ImageIcon formatIcon = new ImageIcon(img);
                  _formatButton = new JButton(formatIcon);
                  _formatButton.setBorderPainted(false);
              } else {
                  // Backup in case something goes wrong with the
                  // class loader.
                  _formatButton = new JButton("S");
              }
              // FIXME: If we failed to get an image, then the letter "S"
              // Is not likely to fit into a 20x20 button.
              _formatButton.setPreferredSize(new Dimension(20, 20));
              _formatButton.setToolTipText(
                      "Set the plot format");
              _formatButton.addActionListener(new ButtonListener());
              add(_formatButton);
          }
          _formatButton.setVisible(visible);
  
          if (_fillButton == null) {
              // Load the image by using the absolute path to the gif.
              // Using a relative location should work, but it does not.
              // Use the resource locator of the class.
              // For more information, see
              // file:///C|/jdk1.3/docs/guide/resources/resources.html
              URL img = getClass().getResource("/due/utilities/functionplotting/genericplot/img/fill.gif");
              if (img != null) {
                  ImageIcon fillIcon = new ImageIcon(img);
                  _fillButton = new JButton(fillIcon);
                  _fillButton.setBorderPainted(false);
              } else {
                  // Backup in case something goes wrong with the
                  // class loader.
                  _fillButton = new JButton("F");
              }
              // FIXME: If we failed to get an image, then the letter "F"
              // Is not likely to fit into a 20x20 button.
              _fillButton.setPreferredSize(new Dimension(20, 20));
              _fillButton.setToolTipText(
                      "Rescale the plot to fit the data");
              _fillButton.addActionListener(new ButtonListener());
              add(_fillButton);
          }
          _fillButton.setVisible(visible);
          // Request the focus so that key events are heard.
          // NOTE: no longer needed?
          // requestFocus();
      }
  
      /**
       * If the argument is false, draw the plot without using color
       * (in black and white).  Otherwise, draw it in color (the default).
       *
       * @param useColor False to draw in back and white.
       */
  
      public synchronized void setColor(boolean useColor) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _usecolor = useColor;
      }
  
      /**
       * Set the point colors.  Note that the default colors have been
       * carefully selected to maximize readability and that it is easy
       * to use colors that result in a very ugly plot.
       *
       * @param colors Array of colors to use in succession for data sets.
       * @see #getColors()
       */
  
      public synchronized void setColors(Color[] colors) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _colors = colors;
      }
  
      /**
       * Set the foreground color.
       *
       * @param foreground The foreground color.
       */
  
      public synchronized void setForeground(Color foreground) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _foreground = foreground;
          super.setForeground(_foreground);
      }
  
      /**
       * Control whether the grid is drawn.
       *
       * @param grid If true, a grid is drawn.
       */
  
      public synchronized void setGrid(boolean grid) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _grid = grid;
      }
  
      /**
       * Set the label font, which is used for axis labels and legend labels.
       * The font names understood are those understood by
       * java.awt.Font.decode().
       *
       * @param name A font name.
       */
  
      public synchronized void setLabelFont(String name) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _labelFont = Font.decode(name);
          _labelFontMetrics = getFontMetrics(_labelFont);
      }
  
      /**
       * Set the plot rectangle inside the axes.  This method
       * can be used to create two plots that share the same axes.
       *
       * @param rectangle Rectangle space inside axes.
       * @see #getPlotRectangle()
       */
  
      public synchronized void setPlotRectangle(Rectangle rectangle) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _specifiedPlotRectangle = rectangle;
      }
  
      /**
       * Set the size of the plot.  This overrides the base class to make
       * it work.  In particular, it records the specified size so that
       * getMinimumSize() and getPreferredSize() return the specified value.
       * However, it only works if the plot is placed in its own JPanel.
       * This is because the JPanel asks the contained component for
       * its preferred size before determining the size of the panel.
       * If the plot is placed directly in the content pane of a JApplet,
       * then, mysteriously, this method has no effect.
       *
       * @param width The width, in pixels.
       * @param height The height, in pixels.
       */
  
      public synchronized void setSize(int width, int height) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _width = width;
          _height = height;
          _preferredWidth = width;
          _preferredHeight = height;
          _sizeHasBeenSet = true;
          super.setSize(width, height);
      }
  
      /**
       * Set the title of the graph.
       *
       * @param title The title.
       */
  
      public synchronized void setTitle(String title) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _title = title;
      }
  
      /**
       * Set the title font.
       * The font names understood are those understood by
       * java.awt.Font.decode().
       *
       * @param name A font name.
       */
  
      public synchronized void setTitleFont(String name) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _titleFont = Font.decode(name);
          _titleFontMetrics = getFontMetrics(_titleFont);
      }
  
      /**
       * Specify whether the X axis is wrapped.
       * If it is, then X values that are out of range are remapped
       * to be in range using modulo arithmetic. The X range is determined
       * by the most recent call to setXRange() (or the most recent zoom).
       * If the X range has not been set, then use the default X range,
       * or if data has been plotted, then the current fill range.
       *
       * @param wrap If true, wrapping of the X axis is enabled.
       */
  
      public synchronized void setWrap(boolean wrap) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _wrap = wrap;
          if (!_xRangeGiven) {
              if (_xBottom > _xTop) {
                  // have nothing to go on.
                  setXRange(0, 0);
              } else {
                  setXRange(_xBottom, _xTop);
              }
          }
          _wrapLow = _xlowgiven;
          _wrapHigh = _xhighgiven;
      }
  
      /**
       * Set the label for the X (horizontal) axis.
       *
       * @param label The label.
       */
  
      public synchronized void setXLabel(String label) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _xlabel = label;
      }
  
      /**
       * Specify whether the X axis is drawn with a logarithmic scale.
       * If you would like to have the X axis drawn with a
       * logarithmic axis, then setXLog(true) should be called before
       * adding any data points.
       *
       * @param xlog If true, logarithmic axis is used.
       */
  
      public synchronized void setXLog(boolean xlog) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _xlog = xlog;
      }
  
      /**
       * Set the X (horizontal) range of the plot.  If this is not done
       * explicitly, then the range is computed automatically from data
       * available when the plot is drawn.  If min and max
       * are identical, then the range is arbitrarily spread by 1.
       *
       * @param min The left extent of the range.
       * @param max The right extent of the range.
       */
  
      public synchronized void setXRange(double min, double max) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _xRangeGiven = true;
          _xlowgiven = min;
          _xhighgiven = max;
          _setXRange(min, max);
      }
  
      /**
       * Set the label for the Y (vertical) axis.
       *
       * @param label The label.
       */
  
      public synchronized void setYLabel(String label) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _ylabel = label;
      }
  
      /**
       * Specify whether the Y axis is drawn with a logarithmic scale.
       * If you would like to have the Y axis drawn with a
       * logarithmic axis, then setYLog(true) should be called before
       * adding any data points.
       *
       * @param ylog If true, logarithmic axis is used.
       */
  
      public synchronized void setYLog(boolean ylog) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _ylog = ylog;
      }
  
      /**
       * Set the Y (vertical) range of the plot.  If this is not done
       * explicitly, then the range is computed automatically from data
       * available when the plot is drawn.  If min and max are identical,
       * then the range is arbitrarily spread by 0.1.
       *
       * @param min The bottom extent of the range.
       * @param max The top extent of the range.
       */
  
      public synchronized void setYRange(double min, double max) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _yRangeGiven = true;
          _ylowgiven = min;
          _yhighgiven = max;
          _setYRange(min, max);
      }
  
      /**
       * Write the current data and plot configuration to the
       * specified stream in PlotML syntax.  PlotML is an XML
       * extension for plot data.  The written information is
       * standalone, in that it includes the DTD (document type
       * definition).  This makes is somewhat verbose.  To get
       * smaller files, use the two argument version of write().
       * The output is buffered, and is flushed and
       * closed before exiting.  Derived classes should override
       * writeFormat and writeData rather than this method.
       *
       * @param out An output stream.
       */
  
      public void write(OutputStream out) {
          write(out, null);
      }
  
      /**
       * Write the current data and plot configuration to the
       * specified stream in PlotML syntax.  PlotML is an XML
       * scheme for plot data. The URL (relative or absolute) for the DTD is
       * given as the second argument.  If that argument is null,
       * then the PlotML PUBLIC DTD is referenced, resulting in a file
       * that can be read by a PlotML parser without any external file
       * references, as long as that parser has local access to the DTD.
       * The output is buffered, and is flushed and
       * closed before exiting.  Derived classes should override
       * writeFormat and writeData rather than this method.
       *
       * @param out An output stream.
       * @param dtd The reference (URL) for the DTD, or null to use the PUBLIC DTD.
       */
  
      public synchronized void write(OutputStream out, String dtd) {
          write(new OutputStreamWriter(out), dtd);
      }
  
      /**
       * Write the current data and plot configuration to the
       * specified stream in PlotML syntax.  PlotML is an XML
       * scheme for plot data. The URL (relative or absolute) for the DTD is
       * given as the second argument.  If that argument is null,
       * then the PlotML PUBLIC DTD is referenced, resulting in a file
       * that can be read by a PlotML parser without any external file
       * references, as long as that parser has local access to the DTD.
       * The output is buffered, and is flushed before exiting.
       *
       * @param out An output writer.
       * @param dtd The reference (URL) for the DTD, or null to use the PUBLIC DTD.
       */
  
      public synchronized void write(Writer out, String dtd) {
          // Auto-flush is disabled.
          PrintWriter output = new PrintWriter(new BufferedWriter(out), false);
          if (dtd == null) {
              output.println("<?xml version=\"1.0\" standalone=\"yes\"?>");
              output.println(
                      "<!DOCTYPE plot PUBLIC \"-//UC Berkeley//DTD PlotML 1//EN\"");
              output.println(
                      "    \"http://ptolemy.eecs.berkeley.edu/xml/dtd/PlotML_1.dtd\">");
          }
          else {
              output.println("<?xml version=\"1.0\" standalone=\"no\"?>");
              output.println("<!DOCTYPE plot SYSTEM \"" + dtd + "\">");
          }
          output.println("<plot>");
          output.println("<!-- Ptolemy plot, version " + PTPLOT_RELEASE
                  + " , PlotML format. -->");
          writeFormat(output);
          writeData(output);
          output.println("</plot>");
          output.flush();
          // NOTE: We used to close the stream, but if this is part
          // of an exportMoML operation, that is the wrong thing to do.
          // if (out != System.out) {
          //    output.close();
          // }
      }
  
      /**
       * Write plot data information to the specified output stream in PlotML.
       * In this base class, there is no data to write, so this method
       * returns without doing anything.
       *
       * @param output A buffered print writer.
       */
  
      public synchronized void writeData(PrintWriter output) {
      }
  
      /**
       * Write plot format information to the specified output stream in PlotML.
       * Derived classes should override this method to first call
       * the parent class method, then add whatever additional format
       * information they wish to add to the stream.
       *
       * @param output A buffered print writer.
       */
  
      public synchronized void writeFormat(PrintWriter output) {
          // NOTE: If you modify this, you should change the _DTD variable
          // accordingly.
          if (_title != null) output.println(
                  "<title>" + _title + "</title>");
          if (_xlabel != null) output.println(
                  "<xLabel>" + _xlabel + "</xLabel>");
          if (_ylabel != null) output.println(
                  "<yLabel>" + _ylabel + "</yLabel>");
          if (_xRangeGiven) output.println(
                  "<xRange min=\"" + _xlowgiven + "\" max=\""
                  + _xhighgiven + "\"/>");
          if (_yRangeGiven) output.println(
                  "<yRange min=\"" + _ylowgiven + "\" max=\""
                  + _yhighgiven + "\"/>");
          if (_xticks != null && _xticks.size() > 0) {
              output.println("<xTicks>");
              int last = _xticks.size() - 1;
              for (int i = 0; i <= last; i++) {
                  output.println("  <tick label=\""
                          + (String)_xticklabels.elementAt(i) + "\" position=\""
                          + (Double)_xticks.elementAt(i) + "\"/>");
              }
              output.println("</xTicks>");
          }
          if (_yticks != null && _yticks.size() > 0) {
              output.println("<yTicks>");
              int last = _yticks.size() - 1;
              for (int i = 0; i <= last; i++) {
                  output.println("  <tick label=\""
                          + (String)_yticklabels.elementAt(i) + "\" position=\""
                          + (Double)_yticks.elementAt(i) + "\"/>");
              }
              output.println("</yTicks>");
          }
          if (_xlog) output.println("<xLog/>");
          if (_ylog) output.println("<yLog/>");
          if (!_grid) output.println("<noGrid/>");
          if (_wrap) output.println("<wrap/>");
          if (!_usecolor) output.println("<noColor/>");
      }
  
      /**
       * Zoom in or out to the specified rectangle.
       * This method calls repaint().
       *
       * @param lowx The low end of the new X range.
       * @param lowy The low end of the new Y range.
       * @param highx The high end of the new X range.
       * @param highy The high end of the new Y range.
       */
  
      public synchronized void zoom(double lowx, double lowy,double highx, double highy) {
          setXRange(lowx, highx);
          setYRange(lowy, highy);
          repaint();
      }
  
  /*******************************************************************************
   *                                                                             *
   *                              PROTECTED METHODS                              *
   *                                                                             *
   ******************************************************************************/
  
      /**
       * Draw the axes using the current range, label, and title information.
       * If the second argument is true, clear the display before redrawing.
       * This method is called by paintComponent().  To cause it to be called
       * you would normally call repaint(), which eventually causes
       * paintComponent() to be called.
       * <p>
       * Note that this is synchronized so that points are not added
       * by other threads while the drawing is occurring.  This method
       * should be called only from the event dispatch thread, consistent
       * with swing policy.
       *
       * @param graphics The graphics context.
       * @param clearfirst If true, clear the plot before proceeding.
       */
  
      protected synchronized void _drawPlot(
              Graphics graphics, boolean clearfirst) {
          Rectangle bounds = getBounds();
          _drawPlot(graphics, clearfirst, bounds);
      }
  
      /**
       * Draw the axes using the current range, label, and title information,
       * at the size of the specified rectangle.
       * If the second argument is true, clear the display before redrawing.
       * This method is called by paintComponent().  To cause it to be called
       * you would normally call repaint(), which eventually causes
       * paintComponent() to be called.
       * <p>
       * Note that this is synchronized so that points are not added
       * by other threads while the drawing is occurring.  This method
       * should be called only from the event dispatch thread, consistent
       * with swing policy.
       *
       * @param graphics The graphics context.
       * @param clearfirst If true, clear the plot before proceeding.
       * @param drawRect A specification of the size.
       */
  
      protected synchronized void _drawPlot(Graphics graphics, boolean clearfirst, Rectangle drawRect) {
          // Ignore if there is no graphics object to draw on.
          if (graphics == null) return;
  
          graphics.setPaintMode();
  
          /* NOTE: The following seems to be unnecessary with Swing...
             if (clearfirst) {
             // NOTE: calling clearRect() here permits the background
             // color to show through, but it messes up printing.
             // Printing results in black-on-black title and axis labels.
             graphics.setColor(_background);
             graphics.drawRect(0, 0, drawRect.width, drawRect.height);
             graphics.setColor(Color.black);
             }
          */
  
          // If an error message has been set, display it and return.
          if (_errorMsg != null) {
              int fheight = _labelFontMetrics.getHeight() + 2;
              int msgy = fheight;
              graphics.setColor(Color.black);
              for (int i = 0; i < _errorMsg.length; i++) {
                  graphics.drawString(_errorMsg[i], 10, msgy);
                  msgy += fheight;
                  System.err.println(_errorMsg[i]);
              }
              return;
          }
  
          // Make sure we have an x and y range
          if (!_xRangeGiven) {
              if (_xBottom > _xTop) {
                  // have nothing to go on.
                  _setXRange(0, 0);
              }
              else {
                  _setXRange(_xBottom, _xTop);
              }
          }
          if (!_yRangeGiven) {
              if (_yBottom > _yTop) {
                  // have nothing to go on.
                  _setYRange(0, 0);
              }
              else {
                  _setYRange(_yBottom, _yTop);
              }
          }
  
          // If user specified a plot rectangle, compute
          // a working plot rectangle which lies inside the
          // drawRect at the user specified coordinates
          Rectangle workingPlotRectangle = null;
          if (_specifiedPlotRectangle != null) {
              workingPlotRectangle = new Rectangle(
                      Math.max(0, _specifiedPlotRectangle.x),
                      Math.max(0, _specifiedPlotRectangle.y),
                      Math.min(drawRect.width, _specifiedPlotRectangle.width),
                      Math.min(drawRect.height, _specifiedPlotRectangle.height) );
          }
  
  
          // Vertical space for title, if appropriate.
          // NOTE: We assume a one-line title.
          int titley = 0;
          int titlefontheight = _titleFontMetrics.getHeight();
  
          if (_title == null) {
              // NOTE: If the _title is null, then set it to the empty
              // string to solve the problem where the fill button overlaps
              // the legend if there is no title.  The fix here would
              // be to modify the legend printing text so that it takes
              // into account the case where there is no title by offsetting
              // just enough for the button.
              _title = "";
          }
          if (_title != null || _yExp != 0) {
              titley = titlefontheight + _topPadding;
          }
  
          // Number of vertical tick marks depends on the height of the font
          // for labeling ticks and the height of the window.
          Font previousFont = graphics.getFont();
          graphics.setFont(_labelFont);
          graphics.setColor(_foreground);        // foreground color not set here  --Rob.
          int labelheight = _labelFontMetrics.getHeight();
          int halflabelheight = labelheight/2;
  
          // Draw scaling annotation for x axis.
          // NOTE: 5 pixel padding on bottom.
          int ySPos = drawRect.height - 5;
          int xSPos = drawRect.width - _rightPadding;
          if (_xlog)
              _xExp = (int)Math.floor(_xtickMin);
          if (_xExp != 0 && _xticks == null) {
              String superscript = Integer.toString(_xExp);
              xSPos -= _superscriptFontMetrics.stringWidth(superscript);
              graphics.setFont(_superscriptFont);
              if (!_xlog) {
                  graphics.drawString(superscript, xSPos,
                          ySPos - halflabelheight);
                  xSPos -= _labelFontMetrics.stringWidth("x10");
                  graphics.setFont(_labelFont);
                  graphics.drawString("x10", xSPos, ySPos);
              }
              // NOTE: 5 pixel padding on bottom
              _bottomPadding = (3 * labelheight)/2 + 5;
          }
  
          // NOTE: 5 pixel padding on the bottom.
          if (_xlabel != null && _bottomPadding < labelheight + 5) {
              _bottomPadding = labelheight + 5;
          }
  
          // Compute the space needed around the plot, starting with vertical.
          // NOTE: padding of 5 pixels below title.
          if (workingPlotRectangle != null) {
              _uly = workingPlotRectangle.y;
          }
          else {
              _uly = titley + 5;
          }
          // NOTE: 3 pixels above bottom labels.
          if (workingPlotRectangle != null) {
              _lry = workingPlotRectangle.y + workingPlotRectangle.height;
          }
          else {
              _lry = drawRect.height-labelheight-_bottomPadding-3;
          }
          int height = _lry-_uly;
          _yscale = height/(_yMax - _yMin);
          _ytickscale = height/(_ytickMax - _ytickMin);
  
          ////////////////// vertical axis
  
          // Number of y tick marks.
          // NOTE: subjective spacing factor.
          int ny = 2 + height/(labelheight+10);
          // Compute y increment.
          double yStep = _roundUp((_ytickMax-_ytickMin)/(double)ny);
  
          // Compute y starting point so it is a multiple of yStep.
          double yStart = yStep*Math.ceil(_ytickMin/yStep);
  
          // NOTE: Following disables first tick.  Not a good idea?
          // if (yStart == _ytickMin) yStart += yStep;
  
          // Define the strings that will label the y axis.
          // Meanwhile, find the width of the widest label.
          // The labels are quantized so that they don't have excess resolution.
          int widesty = 0;
  
          // These do not get used unless ticks are automatic, but the
          // compiler is not smart enough to allow us to reference them
          // in two distinct conditional clauses unless they are
          // allocated outside the clauses.
          String ylabels[] = new String[ny];
  