Package com.googlecode.gchart.client

Source Code of com.googlecode.gchart.client.GChart

/*
* Copyright 2007,2008,2009 John C. Gunther
*
* Licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*
*/
package com.googlecode.gchart.client;

import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Window;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasHTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import java.util.ArrayList;
import java.util.Date;
import com.google.gwt.core.client.GWT;

/**
* A GChart can represent and display a line chart, a bar chart,
* a pie chart, an area chart, or a chart that contains arbitrary
* combinations of line, bar, pie, and/or area based curves.
*
* <p>
* For detailed examples, with screen shots, visit the
* <a href="package-summary.html#ChartGallery">
* Chart Gallery</a>.
*
* <p>
* For detailed instructions on how to integrate Client-side GChart
* into your GWT application, see
* <a href="package-summary.html#InstallingGChart">
* Installing Client-side GChart</a>.
*
* <p>
* <b>CSS Style Rule</b>
* <ul>
* .gchart-GChart { the GChart's primary top-level styles }
* </ul>
*
*
* It is sometimes more natural to consider certain CSS
* attributes as properties of a GChart Java object. So, GChart
* supports "CSS convenience methods" that let you (optionally) use
* Java to specify GChart CSS attributes such as
* <tt>border-color</tt> and <tt>background-color</tt>. See
* {@link #USE_CSS USE_CSS} for a detailed description of these
* CSS convenience methods--which won't interfere with standard
* CSS-based specifications if you never invoke them.
*
*
**/

public class GChart extends Composite implements HasClickHandlers {

  
   /**
    ** Defines the location of a data point's annotation or hover
    ** annotation (which can be defined by either plain text, HTML,
    ** or a widget) relative to the location of that point's
    ** symbol.  The "Field Summary"
    ** section below lists all available annotation locations.
    ** <p>
    **
    ** The default annotation location is {@link
    ** AnnotationLocation#SOUTH SOUTH} for annotations and
    ** is symbol-type-dependent for hover annotations. See the
    ** <tt>setHoverLocation</tt> method for list of these defaults.
    **
    ** <p>
    **
    ** You can further adjust the position of a point's
    ** annotation (or hover annotation) by specifying non-zero
    ** positional shifts via the <tt>setAnnotationXShift</tt>
    ** and <tt>setAnnotationYShift</tt> (or via the
    ** <tt>setHoverXShift</tt>, <tt>setHoverYShift</tt>),
    ** and <tt>setHoverAnnotationSymbolType</tt> methods for
    ** hover annotations).
    ** <p>
    **
    ** @see Curve.Point#setAnnotationLocation Point.setAnnotationLocation
    ** @see Curve.Point#setAnnotationXShift Point.setAnnotationXShift
    ** @see Curve.Point#setAnnotationYShift Point.setAnnotationYShift
    ** @see Symbol#setHoverLocation Symbol.setHoverLocation
    ** @see Symbol#setHoverAnnotationSymbolType
    ** Symbol.setHoverAnnotationSymbolType
    ** @see Symbol#setHoverXShift Symbol.setHoverXShift
    ** @see Symbol#setHoverYShift Symbol.setHoverYShift
    ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
    **
    **/
   public static final class AnnotationLocation {
// non-public tagging-only locations used by ANCHOR_MOUSE_* symbol types
      static final AnnotationLocation AT_THE_MOUSE =
        new AnnotationLocation(0,0);
      static final AnnotationLocation AT_THE_MOUSE_SNAP_TO_X =
        new AnnotationLocation(0,0);
      static final AnnotationLocation AT_THE_MOUSE_SNAP_TO_Y =
        new AnnotationLocation(0,0);
      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned so as to be centered on the symbol
       ** used to represent the point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation CENTER =
        new AnnotationLocation(0,0);

      private static final AnnotationLocation north =
         new AnnotationLocation(0,-1);
      private static final AnnotationLocation west =
        new AnnotationLocation(-1, 0);
      private static final AnnotationLocation south =
          new AnnotationLocation(0, 1);
     
      /**
       ** Specifies that a point's annotation (label) should be
       ** placed just above, and centered horizontally on,
       ** vertical bars that grow down from a horizontal
       ** baseline, and just below, and centered horizontally on,
       ** vertical bars that grow up from a horizontal baseline.
       **
       ** <p>
       **
       ** This another name for
       ** <tt>AnnotationLocation.NORTH</tt>. Its sole purpose is
       ** to clarify/document the behavior of this location type
       ** when used in conjunction with curves that employ
       ** <tt>VBAR_BASELINE_*</tt> symbol types.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see SymbolType#VBAR_BASELINE_CENTER SymbolType.VBAR_BASELINE_CENTER
       **
       **/
      public static final AnnotationLocation
          CLOSEST_TO_HORIZONTAL_BASELINE = north;
     
      /**
       ** Specifies that a point's annotation (label) should be
       ** placed just to the right of, and centered vertically
       ** on, horizontal bars that grow left from a vertical
       ** baseline, and just to the left of, and centered
       ** vertically on, horizontal bars that grow right from a
       ** vertical baseline.
       **
       ** <p>
       **
       ** This another name for
       ** <tt>AnnotationLocation.WEST</tt>. Its sole purpose is
       ** to clarify/document the behavior of this location type
       ** when used in conjunction with curves that employ the
       ** <tt>HBAR_BASELINE_*</tt> symbol types.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see SymbolType#HBAR_BASELINE_CENTER SymbolType.HBAR_BASELINE_CENTER
       **
       **/
      
      public static final AnnotationLocation
         CLOSEST_TO_VERTICAL_BASELINE = west;
     
      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the right of, and vertically
       ** centered on, the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation
       **/
      public static final AnnotationLocation EAST =
         new AnnotationLocation(1, 0);

      /**
       ** Specifies that a point's annotation (label) should be
       ** placed just below, and centered horizontally on,
       ** vertical bars that grow down from a horizontal
       ** baseline, and just above, and centered horizontally on,
       ** vertical bars that grow up from a horizontal baseline.
       **
       ** <p>
       **
       ** This another name for
       ** <tt>AnnotationLocation.SOUTH</tt>. Its sole purpose is
       ** to clarify/document the behavior of this location type
       ** when used in conjunction with curves that employ
       ** <tt>VBAR_BASELINE_*</tt> symbol types.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see SymbolType#VBAR_BASELINE_CENTER SymbolType.VBAR_BASELINE_CENTER
       **
       **/
      public static final AnnotationLocation
          FARTHEST_FROM_HORIZONTAL_BASELINE = south;
     
      /**
       ** Specifies that a point's annotation (label) should be
       ** placed just to the left of, and centered vertically on,
       ** horizontal bars that grow left from a vertical
       ** baseline, and just to the right of, and centered
       ** vertically on, horizontal bars that grow right from a
       ** vertical baseline.
       **
       ** <p>
       **
       ** This another name for
       ** <tt>AnnotationLocation.EAST</tt>. Its sole purpose is
       ** to clarify/document the behavior of this location type
       ** when used in conjunction with curves that employ the
       ** <tt>HBAR_BASELINE_*</tt> family of symbol types.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see SymbolType#HBAR_BASELINE_CENTER SymbolType.HBAR_BASELINE_CENTER
       **
       **/
      public static final AnnotationLocation
          FARTHEST_FROM_VERTICAL_BASELINE = EAST;
     

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just inside, and centered on, the
       ** arc side of a pie slice.
       ** <p>
       **
       ** You can move a pie slice's annotation a specific number
       ** of pixels radially away from (or towards) the pie
       ** center by passing a positive (or negative) argument to
       ** the associated <tt>Point</tt>'s
       ** <tt>setAnnotationXShift</tt> method.
       **
       ** <p> This is pie-friendly synonym for, and when used
       ** with non-pie symbol types will behave exactly the same
       ** as, <tt>AnnotationLocation.NORTH</tt>
       **     
       ** @see #OUTSIDE_PIE_ARC OUTSIDE_PIE_ARC
       ** @see #ON_PIE_ARC ON_PIE_ARC
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see AnnotationLocation#NORTH NORTH
       **/
      public static final AnnotationLocation INSIDE_PIE_ARC = north;

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just above, and horizontally centered on,
       ** the symbol used to represent the point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation NORTH = north;


      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the right of and above,
       ** the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation
       **/
      public static final AnnotationLocation NORTHEAST =
        new AnnotationLocation(1, -1);

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the left of and above,
       ** the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation
       **/
      public static final AnnotationLocation NORTHWEST =
        new AnnotationLocation(-1, -1);

     
      /**
       ** Specifies that a point's annotation (label) should
       ** be centered on the center-point of the
       ** arc side of a pie slice.
       ** <p>
       **
       ** You can move a pie slice's annotation a specific number
       ** of pixels radially away from (or towards) the pie
       ** center by passing a positive (or negative) argument to
       ** the associated <tt>Point</tt>'s
       ** <tt>setAnnotationXShift</tt> method.
       **
       **
       **
       ** <p> This is pie-friendly synonym for, and when used
       ** with non-pie symbol types will behave exactly the same
       ** as, <tt>AnnotationLocation.CENTER</tt>
       **
       ** @see #OUTSIDE_PIE_ARC OUTSIDE_PIE_ARC
       ** @see #INSIDE_PIE_ARC INSIDE_PIE_ARC
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see AnnotationLocation#CENTER CENTER
       **
       **/
      public static final AnnotationLocation ON_PIE_ARC = CENTER;

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just outside, and centered on, the
       ** arc side of a pie slice.
       ** <p>
       **
       ** You can move a pie slice's annotation a specific number
       ** of pixels radially away from (or towards) the pie
       ** center by passing a positive (or negative) argument to
       ** the associated <tt>Point</tt>'s
       ** <tt>setAnnotationXShift</tt> method.
       **
       ** <p> This is pie-friendly synonym for, and when used
       ** with non-pie symbol types will behave exactly the same
       ** as, <tt>AnnotationLocation.SOUTH</tt>
       **
       ** @see #INSIDE_PIE_ARC INSIDE_PIE_ARC
       ** @see #ON_PIE_ARC ON_PIE_ARC
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see AnnotationLocation#SOUTH SOUTH
       **/
      public static final AnnotationLocation OUTSIDE_PIE_ARC = south;

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just below, and horizontally centered on,
       ** the symbol used to represent the point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation SOUTH = south;

     
      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the right of and below,
       ** the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation SOUTHEAST =
        new AnnotationLocation(1, 1);
      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the left of and below,
       ** the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation SOUTHWEST =
        new AnnotationLocation(-1, 1);

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the left of, and vertically
       ** centered on, the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation WEST = west;

     
     
      // these multiply the width and height of the annotation and
      // the symbol it is attached to in order to define the
      // center of the annotation (see equations in later code),
      // and thus the upper left corner anchoring point.
      private int heightMultiplier;
      private int widthMultiplier;
      private AnnotationLocation(int widthMultiplier,
                             int heightMultiplier) {
        validateMultipliers(widthMultiplier, heightMultiplier);
        this.widthMultiplier = widthMultiplier;
        this.heightMultiplier = heightMultiplier;
      }
      // retrieves a static location given its multipliers
      private static AnnotationLocation getAnnotationLocation(
         int widthMultiplier, int heightMultiplier) {
        final AnnotationLocation[][] locationMap = {
         {NORTHWEST, NORTH, NORTHEAST},
         {WEST, CENTER, EAST},
         {SOUTHWEST, SOUTH, SOUTHEAST}};
   // assumes both multiplier are -1, 0, or 1  
        AnnotationLocation result =
         locationMap[heightMultiplier+1][widthMultiplier+1];
       return result;               
      }
                                                  
      // Negative width or height "turn the symbol inside-out",
      // requiring a corresponding "reflection" of annotation
      // location (only needed for baseline-based bar symbols)
      static AnnotationLocation transform(AnnotationLocation a,
                                          int signWidth,
                                          int signHeight) {
        AnnotationLocation result = a;
        if (signWidth < 0 || signHeight < 0)
           result = getAnnotationLocation(
                signWidth*a.widthMultiplier,
                signHeight*a.heightMultiplier);

        return result;
      }
      // These define the alignment of the label within it's
      // containing 1 x 1 Grid. For example, if this
      // containing grid is to the left of the labeled
      // symbol (widthMultiplier==-1) the horizontal
      // alignment will be ALIGN_RIGHT, so as to bring the
      // contained label flush against the left edge of the
      // labeled symbol.
      HasHorizontalAlignment.HorizontalAlignmentConstant
            getHorizontalAlignment() {
         HasHorizontalAlignment.HorizontalAlignmentConstant result;
         if (widthMultiplier == -1)
            result = HasHorizontalAlignment.ALIGN_RIGHT;
         else if (widthMultiplier == 0)
            result = HasHorizontalAlignment.ALIGN_CENTER;
         else if (widthMultiplier == 1)
            result = HasHorizontalAlignment.ALIGN_LEFT;
         else
            throw new IllegalStateException(
               "Invalid widthMultiplier: " + widthMultiplier +
               " 1, 0, or -1 were expected.");
         return result;
      }

      /* Given the x-coordinate at the center of the symbol
       * that this annotation annotates, the annotation's
       * width, and the symbol's width, this method returns
       * the x-coordinate of the upper left corner of
       * this annotation.
       */
      int getUpperLeftX(double x, double w, double symbolW) {
         int result = (int) Math.round(x +
           (widthMultiplier * (w + symbolW) - w)/2.);
         return result;
      }

      /* analogous to getUpperLeftX, except for the y-coordinate */
      int getUpperLeftY(double y, double h, double symbolH) {
         int result = (int) Math.round(y +
           (heightMultiplier * (h + symbolH) - h)/2.);
         return result;
      }
      // analogous to getHorizontalAlignment 
      HasVerticalAlignment.VerticalAlignmentConstant
            getVerticalAlignment() {
         HasVerticalAlignment.VerticalAlignmentConstant result;
         if (heightMultiplier == -1)
            result = HasVerticalAlignment.ALIGN_BOTTOM;
         else if (heightMultiplier == 0)
            result = HasVerticalAlignment.ALIGN_MIDDLE;
         else if (heightMultiplier == 1)
            result = HasVerticalAlignment.ALIGN_TOP;
         else
            throw new IllegalStateException(
               "Invalid heightMultiplier: " + heightMultiplier +
               " -1, 0, or 1 were expected.");
         return result;
      }


       /*
        * This method returns the annotation location whose
        * "attachment point" keeps the annotation either
        * completely outside, centered on, or completely inside
        * (depending on if the heightMultiplier of this annotation
        * is 1, 0, or -1) the point on the pie's circumference
        * associated with the given angle.
        * <p>
        *
        * The use of heightMultiplier rather than widthMultiplier
        * is somewhat arbitrary, but was chosen so that the
        * NORTH, CENTER, and SOUTH annotation locations have the
        * same interpretation for a pie slice whose bisecting
        * radius points due south (due south is the default initial
        * pie slice orientation) and for a 1px x 1px BOX_CENTER
        * type symbol positioned at the due south position on the
        * pie's circumference. As the pie-slice-arc-bisection
        * point moves clockwise around the pie perimeter, the
        * attachment point (except for vertically-centered
        * annotations, which remain centered on the pie arc) also
        * moves clockwise, but in discrete jumps (e.g. from
        * NORTH, to NORTHEAST, to EAST, to SOUTHEAST, to SOUTH,
        * etc. for annotations inside the pie) so the annotation
        * remains appropriately attached to the center of the
        * slice's arc as the angle changes.
        *
        */
     AnnotationLocation decodePieLocation(double thetaMid) {
        // a sin or cos that is small enough so that the
        // associated angle is horizontal (for sines) or vertical
        // (for cosines) enough to warrant use of a "centered"
        // annotation location.
       final double LOOKS_VERTICAL_OR_HORIZONTAL_DELTA = 0.1;
       double sinTheta = Math.sin(thetaMid);
       double cosTheta = Math.cos(thetaMid);
       int pieTransformedWidthMultiplier = heightMultiplier *
          ((cosTheta < -LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? -1 :
          ((cosTheta > LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? 1 : 0));
       int pieTransformedHeightMultiplier = heightMultiplier *
          ((sinTheta < -LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? 1 :
          ((sinTheta > LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? -1 : 0));
            
       return getAnnotationLocation(pieTransformedWidthMultiplier,
                                    pieTransformedHeightMultiplier);
        
     }
     
   } // end of class AnnotationLocation


  /**
   ** Represents an axis of the chart, for example, the x,
   ** y, or y2 axis. An axis consists of the axis itself,
   ** along with its tick marks, tick labels and gridlines.
   **
   ** @see XAxis XAxis
   ** @see YAxis YAxis
   ** @see Y2Axis Y2Axis
   ** @see #getXAxis getXAxis
   ** @see #getYAxis getYAxis
   ** @see #getY2Axis getY2Axis
   **
   **
   **/
   public abstract class Axis {
     protected boolean isHorizontalAxis; // true for X, false for Y
     protected int ticksId;              // sys curve representing ticks
     protected int gridlinesId;          // sys curve representing gridlines
     protected int axisId;               // sys curve representing axis line
     protected int axisPosition;         // +/-1 for right/left axes
     protected TickLocation tickLocation = DEFAULT_TICK_LOCATION;
     private int nCurvesVisibleOnAxis = 0// # of developer curves on axis.
                                       // (count does not include system or
                                       // invisible curves)

     void incrementCurves() {nCurvesVisibleOnAxis++;}
     void decrementCurves() {nCurvesVisibleOnAxis--;}
    
     protected class AxisLimits {
        double min; double max; // in user-defined model units
        AxisLimits(double min, double max) {
           this.min = min;
           this.max = max;
        }
        boolean equals(AxisLimits al) {
           boolean result =  (al.min == min && al.max == max);
           return result;
        }
     }

     // different initial curr, prev ==> "limits have changed" state
     private AxisLimits currentLimits = new AxisLimits(
        Double.MAX_VALUE, -Double.MAX_VALUE);
     private AxisLimits previousLimits = new AxisLimits(
        -Double.MAX_VALUE, Double.MAX_VALUE);
    
     private Widget axisLabel;
     protected int axisLabelThickness = GChart.NAI;
     private boolean hasGridlines = false;
     protected int tickCount = DEFAULT_TICK_COUNT;
// axes auto-scale whenever min or max are NaN.
     protected double axisMax = Double.NaN;
     protected double axisMin = Double.NaN;
// this symbol facilitates rendering of gridlines & axes
     protected String tickLabelFontColor = DEFAULT_TICK_LABEL_FONT_COLOR;
// In CSS font-size pixels. These define the height of each
// character; our code relies on the rule of thumb that
// character width is approximately 3/5th this height to
// obtain a reasonably tight upper bound on tick label widths.
     protected int tickLabelFontSize = DEFAULT_TICK_LABEL_FONTSIZE;
     protected String tickLabelFontStyle = DEFAULT_TICK_LABEL_FONT_STYLE;
     protected String tickLabelFontWeight = DEFAULT_TICK_LABEL_FONT_WEIGHT;

     protected String tickLabelFormat = DEFAULT_TICK_LABEL_FORMAT;
     protected int tickLabelThickness = GChart.NAI;
     protected int tickLabelPadding = 0;
     protected int ticksPerLabel = 1;
     protected int ticksPerGridline = 1;
     protected int tickLength = DEFAULT_TICK_LENGTH;
    
     // this symbol facilitates rendering of labeled tick-marks
     protected int tickThickness = DEFAULT_TICK_THICKNESS;
        
     // is axis itself visible (has no impact ticks or their labels)
     boolean axisVisible = true;
    
     /**
      * Adds a tick on this axis at the specified position.
      * Note that explicitly adding a single tick via this method
      * will eliminate any implicitly generated ticks associated with the
      * <tt>setTickCount</tt> method.
      * <p>
      * The label associated with this tick will be generated by
      * applying the format specified via <tt>setTickLabelFormat</tt>
      * to the specified position.
      * <p>
      * This is a convenience method equivalent to
      * <tt>addTick(tickPosition, thisAxis.formatAsTickLabel(tickPosition), GChart.NAI,
      * GChart.NAI)</tt>. See {@link #addTick(double,String,int,int)
      * addTick(tickPosition,tickLabel,widthUpperBound,heightUpperBound)}
      * for details.
      *
      * @param tickPosition the position, in model units,
      *   along this axis at which this tick is displayed.
      *   For example, if the axis range goes from 0 to 100,
      *   a tick at position 50 would appear in the middle of
      *   the axis.
      *
      * @see #clearTicks clearTicks
      * @see #addTick(double,String) addTick(double,String)
      * @see #addTick(double,String,int,int) addTick(double,String,int,int)
      * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      * @see #formatAsTickLabel formatAsTickLabel
      * @see #setTickCount setTickCount
      * @see #setTickLabelFormat setTickLabelFormat
      * @see #setTickLabelFontStyle setTickLabelFontStyle
      * @see #setTickLabelFontColor setTickLabelFontColor
      * @see #setTickLabelFontWeight setTickLabelFontWeight
      * @see #setTickLabelFontSize setTickLabelFontSize
      *
      */
     public void addTick(double tickPosition) {
        addTick(tickPosition, formatAsTickLabel(tickPosition))
     }
     // adds a labeled tick mark via this Axis' special system tick curve
     private void addTickAsPoint(double tickPosition, String tickLabel,
                                 Widget tickWidget, int widthUpperBound,
                                 int heightUpperBound) {

       Curve c = getSystemCurve(ticksId);
       if (isHorizontalAxis
          c.addPoint(tickPosition, axisPosition*Double.MAX_VALUE);
       else
          c.addPoint(axisPosition*Double.MAX_VALUE, tickPosition);

       // unlabeled tick--we are done, so return to save time
       if (null == tickLabel && null == tickWidget)
          return;
      
       //add an annotation representing the tick label
       Curve.Point p = c.getPoint();
       if (isHorizontalAxis) {
          // below tick on X, above it on (the future) X2
          p.setAnnotationLocation(
            (axisPosition < 0) ? AnnotationLocation.SOUTH :
                                 AnnotationLocation.NORTH);
          if (tickLabelPadding != 0) // padding < 0 is rare but allowed
             p.setAnnotationYShift(axisPosition*tickLabelPadding);
          // else stick with default of 0 y-shift

       }
       else {
          // to left of tick mark on Y, to right of it on Y2
          p.setAnnotationLocation(
               (axisPosition < 0) ? AnnotationLocation.WEST :
                                    AnnotationLocation.EAST);
          if (tickLabelPadding != 0)
            p.setAnnotationXShift(axisPosition*tickLabelPadding);
          // else stick with default of 0 x-shift
       }

      
       if (null != tickLabel)
          p.setAnnotationText(tickLabel, widthUpperBound, heightUpperBound);
       else if (null != tickWidget)
          p.setAnnotationWidget(tickWidget, widthUpperBound, heightUpperBound);
      
       p.setAnnotationFontSize(getTickLabelFontSize());
       p.setAnnotationFontStyle(getTickLabelFontStyle());
       p.setAnnotationFontColor(getTickLabelFontColor());
       p.setAnnotationFontWeight(getTickLabelFontWeight());

     }
     /**
      * Adds a tick at the specified position with the specified
      * label on this axis, whose width and height are within
      * the specified upper-bounds.
      *
      * <p>
      * Note that explicitly adding a single tick via this method
      * will eliminate any auto-generated ticks associated with the
      * <tt>setTickCount</tt> method.
      *
      * <p>
      * Use this method to specify unusually spaced
      * tick marks with labels that do not directly
      * reflect the position (for example, for a logarithmic axis,
      * or for a bar chart with special keyword-type labels, or
      * a time axis that places date and time on two separate lines).
      *
      * @param tickPosition the position, in model units, along
      *   this axis at which the tick is displayed.
      *   For example, if the axis range goes from 0 to 1,
      *   a tick at position 0.5 would appear in the middle of
      *   the axis.
      *  
      *  @param tickLabel the label for this tick.  HTML is
      *  supported in tick labels, but it must be prefixed by
      *  <tt>&lt;html&gt</tt>.  See the {@link
      *  Curve.Point#setAnnotationText(String,int,int)
      *  setAnnotationText} method for more information.
      *
      *  @param widthUpperBound an upper bound on the width of
      *  the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
      *  get GChart to estimate this width for you. See the
      *  <tt>setAnnotationText</tt> method for more information.
      *
      *  @param heightUpperBound an upper bound on the height of
      *  the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
      *  get GChart to estimate this height for you. See the
      *  <tt>setAnnotationText</tt> method for more information.
      *
      * @see #clearTicks clearTicks
      * @see #addTick(double) addTick(double)
      * @see #addTick(double,String) addTick(double,String)
      * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      * @see #setTickCount setTickCount
      * @see #setTickLabelFormat setTickLabelFormat
      * @see #setTickLabelFontSize setTickLabelFontSize
      * @see #setTickLabelFontStyle setTickLabelFontStyle
      * @see #setTickLabelFontColor setTickLabelFontColor
      * @see #setTickLabelFontWeight setTickLabelFontWeight
      * @see Curve.Point#setAnnotationText(String,int,int)
      *      setAnnotationText
      * @see Curve.Point#setAnnotationWidget setAnnotationWidget
      *     
      */
     public void addTick(double tickPosition, String tickLabel,
                         int widthUpperBound,
                         int heightUpperBound) {
       chartDecorationsChanged = true;
       if (GChart.NAI != tickCount) { // clear out any auto-generated ticks
         Curve cTicks = getSystemCurve(ticksId)
         cTicks.clearPoints();
         tickCount = GChart.NAI;
       }
       addTickAsPoint(tickPosition, tickLabel, null, widthUpperBound,
                heightUpperBound);      
     }

     /**
      * Adds a tick at the specified position with the specified
      * label on this axis.
      * <p>
      *
      * This is a convenience method equivalent to
      * <tt>addTick(tickPosition, tickLabel, GChart.NAI,
      * GChart.NAI)</tt>. Most applications can usually just
      * use this convenience method. See {@link #addTick(double,String,int,int)
      * addTick(tickPosition,tickLabel,
      * widthUpperBound,heightUpperBound)} for the fine print.
      *
      * @param tickPosition the position, in model units, along
      *   this axis at which the tick is displayed.
      *  
      * @param tickLabel the plain text or
      * (<tt>&lt;html&gt</tt>-prefixed) HTML defining the tick's
      * label.
      *
      * @see #addTick(double,String,int,int) addTick(double,String,int,int)
      * @see #addTick(double,Widget) addTick(double,Widget)
      *
      */
     public void addTick(double tickPosition,
                         String tickLabel) {
          addTick(tickPosition, tickLabel, GChart.NAI, GChart.NAI);
     }
    
     /**
      *  Adds a widget-defined tick label at the specified
      *  position, whose width and height are within
      *  the specified upper-bounds.
      *
      *
      *  <p>
      **
      ** This method is similar to
      ** <tt>addTick(double,String,int,int)</tt> except that it
      ** uses a widget, rather than a string, to define the
      ** tick's label. Although the string-based method is faster
      ** on first chart rendering, and uses less memory, the
      ** widget-based method allows you to change the label
      ** independently of the chart--potentially bypassing (or
      ** speeding up) expensive chart updates later on.
      **
      ** <p>
      **
      ** You might use a widget-based tick label to pop up a
      ** dialog that allows the user to edit the parameters
      ** defining the axis (min, max, etc.) whenever they click
      ** on one of the tick labels on that axis, to define
      ** hovertext that appears when the user mouses over
      ** a tick label, to use images for your tick labels, etc.
      **
      * @param tickPosition the position, in model units, along
      *   this axis at which the tick is displayed.
      *   For example, if the axis range goes from 0 to 1,
      *   a tick at position 0.5 would appear in the middle of
      *   the axis.
      *  
      *  @param tickWidget the label for this tick, as defined
      *  by any GWT Widget.
      *
      *  @param widthUpperBound an upper bound on the width of
      *  the widget, in pixels. If this and the next
      *  parameter are omitted, GChart will use
      *  <tt>DEFAULT_WIDGET_WIDTH_UPPERBOUND</tt>.
      * 
      *  @param heightUpperBound an upper bound on the height of
      *  the widget, in pixels. If this and the previous
      *  parameter are omitted, GChart will use <tt>
      *  DEFAULT_WIDGET_HEIGHT_UPPERBOUND</tt>
      *
      *  @see #addTick(double,Widget) addTick(double,Widget)
      *  @see #addTick(double,String,int,int) addTick(double,String,int,int)
      *  @see Curve.Point#setAnnotationWidget setAnnotationWidget
      *  @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
      *  @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
      **/
     public void addTick(double tickPosition,
                         Widget tickWidget,
                         int widthUpperBound,
                         int heightUpperBound) {
       chartDecorationsChanged = true;
       if (GChart.NAI != tickCount) { // clear out any auto-generated ticks
         Curve cTicks = getSystemCurve(ticksId)
         cTicks.clearPoints();
         tickCount = GChart.NAI;
       }
       addTickAsPoint(tickPosition, null, tickWidget, widthUpperBound,
                heightUpperBound);
       
     }

     /**
      *  Adds a Widget-defined tick label at the specified
      *  position. Convenience method equivalent to
      *  <tt>addTick(tickPosition, tickWidget,
      *  DEFAULT_WIDGET_WIDTH_UPPERBOUND,
      *  DEFAULT_WIDGET_HEIGHT_UPPERBOUND)</tt>.
      * 
      * @param tickPosition the position, in model units, along
      *   this axis at which the tick is displayed.
      *   For example, if the axis range goes from 0 to 1,
      *   a tick at position 0.5 would appear in the middle of
      *   the axis.
      *  
      * @param tickWidget the label for this tick, as defined
      *  by any GWT Widget.
      * 
      * @see #addTick(double,Widget,int,int)
      * addTick(double,Widget,int,int)
      * 
      */
     public void addTick(double tickPosition,
                         Widget tickWidget) {
        addTick(tickPosition, tickWidget,
                DEFAULT_WIDGET_WIDTH_UPPERBOUND,
                DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
     }
     /**
      *
      * Removes all ticks from this axis. Specifically,
      * erases any ticks that were explicitly specified via
      * <tt>addTick</tt>, and also sets the tick count to 0.
      * <p>
      *
      * @see #setTickCount setTickCount
      * @see #addTick(double) addTick(double)
      * @see #addTick(double,String) addTick(double,String)
      * @see #addTick(double,String,int,int) addTick(double,String,int,int)
      * @see #addTick(double,Widget) addTick(double,Widget)
      * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      *
      */
     public void clearTicks() {
        chartDecorationsChanged = true;
        tickCount = GChart.NAI;
        Curve c = getSystemCurve(ticksId);
        c.clearPoints();
     }


     /**
      * Converts a pixel, client-window coordinate position along this
      * axis into the model units associated with this axis.
      * <p>
      *
      * For example, if the client coordinate associated with
      * this axis' midpoint were passed to this method, it would return
      * <tt>(getAxisMin() + getAxisMax())/2.0</tt>.
      * <p>
      *
      * <small> Note that the client/model coordinate mapping used is as
      * of the last <tt>update</tt>. Before the first <tt>update</tt>,
      * this method returns <tt>GChart.NaN</tt>.  This method also
      * invokes either <tt>getAbsoluteTop</tt> (for the y or y2 axis) or
      * <tt>getAbsoluteLeft</tt> (for the x axis),
      * and these GWT methods return 0 if the chart isn't actually
      * rendered within the browser. So, results likely won't be useful
      * to you until after the page containing your chart becomes
      * visible to the user. Since most applications are expected to
      * invoke this method in response to the user mousing over the
      * page, these requirements should usually be satisfied.  </small>
      * <p>
      *
      * <small> Saurabh Hirani in <a href=
      * "http://groups.google.com/group/Google-Web-Toolkit/msg/80301715acb6f719">
      * this GWT Forum post</a> and in GChart <a
      * href="http://code.google.com/p/gchart/issues/detail?id=21">issue
      * #22</a> most recently suggested the need for client to model
      * coordinate mapping. Client to
      * model conversion was requested earlier in GChart <a
      * href="http://code.google.com/p/gchart/issues/detail?id=9">issue
      * #9</a> from <a href="http://yoxel.com">yoxel.com</a>.
      * </small>
      *
      * @param clientCoordinate a pixel-based coordinate that defines
      * the dimension associated with this axis in the standard
      * client window coordinates of GWT.
      *
      * @return the location defined by the client-coordinate argument,
      * but converted into the model units associated
      * with this axis.
      *
      * @see #getMouseCoordinate getMouseCoordinate
      * @see #modelToClient modelToClient
      * @see #pixelToModel pixelToModel
      * @see #modelToPixel modelToPixel
      *
      *
      */
     public abstract double clientToModel(int clientCoordinate);
    
     // these are used in formatting tick positions into tick labels:
     private NumberFormat numberFormat =
        NumberFormat.getFormat(DEFAULT_TICK_LABEL_FORMAT);
     private DateTimeFormat dateFormat =
        DateTimeFormat.getShortDateTimeFormat();
     private final int NUMBER_FORMAT_TYPE = 0;
     private final int DATE_FORMAT_TYPE = 1;
     private final int LOG10INVERSE_FORMAT_TYPE = 2;
     private final int LOG2INVERSE_FORMAT_TYPE = 3;
     private int tickLabelFormatType = NUMBER_FORMAT_TYPE;
     /**
     *
     * Applies this axis' tick label format to format a given value.
     *
     * @return the value formated as per this axis' currently specified
     * tick label format.
     *
     * @see  #setTickLabelFormat(String) setTickLabelFormat
     *
     */
     public String formatAsTickLabel(double value) {
       String result = null;
       switch (tickLabelFormatType) {
         case DATE_FORMAT_TYPE:
           Date transDate = new Date((long) value);
           result = dateFormat.format(transDate);
           break;
         case LOG10INVERSE_FORMAT_TYPE:
           value = Math.pow(10., value);
           result = numberFormat.format(value);
           break;
         case LOG2INVERSE_FORMAT_TYPE:
           value = Math.pow(2., value);
           result = numberFormat.format(value);
           break;
         default
           result = numberFormat.format(value);
           break;
       }
      
       return result;
     }
     /**
      * @deprecated
      *
      * Equivalent to the better-named formatAsTickLabel.

      * <p>
      *
      * @see #formatAsTickLabel formatAsTickLabel
      *
      */
     public String formatNumberAsTickLabel(double value) {
        return formatAsTickLabel(value);
     }

     /** Returns the previously specified label of this axis.
      **
      ** @return the Widget used as the label of this axis
      **
      ** @see #setAxisLabel setAxisLabel
      **
      */
     public Widget getAxisLabel() {
       return axisLabel;
     }
    
     /** Returns the thickness of the axis-label-holding region
      ** adjacent to the region allocated for this axis' tick labels.
      ** <p>
      **
      ** Note that if the axis label is <tt>null</tt> (the
      ** default) then this method always returns 0, since
      ** in that case no rectangular region will be allocated
      ** for the axis label.
      ** <p>
      **
      ** @return the thickness of the axis-label-holding
      ** region, in pixels.
      **
      ** @see #setAxisLabelThickness setAxisLabelThickness
      **
      */
     public int getAxisLabelThickness() {
        int result = 0;
// Base class implementation is for y axes (x-axis will override).
        final int EXTRA_CHARWIDTH = 2; // 1-char padding on each side
        final int DEF_CHARWIDTH = 1; // when widget has no text
        if (null == getAxisLabel())
           result = 0;
        else if (GChart.NAI != axisLabelThickness)
           result = axisLabelThickness;
        else if (getAxisLabel() instanceof HasHTML) {
           int charWidth = htmlWidth(
              ((HasHTML) (getAxisLabel())).getHTML());
           result = (int) Math.round((charWidth + EXTRA_CHARWIDTH) *
                      getTickLabelFontSize() *
                     TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
        }
        else if (getAxisLabel() instanceof HasText) {
           String text = ((HasText) (getAxisLabel())).getText();
           result = (int) Math.round((EXTRA_CHARWIDTH +
                      ((null==text)?0:text.length())) *
                      getTickLabelFontSize() *
                      TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
        }
        else // non-text widget. Not a clue, just use def width
           result = (int) Math.round(
                    (DEF_CHARWIDTH + EXTRA_CHARWIDTH) *
                     getTickLabelFontSize() *
                     TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
        return result;
     }
     /**
      ** Returns the maximum value displayed on this axis.
      ** If the explicitly specified maximum value is
      ** undefined (<tt>Double.NaN</tt>) the maximum value returned
      ** by this function is calculated as the maximum of
      ** all of the values either displayed on this axis via
      ** points on a curve, or explicitly specified via tick
      ** positions.
      **
      ** @return maximum value visible on this axis, in
      ** "model units" (arbitrary, application-specific,
      ** units)
      **
      ** @see #setAxisMax setAxisMax
      ** @see #getDataMin getDataMin
      ** @see #getDataMax getDataMax
      **/
     public double getAxisMax() {
          
        if (!(axisMax!=axisMax)) { // x!=x is a faster isNaN
           return axisMax;
        }
        else if (GChart.NAI != tickCount) {
          return getDataMax();
        }
        else {
           return Math.max(getDataMax(), getTickMax());          
        }
     }
     /**
      **
      ** Returns the minimum value displayed on this axis.
      ** If the minimum value is undefined (<tt>Double.NaN</tt>) the
      ** minimum value returned by this function is the
      ** minimum of all of the values either displayed on
      ** this axis via points on a curve, or explicitly specified
      ** via tick positions.
      **
      ** @return minimum value visible on this axis, in
      ** "model units" (arbitrary, application-specific,
      ** units)
      **
      ** @see #setAxisMin setAxisMin
      **/
     public double getAxisMin() {
        if (!(axisMin!=axisMin)) { // x!=x is a faster isNaN
           return axisMin; // explicitly set
        }
        else if (GChart.NAI != tickCount) {
           return getDataMin();
        }
        else
           return Math.min(getDataMin(), getTickMin());          
        }
     }

   /** Is axis line visible on the chart? Note that
    ** this property only determines the visibility of the axis line
    ** itself. It does not control the visibility of the
    ** tick marks or tick labels along this axis.
    ** <p>
    **
    ** @return true if the axis line is visible, false otherwise.
    **
    ** @see #setAxisVisible setAxisVisible
    **
    **/
     public boolean getAxisVisible() {
       return axisVisible;
     }


    
     /** Returns the maximum data value associated with values
     ** represented on this axis. For example, for the left
     ** y-axis, this would be the largest y-value of all points
     ** contained in curves that are displayed on the left y-axis.
     **
     ** @return the maximum value associated with values
     **   mapped onto this axis.
     **
     ** @see #getDataMin getDataMin
     ** @see #getAxisMax getAxisMax
     ** @see #getAxisMin getAxisMin
     **
      */
    public abstract double getDataMax();
     /** Returns the minimum data value associated with values
     ** represented on this axis. For example, for the left
     ** y-axis, this would be the smallest y-value of all points
     ** contained in curves that are displayed on the left y-axis.
     **
     ** @return the minimum value associated with values
     **   mapped onto this axis.
     **
     ** @see #getDataMax getDataMax
     ** @see #getAxisMax getAxisMax
     ** @see #getAxisMin getAxisMax
     **
      */
    public abstract double getDataMin();
    
     /** Returns the gridline setting previously made with
      ** <tt>setHasGridlines</tt>.
      **
      ** @return true if gridlines have been enabled, false if not.
      **
      ** @see #setHasGridlines setHasGridlines
      **
      **/
     public boolean getHasGridlines() {
        return hasGridlines;
     }

     /**
      * Returns the coordinate along this axis that
      * is associated with the last "GChart-tracked" mouse
      * location.
      * <p>
      *
      * The coordinate returned is in the "scale" associated
      * with the axis. For example, if the axis mininum is
      * 0 and the maximum is 100, and the mouse is at the
      * axis midpoint, this method would return 50.
      * <p>
      *
      * The main intended use for this method is to allow you to create
      * points that, if they have x and y coordinates defined by calling
      * this method on appropriate axes, will be positioned on the chart
      * at the last GChart-tracked mouse location.
      * <p>
      *
      * As the user moves their mouse over the chart, GChart watches
      * those mouse moves and updates it's currently "tracked" mouse
      * location.  That internally maintained position is the basis for
      * the value returned by this method.  Note that the actual,
      * physical, mouse cursor position could differ from this
      * GChart-tracked position because:
      *
      * <p>
      *
      * <ol>
      *
      *   <li>The mouse has moved off the chart, and it's
      *       GChart-tracked location has become undefined
      *       (this method returns <tt>Double.NaN</tt> in that case)
      *
      *   <li>You have invoked <tt>setHoverTouchingEnabled(false)</tt>
      *       which means that mouse moves are no longer tracked,
      *       so the last GChart-tracked mouse location will
      *       be the last position that the user clicked on.
      *
      *   <li>You have popped up a modal dialog that "eats" mouse
      *       moves so GChart no longer sees them. In that case,
      *       the GChart-tracked mouse location is the location the
      *       mouse was at when the modal dialog popped up.
      *
      *    <li>You are mousing over the opened hover widget (popup).
      *    Note that, to prevent the user from accidentally "touching"
      *    nearby points while interacting with the opened
      *    hover widget, GChart ignores mouse moves over
      *    the opened hover widget.
      *
      *    <li>Other, similar, reasons.
      *
      * </ol>
      * <p>
      *
      * In other words, this routine tells you where, for
      * hit testing and hover selection feedback purposes,
      * GChart considers the mouse to be, not the actual
      * physical location of the mouse. Despite the potential
      * for differences, in most cases, with the default
      * setting of <tt>setHoverTouchingEnabled(true)</tt>,
      * and when you are not over the opened hover widget,
      * you can use the value returned by this method
      * as if it represented the physical mouse location.
      * <p>
      *
      * For an example that uses this method to create
      * points at the current mouse location within a very
      * simple line chart editor, see <a
      * href="package-summary.html#GChartExample22a">the
      * Chart Gallery's GChartExample22a</a><p>
      *
      *
      * @return the coordinate, projected along this axis, in
      *   the scale defined by this axis, representing the
      *   position GChart has currently "tracked" the mouse to,
      *   or <tt>Double.NaN</tt> if GChart has tracked the mouse
      *   right off the edge of the chart.
      *
      * @see #clientToModel clientToModel
      * @see #modelToClient modelToClient
      * @see #pixelToModel pixelToModel
      * @see #modelToPixel modelToPixel
      * @see GChart#setHoverTouchingEnabled setHoverTouchingEnabled
      *  
      */
     public abstract double getMouseCoordinate();
     /**
      * Returns the number of visible curves displayed on this axis.
      * <p>
      *
      * @return the number of visible curves on this axis, or <tt>0</tt> if
      * there are no visible curves on this axis.
      *
      * @see Axis#setVisible setVisible
      *
      */
     public int getNCurvesVisibleOnAxis() { return nCurvesVisibleOnAxis; }    
     /**
      ** Returns the number of ticks on this axis.
      **
      ** @return the number of ticks on this axis.
      **
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #clearTicks clearTicks
      **
      **/
     public int getTickCount() {
        int result = tickCount;
        if (GChart.NAI == tickCount) {
          Curve c = getSystemCurve(ticksId);
          result = c.getNPoints();
        }
        return result;

     }
     /**
      ** Returns the CSS font-weight specification to be used
      ** by this axis' tick labels.
      **
      ** @return font-weight of this axis' tick labels
      **
      ** @see #setTickLabelFontWeight setTickLabelFontWeight
      **/
     public String getTickLabelFontWeight() {
        return tickLabelFontWeight;
     }
     /**
      ** Returns the color of the font used to display the
      **    text of the tick labels on this axis.
      **  
      **  
      ** @return CSS color string defining the color of the text of
      **    the tick labels for this axis.
      **
      ** @see #setTickLabelFontColor setTickLabelFontColor
      **
      ** @see #DEFAULT_TICK_LABEL_FONT_COLOR DEFAULT_TICK_LABEL_FONT_COLOR
      **
      **
      **
      **/
     public String getTickLabelFontColor() {
        return tickLabelFontColor;
     }

     /**
      ** Returns the font-style of the font used to render tick
      ** labels on this axis (typically either "italic" or
      ** "normal")
      **
      ** @return the CSS font-style in which tick labels of this axis
      **   are rendered.
      **
      ** @see #setTickLabelFontStyle setTickLabelFontStyle
      **/
     public String getTickLabelFontStyle() {
        return tickLabelFontStyle;
     }
     /** Returns the CSS font size, in pixels, used for tick labels
      ** on this axis.
      **
      ** @return the tick label font size in pixels
      **
      ** @see #setTickLabelFontSize setTickLabelFontSize
      **/
     public int getTickLabelFontSize() {
        return tickLabelFontSize;
     }

     /**
     ** Returns the tick label numeric format string for this
     ** axis.
     **
     ** @return numeric format used to generate tick labels.
     **
     ** @see #setTickLabelFormat setTickLabelFormat
     **
     **/
    public String getTickLabelFormat() {
       return tickLabelFormat;
    }
     /**
     ** Returns the amount of padding (blank space) between the
     ** ticks and their labels.<p>
     **
     ** @return amount of padding between ticks and their labels,
     ** in pixels.
     **
     ** @see #setTickLabelPadding setTickLabelPadding
     **
     **/
    public int getTickLabelPadding() {
       return tickLabelPadding;
    }
    // Does real work of public getTickLabelThickness; flag saves time
    // during repeated calls made in updateChartDecorations.
    int getTickLabelThickness(boolean needsPopulation) {
      int maxLength = 0;
      int result;
      if (tickLabelThickness != GChart.NAI)
        result = tickLabelThickness;
      else { // use an heuristic to estimate thickness           
       if (needsPopulation) maybePopulateTicks();
       Curve c = getSystemCurve(ticksId);
       int nTicks = c.getNPoints();
       for (int i=0; i < nTicks; i++) {
         String tt = c.getPoint(i).getAnnotationText();
         if (null != tt)
            maxLength = Math.max(maxLength,
                                 Annotation.getNumberOfCharsWide(tt));
       }
       result = (int) Math.round(maxLength * tickLabelFontSize *
                TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
      }
      return result;
    }

    /** Returns the thickness of the band adjacent to
     ** this axis that GChart will
     ** allocate to hold this axis' tick labels.
     ** <p>
     **
     ** @return width of band, in pixels, GChart will reserve
     **   for this axis' tick labels.
     **  
     ** @see #setTickLabelThickness setTickLabelThickness
     **
     **/
    public int getTickLabelThickness() {
       int result = getTickLabelThickness(true);
       return result;
    }  

    /**
     ** Returns the ratio of the number of ticks to the number of
     ** ticks that have an associated gridline.
     **
     ** @return number of ticks per gridline for this axis
     **
     ** @see #setTicksPerGridline setTicksPerGridline
     **
     **/
    public int getTicksPerGridline() {
       return ticksPerGridline;
    }
    /**
     ** Returns the ratio of the number of ticks to the number of
     ** labeled ticks.
     **
     ** @return number of ticks per label.
     **
     ** @see #setTicksPerLabel setTicksPerLabel
     **
     **/
    public int getTicksPerLabel() {
      return ticksPerLabel;
    }

     /**
     * Returns the length of ticks for this axis.
     *
     * @return the length of this axis' ticks, in pixels.
     *
     * @see #setTickLength setTickLength
     */
    public int getTickLength() {
       return tickLength;
    }

    // GChart adds a pixel to even, centered, tick lengths (only
    // odd-length HTML ticks can be exactly centered on 1px axis)
    int getActualTickLength() {
       int result = tickLength;
       if (TickLocation.CENTERED == tickLocation &&
           0 == (tickLength % 2&& tickLength > 0)
          result++;
       return result;
    }

     /**
      * Returns relative location of ticks on this axis.
      * <p>
      *
      * @see #setTickLocation setTickLocation
      *
      * @return <tt>TickLocation.INSIDE</tt>,
      *         <tt>TickLocation.OUTSIDE</tt>, or
      *         <tt>TickLocation.CENTERED</tt>
      *
      */
     public TickLocation getTickLocation() {
        return tickLocation;
     }
   
   
    /** Returns the amount of space along the axis reserved for
     *  the tick marks themselves, in pixels.
     *  <p>
     *
     *  This equals the length of
     *  the part of the tick that is outside of the plot area.
     *
     * @see #setTickLength setTickLength
     * @see #setTickLabelPadding setTickLabelPadding
     * @see #setTickLocation setTickLocation
     *
     * @return the space GChart will allocate just outside the
     * axis to hold any tick marks.
     *
     */
   
    public int getTickSpace() {
       int result;
       if (TickLocation.CENTERED == tickLocation)
         result = (tickLength+1)/2// round up to nearest pixel
       else if (TickLocation.OUTSIDE == tickLocation)
          result = tickLength;
       else // INSIDE
          result = 0;

       return result;
    }

   
    /**
     * Returns the thickness of ticks for this axis.
     *
     * @return the thickness of this axis' ticks, in pixels.
     *
     * @see #setTickThickness setTickThickness
     * @see #getTickLength getTickLength
     */
    public int getTickThickness() {
       return tickThickness;
    }


     /**
      * Converts a coordinate position in the model units associated
      * with this axis into a corresponding coordinate position
      * expressed in standard GWT client-window pixel coordinates.
      *
      * <p>
      *
      * For example, consider a completely undecorated chart (no axes,
      * tick labels, legend keys, etc.) that exactly fills a
      * 1000px wide client window, and whose x-axis min and max
      * are 0 and 100. Then <tt>getXAxis().modelToClient(50)</tt> would
      * return <tt>500</tt>.
      * <p>
      *
      * <small> Note that the client/model coordinate mapping used is as
      * of the last <tt>update</tt>. Before the first <tt>update</tt>,
      * this method returns <tt>GChart.NaN</tt>.  This method also
      * invokes either <tt>getAbsoluteTop</tt> (for the y or y2 axis) or
      * <tt>getAbsoluteLeft</tt> (for the x axis),
      * and these GWT methods return 0 if the chart isn't actually
      * rendered within the browser. So, results likely won't be useful
      * to you until after the page containing your chart becomes
      * visible to the user. Since most applications are expected to
      * invoke this method in response to the user mousing over the
      * page, these requirements should usually be satisfied.  </small>
      *
      * @param modelCoordinate the position along this axis defined
      *  in the model units associated with this axis.
      *
      * @return a pixel-based coordinate that defines
      * the position associated with the argument in the standard
      * pixel, client window, coordinates of GWT.
      *
      * @see #getMouseCoordinate getMouseCoordinate
      * @see #clientToModel clientToModel
      * @see #pixelToModel pixelToModel
      * @see #modelToPixel modelToPixel
      *
      *
      */
     public abstract double modelToClient(double modelCoordinate);

     /**
      * Converts a coordinate position in the model units associated
      * with this axis into a corresponding coordinate position
      * expressed in GChart's decorated chart pixel coordinates.
      * <p>
      *
      * These
      * coordinates have their origin at the upper left corner
      * of the decorated GChart, and x pixel-coordinates that increase
      * as you move right, and y pixel-coordinates that increase
      * as you move down. They are related to GWT's standard
      * client window coordinates via the following equations:
      *
      * <pre>
      *   xClient = plotPanel.getAbsoluteLeft()
      *             - Window.getScrollLeft()
      *             + xPixel;
      *   yClient = plotPanel.getAbsoluteTop()
      *             - Window.getScrollTop()
      *             + yPixel;
      * </pre>
      * <p>
      *
      * In the above <tt>plotPanel</tt> is an internal
      * <tt>AbsolutePanel</tt>
      * GChart creates to hold the entire, decorated, chart. Apart from
      * borders and such applied to the GChart as a whole, its
      * absolute top and left positions should be the same as
      * those of the GChart itself.
      * <p>
      *
      * <i>Tip:</i> In applications that continuously track mouse moves
      * over the chart, and where absolute and scroll positions cannot
      * change, you can gain a significant performance boost by
      * computing the difference between pixel and client coordinates
      * once (<tt>modelToPixel(axisMin)-modelToClient(axisMin)</tt>)
      * and then adding that difference to the client coordinates
      * to get the pixel coordinates, and then using
      * <tt>pixelToModel</tt>, instead of using <tt>clientToModel</tt>
      * directly, which must repeatedly call GWT's scroll and absolute
      * position methods.
      *
      * <p>
      *
      *
      * For example, for a completely undecorated chart (no tick labels,
      * legend keys, etc.) the plot area takes up the entire chart. In
      * that case, if the pixel units of the plot area range from
      * <tt>0...100</tt> along this axis, and the model coordinates range
      * from <tt>0...10</tt> along this axis, then
      * <tt>modelToPixel(modelCoordinate)</tt> returns
      * <tt>10*modelCoordinate</tt><p>
      *
      * The model/pixel mapping is as of the last <tt>update</tt>;
      * this method returns <tt>Double.NaN</tt> before the first
      * <tt>update</tt>. Note that, unlike <tt>clientToModel</tt>
      * and <tt>modelToClient</tt>, the GChart does <i>not</i>
      * need to be actually rendered within the browser for you to
      * use this method--a call to update is sufficient.
      * <p>
      *
      * <i>Tip:</i> If you need to access this mapping before
      * the first real update, you can explicitly specify the min and
      * max of this axis via <tt>setAxisMin</tt> and
      * <tt>setAxisMax</tt>, and then call <tt>update</tt> before adding
      * any curves to the chart (which, since the chart is empty, should
      * be very fast). This approach will allow you to convert between
      * model and pixel coordinates before the first real update, and
      * before the chart is rendered in the browser.
      * <p>
      *
      *
      *
      * @param modelCoordinate a position on this axis expressed
      *  in the model units associated with this axis.
      *
      * @return the distance,
      * in pixels, from the left edge (for the x axis) or top
      * edge (for the y or y2 axis) of
      * the decorated chart to the given position on this axis.
      *
      * @see #getMouseCoordinate getMouseCoordinate
      * @see #clientToModel clientToModel
      * @see #modelToClient modelToClient
      * @see #modelToPlotAreaPixel modelToClient
      * @see #pixelToModel pixelToModel
      *
      *
      */
     public abstract double modelToPixel(double modelCoordinate);


     /**
      * Converts a coordinate position in the model units associated
      * with this axis into a corresponding coordinate position
      * expressed in GChart's plot area pixel coordinates.
      * <p>
      *
      * These
      * coordinates have their origin at the upper left corner
      * of the plot area, and x pixel-coordinates that increase
      * as you move right, and y pixel-coordinates that increase
      * as you move down.
      * <p>
      *
      * The plot area is the rectangular region bounded by the
      * chart's axes, and with a size specified via
      * <tt>setChartSize</tt>, where the chart's curves are
      * typically displayed.
      * <p>
      *
      * Apart from a shift in the origin of the pixel coordinates
      * used, this method works just like <tt>modelToPixel</tt>;
      * see that method for additional details, tips, and
      * restrictions.
      *
      * @param modelCoordinate a position on this axis expressed
      *  in the model units associated with this axis.
      *
      * @return the distance,
      * in pixels, from the left edge (for the x axis) or top
      * edge (for the y or y2 axis) of
      * the plot area to the given position on this axis.
      *
      * @see #getMouseCoordinate getMouseCoordinate
      * @see #plotAreaPixelToModel plotAreaPixelToModel
      * @see #modelToPixel modelToPixel
      * @see #setChartSize setChartSize
      *
      */
     public abstract double modelToPlotAreaPixel(double modelCoordinate);

    
     /**
      * Converts a coordinate position in GChart's decorated
      * chart pixel
      * coordinates into the model units associated with this axis.
      * <p>
      *
      * GChart's decorated chart pixel
      * coordinates have their origin at the upper left corner
      * of the decorated GChart, and x pixel-coordinates that increase
      * as you move right, and y pixel-coordinates that increase
      * as you move down. They are related to GWT's standard
      * client window coordinates via the following equations:
      *
      * <pre>
      *   xClient = plotPanel.getAbsoluteLeft()
      *             - Window.getScrollLeft()
      *             + xPixel;
      *   yClient = plotPanel.getAbsoluteTop()
      *             - Window.getScrollTop()
      *             + yPixel;
      * </pre>
      * <p>
      *
      *
      * In the above <tt>plotPanel</tt> is an internal
      * <tt>AbsolutePanel</tt>
      * GChart creates to hold the entire, decorated, chart. Apart from
      * borders and such applied to the GChart as a whole, its
      * absolute top and left positions should be the same as
      * those of the GChart itself.
      * <p>
      *
      * For example, for a completely undecorated chart (no tick labels,
      * legend keys, etc.) the plot area takes up the entire chart. In
      * that case, if the pixel units of the plot area range from
      * <tt>0...100</tt> along this axis, and the model coordinates range
      * from <tt>0...10</tt> along this axis, then
      * <tt>pixelToModel(pixelCoordinate)</tt> returns
      * <tt>pixelCoordinate/10.</tt><p>
      *
      * The model/pixel mapping is as of the last <tt>update</tt>;
      * this method returns <tt>Double.NaN</tt> before the first
      * <tt>update</tt>. Note that, unlike <tt>clientToModel</tt>
      * and <tt>modelToClient</tt>, the GChart does <i>not</i>
      * need to be actually rendered within the browser for you to
      * use this method.
      * <p>
      *
      * <i>Tip:</i> If you need to access this mapping before
      * the first real update, you can explicitly specify the min and
      * max of this axis via <tt>setAxisMin</tt> and
      * <tt>setAxisMax</tt>, and then call <tt>update</tt> before adding
      * any curves to the chart (which, since the chart is empty, should
      * be very fast). This approach will allow you to convert between
      * model and pixel coordinates before the first real update, and
      * before the chart is rendered in the browser.
      * <p>
      *
      * @param pixelCoordinate the distance,
      * in pixels, from the left edge (for the x axis) or top
      * edge (for the y or y2 axis) of
      * the decorated chart to a point on this axis.
      *
      * @return that same position on this axis expressed in the
      *  the model units associated with this axis.
      * 
      * @see #getMouseCoordinate getMouseCoordinate
      * @see #clientToModel clientToModel
      * @see #modelToClient modelToClient
      * @see #modelToPixel modelToPixel
      * @see #plotAreaPixelToModel plotAreaPixelToModel
      *
      */
     public abstract double pixelToModel(int pixelCoordinate);


     /**
      * Converts a coordinate position in GChart's plot area
      * pixel
      * coordinates into the model units associated with this axis.
      * <p>
      *
      * GChart's plot area pixel
      * coordinates have their origin at the upper left corner
      * of the plot area, and x pixel-coordinates that increase
      * as you move right, and y pixel-coordinates that increase
      * as you move down.
      * <p>
      *
      * The plot area is the rectangular region bounded by the
      * chart's axes, and with a size specified via
      * <tt>setChartSize</tt>, where the chart's curves are
      * typically displayed.
      * <p>
      *
      * Apart from a shift in the origin of the pixel coordinates
      * used, this method works just like <tt>pixelToModel</tt>;
      * see that method for additional details, tips, and
      * restrictions.
      *
      * @param pixelCoordinate the distance,
      * in pixels, from the left edge (for the x axis) or top
      * edge (for the y or y2 axis) of
      * the plot area to a point on this axis.
      *
      * @return that same position on this axis expressed in the
      *  the model units associated with this axis.
      * 
      * @see #modelToPlotAreaPixel modelToPlotAreaPixel
      * @see #pixelToModel pixelToModel
      * @see #setChartSize setChartSize
      *
      */
     public abstract double plotAreaPixelToModel(int pixelCoordinate);


    
  
     /** Specifies the label of this axis.
      ** <p>
      **
      ** This label will be positioned just outside of, and
      ** centered lengthwise on, the region adjacent to
      ** this axis that GChart reserves for this axis' tick labels.
      **
      ** @param axisLabel a Widget to use as the label of this axis.
      **
      ** @see #getAxisLabel getAxisLabel
      ** @see #setTickLabelThickness setTickLabelThickness
      ** @see #setAxisLabelThickness setAxisLabelThickness
      **
      */
    
     public void setAxisLabel(Widget axisLabel) {
        this.axisLabel = axisLabel;
        chartDecorationsChanged = true;
     }
    
     /**
      * Convenience method equivalent to
      * <tt>setAxisLabel(new HTML(html))</tt>
      *
      * @param html HTML text used to define the axis label
      *
      * @see #setAxisLabel(Widget) setAxisLabel(Widget)
      */
      public void setAxisLabel(String html) {
       setAxisLabel(new HTML(html));
      }

     /** Sets the thickness of the axis-label-holding region
      ** adjacent to the region allocated for tick labels.<p>
      **
      ** The axis label widget will be centered in this region.
      ** Choose a thickness large enough to hold the largest
      ** font size you want users to be able to zoom up to
      ** without the axis label spilling over into
      ** adjacent regions.
      ** <p>
      **
      ** If the axis label thickness is <tt>GChart.NAI</tt> (the
      ** default), and the widget defining the axis label
      ** implements <tt>HasHTML</tt> (or <tt>HasText</tt>) then
      ** GChart uses a thickness based on the estimated number of
      ** non-tag characters in the first <tt>&lt;br&gt;</tt> or
      ** <tt>&lt;li&gt;</tt>
      ** delimited line for y-axis labels, and based on the
      ** estimated number of (<tt>&lt;br&gt;</tt> or
      ** <tt>&lt;li&gt;</tt> delimited)
      ** text lines for x-axis labels.<p>
      **
      ** Note that if the axis label is <tt>null</tt> (its
      ** default setting) then no space is allocated for the axis
      ** label, regardless of this thickness setting.
      ** <p>
      **
      ** @param thickness the thickness of the axis-label-holding
      ** region, in pixels, or <tt>GChart.NAI</tt> to use
      ** GChart's character-based default thickness estimates.
      **
      ** @see #getAxisLabelThickness getAxisLabelThickness
      ** @see #setAxisLabel setAxisLabel
      */
     public void setAxisLabelThickness(int thickness) {
       axisLabelThickness = thickness;
       chartDecorationsChanged = true;
     }

     /**
      ** Specifies the maximum value visible on this axis.
      ** <p>
      **
      ** Aspects of the chart rendered beyond this maximum will
      ** be clipped if the chart's <tt>clipToPlotArea</tt>
      ** property is <tt>true</tt>.
      **
      ** <p>
      **
      ** If <tt>Double.NaN</tt> is specified, this maximum
      ** is auto-determined as described in <tt>getAxisMax</tt>.
      **
      ** <p> <i>Performance tip:</i> Using auto-determined axis
      ** limits (via <tt>Double.NaN</tt>) forces GChart, at the
      ** next update, to re-render many chart elements whenever
      ** the min or max data value displayed on this axis
      ** changes.  These (often expensive) re-renderings can be
      ** avoided by using explicitly specified axis limits
      ** whenever possible. <p>
      **
      ** @param max maximum value visible on this axis, in "model units"
      ** (arbitrary, application-specific, units) or <tt>Double.NaN</tt>
      ** (the default value) to use an auto-determined maximum.
      **
      ** @see #getAxisMax getAxisMax
      ** @see #getDataMin getDataMin
      ** @see #getDataMax getDataMax
      ** @see GChart#setClipToPlotArea setClipToPlotArea
      **
      **/
     public void setAxisMax(double max) {
        chartDecorationsChanged = true;
        this.axisMax = max;
     }
     /**
      ** Specifies the minimum value of this axis.
      ** <p>
      **
      ** Aspects of the chart rendered at positions before this
      ** minimum
      ** value will be clipped if the chart's
      ** <tt>clipToPlotArea</tt> property is <tt>true</tt>.
      ** <p>
      **
      ** If <tt>Double.NaN</tt> is specified, this minimum
      ** is auto-determined as described in <tt>getAxisMin</tt>.
      **
      ** <p> <i>Performance tip:</i> Using auto-determined axis
      ** limits (via <tt>Double.NaN</tt>) forces GChart, at the
      ** next update, to re-render many chart elements whenever
      ** the min or max data value displayed on this axis
      ** changes.  These (often expensive) re-renderings can be
      ** avoided by using explicitly specified axis limits
      ** whenever possible. <p>
      **
      ** @param min minimum value visible on this axis, in "model units"
      ** (arbitrary, application-specific, units), or Double.NaN
      ** (the default) to use an auto-determined minimum.
      **
      ** @see #getAxisMin getAxisMin
      ** @see #getDataMin getDataMin
      ** @see #getDataMax getDataMax
      **
      **/
     public void setAxisMin(double min) {
// min can change axis label width ==> changes position of plot area
        chartDecorationsChanged = true;
        this.axisMin = min;
     }

  /**
   ** Defines if this axis is visible. Note that
   ** this property only defines the visibility of the axis line
   ** itself, it does not control the visibility of
   ** tick marks or tick labels associated with the axis.
   **
   ** <p>
   ** <i>Tip:</i>Tick marks can be made invisible by using
   ** <tt>setTickThickness</tt> to set the tick thickness
   ** to 0. Tick labels can be made invisible by using
   ** <tt>setTickLabelFontColor</tt> to set the tick label
   ** color to the chart's background color.
   ** <p>
   **
   ** @param axisVisible false to hide axis, true to show it.
   **
   ** @see #setTickThickness setTickThickness
   ** @see #setTickLabelFontColor setTickLabelFontColor
   ** @see #getAxisVisible getAxisVisible
   **/
   public void setAxisVisible(boolean axisVisible) {
      chartDecorationsChanged = true;
      this.axisVisible = axisVisible;
   }  
    
     /**
      ** Specifies if this axis should have gridlines. When an
      ** axis has gridlines, tick marks with indexes <tt>0, N,
      ** 2*N,...</tt> where <tt>N</tt> is the value of this axis'
      ** <tt>ticksPerGridline</tt> property, are in effect
      ** extended across the entire chart.
      **
      ** @param hasGridlines true to display gridlines,
      ** false (the default) to not display them.
      **
      ** @see #getHasGridlines getHasGridlines
      ** @see #setTicksPerGridline setTicksPerGridline
      **
      **/
     public void setHasGridlines(boolean hasGridlines) {
        chartDecorationsChanged = true;
        this.hasGridlines = hasGridlines;
     }
     /** Sets the number of ticks to be placed on this axis. The
      ** default tick count is 10. Ticks are always evenly
      ** spaced across the entire axis, unless explicitly
      ** specified via <tt>addTick</tt>.
      ** <p>
      **
      ** Note that setting the tick count overrides (erases)
      ** any ticks explicitly specified via <tt>addTick</tt>.
      **
      ** @param tickCount the number of ticks for this axis.
      **
      ** @see #getTickCount getTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double, String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #setTickLabelFormat setTickLabelFormat
      ** @see #setTickLabelFontSize setTickLabelFontSize
      ** @see #setTickLabelFontStyle setTickLabelFontStyle
      ** @see #setTickLabelFontColor setTickLabelFontColor
      ** @see #setTickLabelFontWeight setTickLabelFontWeight
      **
      **/
     public void setTickCount(int tickCount) {
        chartDecorationsChanged = true;
        getSystemCurve(ticksId).clearPoints(); // eliminate user specified ticks
        this.tickCount = tickCount;
     }
     /**
      ** Specifies the weight of the font used in this axis' tick
      ** labels.
      **
      ** @param cssWeight the weight of the font, such as bold,
      **    normal, light, 100, 200, ... 900, for tick labels.
      **
      ** @see #getTickLabelFontWeight getTickLabelFontWeight 
      ** @see #setTickLabelFormat setTickLabelFormat
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #setTickLabelFontStyle setTickLabelFontStyle
      ** @see #setTickLabelFontColor setTickLabelFontColor
      ** @see #setTickLabelFontSize setTickLabelFontSize
      ** @see #DEFAULT_TICK_LABEL_FONT_WEIGHT DEFAULT_TICK_LABEL_FONT_WEIGHT
      **/
     public void setTickLabelFontWeight(String cssWeight) {
        chartDecorationsChanged = true;
        // assure that any existing ticks are updated with new weight
        Curve c = getSystemCurve(ticksId);
        int nPoints = c.getNPoints();
        for (int i = 0; i < nPoints; i++)
           c.getPoint(i).setAnnotationFontWeight(cssWeight);
        tickLabelFontWeight = cssWeight;
     }
     /**
      ** Specifies the color of the font used to render tick labels
      ** for this axis.
      **
      ** <p>
      ** For more information on standard CSS color
      ** specifications see the discussion in
      ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
      ** <p>
      **       
      ** @param cssColor color of the font used to display this
      **    axis' tick labels, in standard CSS format.
      **
      ** @see #getTickLabelFontColor getTickLabelFontColor 
      ** @see #setTickLabelFormat setTickLabelFormat
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #setTickLabelFontStyle setTickLabelFontStyle
      ** @see #setTickLabelFontWeight setTickLabelFontWeight
      ** @see #setTickLabelFontSize setTickLabelFontSize
      **/
     public void setTickLabelFontColor(String cssColor) {
        chartDecorationsChanged = true;
        Curve c = getSystemCurve(ticksId);
        int nPoints = c.getNPoints();
        for (int i = 0; i < nPoints; i++)
           c.getPoint(i).setAnnotationFontColor(cssColor);
        tickLabelFontColor = cssColor;
     }

     /**
      ** Specifies the CSS font-style of this
      ** axis' tick labels.
      **
      ** @param cssStyle any valid CSS font-style, namely,
      **   normal, italic, oblique, or inherit.
      **
      ** @see #getTickLabelFontStyle getTickLabelFontStyle 
      ** @see #setTickLabelFormat setTickLabelFormat
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #setTickLabelFontColor setTickLabelFontColor
      ** @see #setTickLabelFontWeight setTickLabelFontWeight
      ** @see #setTickLabelFontSize setTickLabelFontSize
      ** @see #DEFAULT_TICK_LABEL_FONT_STYLE
      ** DEFAULT_TICK_LABEL_FONT_STYLE
      **/
     public void setTickLabelFontStyle(String cssStyle) {
        chartDecorationsChanged = true;
        Curve c = getSystemCurve(ticksId);
        int nPoints = c.getNPoints();
        for (int i = 0; i < nPoints; i++)
           c.getPoint(i).setAnnotationFontStyle(cssStyle);
        tickLabelFontStyle = cssStyle;
     }

     /**
      ** Sets the CSS font size for tick labels on this
      ** axis, in pixels.
      **
      ** @param tickLabelFontSize the font size of tick labels
      **   displayed on this axis.
      **
      ** @see #getTickLabelFontSize getTickLabelFontSize
      ** @see #setTickLabelFormat setTickLabelFormat
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #setTickLabelFontStyle setTickLabelFontStyle
      ** @see #setTickLabelFontColor setTickLabelFontColor
      ** @see #setTickLabelFontWeight setTickLabelFontWeight
      ** @see GChart#DEFAULT_TICK_LABEL_FONTSIZE DEFAULT_TICK_LABEL_FONTSIZE
      **
      **/

     public void setTickLabelFontSize(int tickLabelFontSize) {
        chartDecorationsChanged = true;
        Curve c = getSystemCurve(ticksId);
        int nPoints = c.getNPoints();
        for (int i = 0; i < nPoints; i++)
           c.getPoint(i).setAnnotationFontSize(tickLabelFontSize);
        this.tickLabelFontSize = tickLabelFontSize;
     }

     /**
     * Specifies a format string to be used in
     * converting the numeric values associated with each
     * tick on this axis into tick labels.  This string must
     * follow the conventions of the number format patterns
     * used by the GWT <a
     * href="http://google-web-toolkit.googlecode.com/svn/javadoc/1.4/com/google/gwt/i18n/client/NumberFormat.html">
     * NumberFormat</a> class, <i>with three
     * exceptions:</i>
     * <p>
     * <ol>
     *
     *  <li><i>Log10 inverse prefix</i>: If the string begins
     *  with the prefix <tt>=10^</tt> the value is replaced with
     *  <tt>pow(10.,value)</tt> and the so-transformed value is
     *  then formatted using the part of the format string that
     *  comes after this prefix, which must be a valid GWT
     *  <tt>NumberFormat</tt> pattern (e.g. "##.##").
     *  <p>
     *  For an example of how to use this prefix to create a
     *  semi-log plot, see <a
     *  href="package-summary.html#GChartExample04">the
     *  Chart Gallery's GChartExample04</a>.
     *  <p>
     *
     *  <li><i>Log2 inverse prefix</i>: If the string begins with
     *  the prefix <tt>=2^</tt> the value is replaced with
     *  <tt>pow(2.,value)</tt> and the so-transformed value is
     *  then formatted using the part of the format string that
     *  comes after this prefix, which must be a valid GWT
     *  <tt>NumberFormat</tt> pattern.
     *  <p>
    
     *  <li><i>Date casting prefix</i>: If the string begins with
     *  the prefix <tt>=(Date)</tt> the value is replaced with
     *  <tt>new Date((long) value)</tt> and the so-transformed
     *  value is then formatted using the format string that
     *  comes after this prefix, which must be a valid GWT
     *  <a href="http://google-web-toolkit.googlecode.com/svn/javadoc/1.4/com/google/gwt/i18n/client/DateTimeFormat.html">
     *  DateTimeFormat</a>  pattern (e.g. "yyyy-MM-dd&nbsp;HH:mm").
     *  For the special case format string of <tt>"=(Date)"</tt>
     *  (just the date casting prefix) GChart uses the
     *  <tt>DateTimeFormat</tt> returned by the
     *  <tt>DateTimeFormat.getShortDateTimeFormat</tt> method.  <p>
    
     *  Note that the values associated with this Axis must
     *  represent the number of milliseconds since January 1,
     *  1970 (in the GMT time zone) whenever this date
     *  casting prefix is used.  <p>
     *
     * 
     *  For example, if the x-axis tick label format were
     *  "=(Date)MMM-dd-yyyy HH", then, for a tick located at the
     *  x position of 0, the tick label would be "Jan-01-1970 00"
     *  (on a client in the GMT time zone) and for a tick located
     *  at the x position of 25*60*60*1000 (one day + one hour,
     *  in milliseconds) the tick label would be "Jan-02-1970 01"
     *  (again, on a GMT-based client). Results would be
     *  shifted appropriately on clients in different time zones.
     *  <p>
     *
     *  Note that if your chart is based on absolute, GMT-based,
     *  millisecond times then date labels will change when your
     *  chart is displayed on clients in different time zones.
     *  Sometimes, this is what you want. To keep the date labels
     *  the same in all time zones, convert date labels into Java
     *  <tt>Date</tt> objects in your client-side code, then use
     *  the <tt>Date.getTime</tt> method, also in your
     *  client-side code, to convert those dates into the
     *  millisecond values GChart requires.  The <a
     *  href="package-summary.html#GChartExample12"> Chart
     *  Gallery's GChartExample12</a> illustrates how to use this
     *  second approach to produce a time series chart whose
     *  date-time labels are the same in all time zones.
     * 
     *  <p>
    
     *  <blockquote><small>
    
     *  Ben Martin describes an alternative (and more flexible)
     *  approach to formatting time series tick labels in his <a
     *  href="http://www.linux.com/feature/132854">GChart
     *  tutorial</a>. Ben's article, along with Malcolm Gorman's
     *  related <a
     *  href="http://groups.google.com/group/Google-Web-Toolkit/msg/6125ce39fd2339ac">
     *  GWT forum post</a> were the origin of this date
     *  casting prefix. Thanks! </small></blockquote>
     *   
     * </ol>
     * <p>
     *
     *
     * <p> Though HTML text is not supported in the tick label
     * format string, you can change the size, weight, style, and
     * color of tick label text via the
     * <tt>setTickLabelFont*</tt> family of methods. You
     * <i>can</i> use HTML in tick labels (e.g. for a multi-line
     * x-axis label) but but only if you define each tick label
     * explicitly using the <tt>addTick</tt> method.
     *
     * @param format an appropriately prefixed
     *   GWT <tt>NumberFormat</tt> compatible or
     *   GWT <tt>DateTimeFormat</tt> compatible format string that
     *   defines how to convert tick values into tick labels.
     *
     * @see #setTickCount setTickCount
     * @see #addTick(double) addTick(double)
     * @see #addTick(double,String) addTick(double,String)
     * @see #addTick(double,String,int,int) addTick(double,String,int,int)
     * @see #addTick(double,Widget) addTick(double,Widget)
     * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
     * @see #setTickLabelFontSize setTickLabelFontSize
     * @see #setTickLabelFontStyle setTickLabelFontStyle
     * @see #setTickLabelFontColor setTickLabelFontColor
     * @see #setTickLabelFontWeight setTickLabelFontWeight
     * @see #getTickLabelFormat getTickLabelFormat
     */
     public void setTickLabelFormat(String format) {
       // interpret prefixes and create an appropriate formatter
       if (!tickLabelFormat.equals(format)) {
         chartDecorationsChanged = true;
         if (format.startsWith("=(Date)")) {
           String transFormat = format.substring("=(Date)".length());
           if (transFormat.equals("")) // so "=(Date)" works
             dateFormat = DateTimeFormat.getShortDateTimeFormat();
           else // e.g. "=(Date)mm/dd/yy hh:mm"
             dateFormat = DateTimeFormat.getFormat(transFormat);
           tickLabelFormatType = DATE_FORMAT_TYPE;
         }
         else if (format.startsWith("=10^")) {
           String transFormat = format.substring("=10^".length());
           numberFormat = NumberFormat.getFormat(transFormat);
           tickLabelFormatType = LOG10INVERSE_FORMAT_TYPE;
         }
         else if (format.startsWith("=2^")) {
           String transFormat = format.substring("=2^".length());
           numberFormat = NumberFormat.getFormat(transFormat);
           tickLabelFormatType = LOG2INVERSE_FORMAT_TYPE;
         }
         else {
           numberFormat = NumberFormat.getFormat(format);
           tickLabelFormatType = NUMBER_FORMAT_TYPE;
         }
       }
       // remember original format (for use with the getter)
       tickLabelFormat = format;
     }

     /** Specifies the number of pixels of padding (blank space)
      ** between the tick marks and their labels. <p>
      **
      ** With the default of <tt>0</tt>, each
      ** tick label is flush against its tick mark.
      **
      ** @param tickLabelPadding the amount of padding between
      ** tick labels and tick marks, in pixels.
      **
      **
      ** @see #getTickLabelPadding getTickLabelPadding
      ** @see #setTickLength setTickLength
      ** @see #setTickLocation setTickLocation
      **
      **/
     public void setTickLabelPadding(int tickLabelPadding) {
       chartDecorationsChanged = true;
       this.tickLabelPadding = tickLabelPadding;  
     }
     /** Specifies the thickness of the region adjacent to
      ** this axis that GChart will reserve for purposes of
      ** holding this axis' tick labels.  <p>
      ** <p>
      **
      ** For vertical axes, this represents the width of the
      ** widest tick label, for horizontal axes, this represents
      ** the height of highest tick label.
      ** <p>
      **
      **
      ** By default, this property has the special "undefined"
      ** value <tt>GChart.NAI</tt>. With this value, the
      ** companion method <tt>getTickLabelThickness</tt> uses an
      ** HTML-based heuristic to estimate the thickness.
      **
      **
      ** @see #getTickLabelThickness getTickLabelThickness
      ** @see #setTickLabelFontSize setTickLabelFontSize
      ** @see #setTickLocation setTickLocation
      ** @see #setTickLabelPadding setTickLabelPadding
      ** @see #setAxisLabel setAxisLabel
      ** @see GChart#NAI NAI
      **
      **/
     public void setTickLabelThickness(int tickLabelThickness) {
       chartDecorationsChanged = true;
       this.tickLabelThickness = tickLabelThickness;
     }
     /** Specifies the ratio of the number of tick marks on the
      ** axis, to the number of gridlines on the axis.
      ** <p>
      **
      ** For example, with the default value of 1, every tick has
      ** an associated gridline, whereas with a
      ** <tt>ticksPerGridline</tt> setting of 2, only the first,
      ** third, fifth, etc. ticks have gridlines. 
      **
      ** <p>
      **
      ** This setting only has an impact when the axis' gridlines
      ** are turned on, that is, when this axis'
      ** <tt>getHasGridlines</tt> method returns true.
      **     
      ** @see #setHasGridlines setHasGridlines
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      **
      ** @param ticksPerGridline the number of ticks on this
      **   axis per "gridline-extended" tick.
      **
      **/
     public void setTicksPerGridline(int ticksPerGridline) {
       if (ticksPerGridline <= 0)
         throw new IllegalArgumentException("ticksPerGridline=" +
           ticksPerGridline + "; ticksPerGridline must be > 0");
       chartDecorationsChanged = true;
       this.ticksPerGridline = ticksPerGridline;
     }
     /** Specifies the ratio of the number of tick marks on the
      ** axis, to the number of labeled tick marks on the axis.
      ** <p>
      **
      ** For example, with the default value of 1, every tick is
      ** labeled, whereas with a <tt>ticksPerLabel</tt> setting
      ** of 2, only the first, third, fifth, etc. ticks are
      ** labeled.
      **
      ** <p>
      **
      ** This setting is only used when tick labels
      ** are specified implicitly via <tt>setTickCount</tt>. It
      ** is ignored when tick positions and their labels are
      ** explicitly specified via <tt>addTick</tt>.
      **
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      **
      ** @param ticksPerLabel the ratio of the number of ticks,
      **  to the number of labeled ticks.
      **
      **/
     public void setTicksPerLabel(int ticksPerLabel) {
       chartDecorationsChanged = true;
       if (ticksPerLabel <= 0)
           throw new IllegalArgumentException("ticksPerLabel=" +
           ticksPerLabel + "; ticksPerLabel must be > 0");
        this.ticksPerLabel = ticksPerLabel;
     }

     /**
     * Sets this axis' tick length. Set the tick length to zero to
     * eliminate the tick entirely.
     * <p>
     *
     *
     * @param tickLength the length of the tick.
     *
     * @see #getTickLength getTickLength
     * @see #setTickThickness setTickThickness
     * @see #setTickLabelPadding setTickLabelPadding
     * @see #setTickLocation setTickLocation
     *
     */
    abstract public void setTickLength(int tickLength);


     /**
      * Specifies the location of the tick marks relative to this
      * axis, namely, if tick marks are outside, inside, or
      * centered on this axis.
      * <p>
      *
      * @see #getTickLocation getTickLocation
      * @see #setTickThickness setTickThickness
      * @see #setTickLength setTickLength
      * @see #setTickLabelPadding setTickLabelPadding
      *
      * @param tickLocation Specify either
      * <tt>TickLocation.INSIDE</tt>,
      * <tt>TickLocation.OUTSIDE</tt>, or
      * <tt>TickLocation.CENTERED</tt>
      *
      */
     public void setTickLocation(TickLocation tickLocation) {
        this.tickLocation = tickLocation;
        chartDecorationsChanged = true;
        GChart.Symbol sym = getSystemCurve(ticksId).getSymbol();
        if (isHorizontalAxis) {
          sym.setSymbolType(
             tickLocation.getXAxisSymbolType(axisPosition));
          sym.setHeight(getActualTickLength());
        }
        else {
          sym.setSymbolType(
             tickLocation.getYAxisSymbolType(axisPosition));
          sym.setWidth(getActualTickLength());
        }
     }


     /**
     * Sets this axis' tick thickness.
     * <p>
     *
     * @param tickThickness the thickness of the tick.
     *
     * @see #getTickThickness getTickThickness
     * @see #setTickLength setTickLength
     * @see #setTickLabelPadding setTickLabelPadding
     * @see #setTickLocation setTickLocation
     *
     */
    abstract public void setTickThickness(int tickThickness);
    void maybePopulateTicks() {
       if (tickCount != GChart.NAI) populateTicks();
    }

    // fills in the tick positions when auto-generated.
    private void populateTicks() {
       getSystemCurve(ticksId).clearPoints();
//TODO: It should be possible to control the visibility of each axis,
// including ticks and tick labels, independent of the specifications of
// the tick marks on that axis, and independent of if any curves are
// mapped to that axis or not.  A setVisible(Boolean isVisible) as a
// three-way, with null being the current, dependent, defaults, and
// TRUE, FALSE explicitly making the entire axis, including tick marks
// and labels visible or not without having to zero the tick count, add
// dummy curve to the axis, etc. to control axis visibility is needed.
       if (XTICKS_ID == ticksId || // x, y ticks are drawn even
           YTICKS_ID == ticksId || // if no curves are on these axes
           0 < getNCurvesVisibleOnAxis()) {
          AxisLimits l = getAxisLimits();
          for (int i = 0; i < tickCount; i++) {
             // linear interpolation between min and max
             double position =(tickCount == 1) ? l.max :
                              (l.min * ((tickCount-1)-i) + i * l.max)/(tickCount-1.0);
             addTickAsPoint(position,
                            (0 == i % ticksPerLabel) ?
                            formatAsTickLabel(position) : null, null,
                            GChart.NAI, GChart.NAI);
          }
       }
    }


    // fills in the gridlines; ticks are assumed already populated
    void populateGridlines() {
       Curve cTicks = getSystemCurve(ticksId)
       Curve cGridlines = getSystemCurve(gridlinesId);
       cGridlines.clearPoints();
       int nTicks = cTicks.getNPoints();
       for (int iTick = 0; iTick < nTicks; iTick++) {
          if (hasGridlines && (iTick % ticksPerGridline) == 0) {
             Curve.Point p = cTicks.getPoint(iTick);
             cGridlines.addPoint(p.getX(), p.getY());
          }
       }
    }

    protected void getAxisLimits(AxisLimits result) {
       // so we get 1-unit changes between adjacent ticks
       final int DEFAULT_AXIS_RANGE = DEFAULT_TICK_COUNT-1;
       double min = getAxisMin();
       double max = getAxisMax();
       // Adjust min/max so that special cases, like one-point
       // charts, do not have axes that shrink down to a point,
       // which would create numerical and visual difficulties.
       if ((min!=min) && (max!=max)) { // x!=x is a faster isNaN
       // e.g. no data and no explicitly specified ticks
          min = 0;                                  
          max = min + DEFAULT_AXIS_RANGE;
       }
       else if ((min!=min) && !(max!=max)) { // x!=x is a faster isNaN
         // e.g. no data but only max explicitly set
          min = max - DEFAULT_AXIS_RANGE;
       }
       else if (!(min!=min) && (max!=max)) { // x!=x is a faster isNaN
         // e.g. no data but only min explicitly set
          max = min + DEFAULT_AXIS_RANGE;
       }
       else if (min == max) {
         // e.g one data point only, or they set min=max
          max = min + DEFAULT_AXIS_RANGE;
       }
       result.min = min;
       result.max = max;
    }
    AxisLimits getAxisLimits() {
       getAxisLimits(currentLimits);
       return currentLimits;
    }
      
    void rememberLimits() {
       getAxisLimits(previousLimits);
    }
    boolean limitsChanged() {
       boolean result = !getAxisLimits().equals(previousLimits);
       return result;
    }
   

    /* similar to getTickText, except for the tick position */
     private double getTickPosition(Curve c, int iTick) {
        double result;
        if (isHorizontalAxis)
           result = c.getPoint(iTick).getX();
        else
           result = c.getPoint(iTick).getY();
        return result;
     }

    // returns the largest, explicitly specified, tick position
     private double getTickMax() {
        double result = -Double.MAX_VALUE;
        Curve c = getSystemCurve(ticksId);
        int nTicks = c.getNPoints();
        for (int i = 0; i < nTicks; i++)
           result = Math.max(result, getTickPosition(c, i));
        return result;
     }

    // returns the smallest, explicitly specified, tick position
     private double getTickMin() {
        double result = Double.MAX_VALUE;
        Curve c = getSystemCurve(ticksId);
        int nTicks = c.getNPoints();
        for (int i = 0; i < nTicks; i++)
           result = Math.min(result, getTickPosition(c, i));
        return result;
     }
    

    // Same as max, except treats NaN/MAX_VALUE values as "not there"
    protected double maxIgnoreNaNAndMaxValue(double x1, double x2) {
       double result;
       if ((x1!=x1) ||
           Double.MAX_VALUE == x1 ||
           -Double.MAX_VALUE == x1) // x!=x is a faster isNaN
          result = x2;
       else if ((x2!=x2) ||
                Double.MAX_VALUE == x2 ||
                -Double.MAX_VALUE == x2)
          result = x1;
       else
          result = Math.max(x1, x2);
       return result;
    }
    // Same as Math.min, except treats NaN/MAX_VALUE values as "not there"
    protected double minIgnoreNaNAndMaxValue(double x1, double x2) {
       double result;
       if ((x1!=x1) ||
           Double.MAX_VALUE == x1 ||
           -Double.MAX_VALUE == x1 ) // x!=x is a faster isNaN
          result = x2;
       else if ((x2!=x2) ||
                Double.MAX_VALUE == x2 ||
                -Double.MAX_VALUE == x2)
          result = x1;
       else
          result = Math.min(x1, x2);
       return result;
    }
     // does a dummy set of any dynamically determined axis
     // limit, so, for update purposes, they are considered
     // to have changed.
     void invalidateDynamicAxisLimits() {
        // x!=x is a faster isNaN
        if ((axisMin!=axisMin)) setAxisMin(axisMin);
         if ((axisMax!=axisMax)) setAxisMax(axisMax);
     }


       
  } // end of class Axis      

  // creates canvas Widgets GChart needs for *_CANVAS symbol types.
  private static GChartCanvasFactory canvasFactory = null;
  /**
   *
   * Tells GChart how to create the canvas widgets it needs
   * (specifically, widgets that implement GChart's
   * <tt>GChartCanvasLite</tt> interface) to render your
   * charts using an external vector graphics library.  <p>
   *
   * You must define a class that implements
   * <tt>GChartCanvasFactory</tt> and then pass an instance of that
   * class to this method, if you want to have the fast, crisply drawn
   * connecting lines, polygonal areas, and 2-D pie slices that only a
   * vector graphics library can provide.
   * <p>
   *
   * <small>
   * <i>Note:</i> If all of your charts only have
   * rectangular elements (e.g. bar charts) GChart will continue
   * to render those charts using HTML elements, even if a
   * canvas factory is provided. Thus, there is no point
   * to defining a canvas factory if all you use GChart for
   * is bar charts, scatter plots without connecting lines
   * between each point, banded-fill pie slices, and so on.
   * <p>
   *
   * On the other hand, if you need continously connected lines,
   * solid-fill pie slices, or solid-fill area charts (all of which
   * will also need a <tt>setFillSpacing(0)</tt> to specify continuous
   * filling) you will gain substantial performance/quality
   * improvements if you add a canvas factory.  <p>
   * </small>
   *
   * In detail, to exploit browser-based vector graphics (Mozilla's
   * canvas, IE's VML, etc.)  rendering in your charts, you must:
   * 
   * <ol>
  
   *   <li>Import an external GWT canvas library, such
   *   as provided by the
   *   <a href="http://code.google.com/p/google-web-toolkit-incubator/">
GWT incubator project's</a> <tt>GWTCanvas</tt> class, into your project.
   *   For example, our GChart test application uses
   *   <tt>GWTCanvas</tt>
   *   and imports it by adding this line to its
   *   <tt>.gwt.xml</tt> file:
   * <p> 
   * <pre>
   *   &lt;inherits name='com.google.gwt.widgetideas.GWTCanvas' /&gt;
   * </pre>
   *
   * To make this work, we also had to add the
   * <a href="http://code.google.com/p/google-web-toolkit-incubator/">
   * gwt-incubator.jar</a> file (which contains
   * <tt>GWTCanvas</tt>) to our build path via Eclipse's
   * "Configure Build Path..." command and to the libraries
   * listed in the
   * <tt>classpath=...</tt> line of our ant build script's
   * <tt>java</tt> task.
   *
   * <p>
   *
   * <small>For more on the <tt>GWTCanvas</tt> widget, see the <a
   * href="http://code.google.com/p/google-web-toolkit-incubator/wiki/GWTCanvas">
   * GWTCanvas Wiki page</a> within the GWT
   * incubator site.</small>
   *
   * <p>
   *
   *   <li>Implement a class that extends <tt>Widget</tt> and
   *   implements the <tt>GChartCanvasLite</tt> interface that GChart
   *   expects. Again, in GChart's <tt>GWTCanvas</tt>-based test
   *   application we use:
   *
   * {@code.sample ..\..\..\..\..\..\gcharttestapp\src\com\googlecode\gchart\gcharttestapp\client\GWTCanvasBasedCanvasLite.java}
   *  
   *   <li>Create a <tt>GChartCanvasFactory</tt> class that has a
   *   single <tt>create</tt> method that returns new
   *   instances of your <tt>GChartCanvasLite</tt> Widget.
   *   GChart's test application uses:
   *
   *
   * {@code.sample ..\..\..\..\..\..\gcharttestapp\src\com\googlecode\gchart\gcharttestapp\client\GWTCanvasBasedCanvasFactory.java}
   *
   *   <li>Finally pass an instance of that factory to
   *   GChart via a single invocation of
   *   <tt>setCanvasFactory</tt>:
   *
   *   <pre>
   *  static {
   *    GChart.setCanvasFactory(new GWTCanvasBasedCanvasFactory());
   *  }
   *   </pre>
   <p>
  
   *  <small>Because the above line essentially completes the definition
   *  of the GChart class (and thus should only be executed once per
   *  application) a good home for it is in a static initializer, as
   *  shown above. I recommend placing that initializer within your
   *  application's <tt>EntryPoint</tt> class.</small>
   *
   * </ol>
   *
   *  <p>
   *
   *
   * <i>Note:</i> To make things a bit simpler for you, the following
   * code combines steps 2, 3, and 4 into a single chunk of easily pasted
   * boilerplate:
   *
   * <p>
   * <small><small>
   * {@code.sample
   * ..\..\..\..\..\..\gchartdemoapp\src\com\googlecode\gchart\gchartdemoapp\client\GChartDemoApp.java#1}
   * {@code.sample
   * ..\..\..\..\..\..\gchartdemoapp\src\com\googlecode\gchart\gchartdemoapp\client\GChartDemoApp.java#2}
   * </small></small>
   *
   * <p>
   *
   * To see the above boilerplate within a working example application,
   * follow the link at the bottom of GChart's
   * <a href="http://gchart.googlecode.com/svn/trunk/live-demo/v2_6/com.googlecode.gchart.gchartdemoapp.GChartDemoApp/GChartDemoApp.html">
   * live demo page</a> to examine its <tt>EntryPoint</tt> class' code.
   *
   * <p>
   *
   *  <i>GChart's mixed canvas and HTML rendering</i>
   *
   *  <small><blockquote>
   *
   *  Even with an external canvas factory enabled, many aspects
   *  of your chart will remain HTML-rendered. In general, GChart
   *  only renders non-rectangular, "continuously filled" aspects
   *  of your chart using canvas. Other aspects, such as the bars
   *  on a bar chart, or the individual rectangular point markers
   *  on a line chart, as well as all chart text, will remain HTML
   *  rendered.
   *  <p>
   *
   *  The rendering mode (HTML-only or HTML+canvas) for each curve is
   *  defined by if a canvas factory is available and if that curve uses
   *  the special <tt>setFillSpacing(0)</tt> setting (meaning:
   *  "continuously filled"). So, to force any curve to be only HTML
   *  rendered, simply set this fill spacing to a value greater than
   *  <tt>0</tt><p>
   *
   *  HTML rendering offers some features not available with canvas
   *  rendering.  For example, only HTML-rendered curves can overwrite
   *  the enclosing page without monopolizing mouse events within that
   *  curve's bounding rectangle, and only HTML-rendered curves can
   *  define their "fill" via an image. HTML rendered curves can
   *  sometimes even be more memory-efficent, provided the number of
   *  pixels greatly exceeds the number of HTML elements. And the HTML
   *  only rendering option provides a useful least common
   *  denominator/fall-back, supported by even the most obscure
   *  browsers.<p>
   *
   *  Note that the internal curves that GChart uses to render tick
   *  marks, gridlines, etc.  are never canvas rendered, because they
   *  only involve vertical or horizontal rectangles, which GChart
   *  always renders with HTML.
   * 
   *  </blockquote></small>
  
   * <p>
   *
   * Finally, if you are content with GChart's built-in HTML-based
   * rendering, or if your charts only use rectangular elements (e.g.
   * bar charts) you don't need to bother with any of the steps listed
   * above. Your charts will then only depend on the standard GWT
   * distribution and the 3,000 or so lines of pure GWT Java that
   * implement GChart.  <p>
   *
   * <small> <i>Important</i>: GChart only uses your external
   * canvas facility to draw a chart's non-rectangular aspects.
   * Given how GChart works, a curve can only have
   * non-rectangular aspects if <tt>setFillSpacing</tt> has been
   * set to <tt>0</tt> (which implies "continuous filling"), and
   * <tt>setFillThickness</tt> has been set to a value <tt>&gt;
   * 0</tt>. If you are not seeing crisp, canvas-rendered area,
   * line, or pie charts, be sure to check these two settings on
   * the curves in question.  </small>
   *
   * @see GChartCanvasFactory GChartCanvasFactory
   * @see GChartCanvasLite GChartCanvasLite
   * @see #getCanvasFactory getCanvasFactory
   * @see GChart.Symbol#setFillSpacing setFillSpacing
   * @see GChart.Symbol#setFillThickness setFillThickness
   *
   */
  public static void setCanvasFactory(GChartCanvasFactory factory) {
     canvasFactory = factory;
  }

  /**
   * Returns the GChart class' canvas factory, or <tt>null</tt>
   * if no canvas factory has been specified.
   *
   * @return the previously specified canvas factory
   *
   * @see #setCanvasFactory setCanvasFactory
   *
   */
  public static GChartCanvasFactory getCanvasFactory() {
     return canvasFactory;
  }
 
  /**
   * Represents a curve on a chart, which includes
   * information such as the x,y coordinates of each point,
   * the symbol used to represent points on the curve, etc.
   * <p>
   * To create a new curve, use the <tt>GChart.addCurve</tt>
   * method.
   *
   * @see GChart#addCurve() addCurve()
   *
   */
  public class Curve {
     private boolean isVisible = true;
     private String legendHTML = null;
     private ArrayList<Point> points = new ArrayList<Point>();
     // symbol defines how every point on this curve is rendered
     private Symbol symbol = new Symbol(this);

     private YAxisId yAxisId = Y_AXIS;

     private boolean isValidated = false;
     boolean isValidated() { return isValidated; }
/*
* TestGChart14d revealed that curves.indexOf(curve) could, due to its
* sequential search, create a performance bug if the chart had
* over 100 curves (e.g. the 160 pie chart slices/curves of TestGChart14d)
* <p>
*
* With a little extra bookkeeping during add/remove curve to call these
* methods (and the extra int) this problem was corrected.
*
*
*/
     private int indexOf = GChart.NAI;
     void incrementIndex() {indexOf++;}
     void decrementIndex() {indexOf--;}
     void clearIndex() {indexOf = GChart.NAI;}
     int getIndexOf() {return indexOf;}
//     private void assertCurveNotRemoved() {
//       if (indexOf == GChart.NAI)
//          throw new IllegalStateException(
//            "Removed curves should not be modified. " +
//            "You removed a curve, but retained a reference " +
//            "to that curve, and then tried to modify one of " +
//            "its properties after you removed it.");
//     }
    
     /*
      * No public constructor because curves are always
      * contained within, and managed by, their containing
      * GChart via its addCurve, removeCurve, and related
      * methods.
      *
      */
     Curve(int indexOf) {
        super();
        this.indexOf = indexOf;
     }
     /**
     * Adds a new point to the curve, at the end of the current
     * list of points, with the specified
     * coordinates in model-units (arbitrary, application-specific,
     * units).
     * <p>
     *
     * GChart gives a special interpretation to the following values:
     * <p>
     *
     * <ol>
     *
     * <li> If <tt>-Double.MAX_VALUE</tt> is specified for either x or y,
     * the point acts as if it were placed at the minimum visible
     * x or y position within the plot area.
     * <p>
     *
     * <li> Similarly, if <tt>Double.MAX_VALUE</tt> is specified for
     * either x or y, the point acts as if it were placed at the
     * maximum visible x or y position within the plot area.  <p>
     *
     * <p>
     * <li>If <tt>Double.NaN</tt> is specified for either x or y, the
     * point is created, but it will not be visible in the
     * charting region.
     *
     * <p>
     * <i>Tip:</i> Connecting lines to/from such
     * <tt>Double.NaN</tt> points are elided, so you can use such a
     * point to create a break in an otherwise connected curve.
     *
     * </ol>
     *
     *
     * @param x the x-coordinate of the new point
     * @param y the y-coordinate of the new point
     *
     * @see #getPoint getPoint
     * @see #addPoint(int,double,double) addPoint(int,double,double)
     * @see #removePoint removePoint
     * @see #clearPoints clearPoints
     * @see #getNPoints getNPoints
     */
     public void addPoint(double x, double y) {
       invalidate();
       points.add(new Point(x, y));
    }

     /**
     * Adds a new point at the specified position in the point
     * sequence, increasing the indexes of existing points at or after
     * the specified position by 1.
     *
     * @param iPoint the position that the new point will occupy
     * @param x the x-coordinate of the new point (model units)
     * @param y the y-coordinate of the new point (model units)
     *
     * @see #getPoint getPoint
     * @see #addPoint(double, double) addPoint(double,double)
     * @see #removePoint removePoint
     * @see #clearPoints clearPoints
     * @see #getNPoints getNPoints
     */ 
     public void addPoint(int iPoint, double x, double y) {
        invalidate();
        points.add(iPoint, new Point(x, y));
    }

   /**
   * Removes every point this curve contains.
   *
   * @see Point Point
   * @see #getPoint getPoint
   * @see #addPoint(double, double) addPoint(double,double)
   * @see #addPoint(int,double,double) addPoint(int,double,double)
   * @see #removePoint removePoint
   * @see #getNPoints getNPoints
   */
   public void clearPoints() {
      if (this == getTouchedCurve())
         plotPanel.touch(null);
      invalidate();
      points.clear();
   }


  /*
   * Locates index of vertical or horizontal hit-testing band
   * that the given point appears in. The first and last
   * "pseudo-bands" are devoted to holding all points that fall
   * either to the left of or to the right of (or above or below
   * for horizontal banding) the first or last "normal" band
   * covering the decorated chart's containing "box".
   * <p>
   *
   * Note that some points just slightly off the right or bottom edge
   * may not end up in a pseudo-band, due to the fact that the chart
   * width (or height) need not be an even multiple of the (fixed) band
   * thickness (the "last band sticking out a bit" effect).
   *
   */
   private int getBand(int iPoint, double bandThickness) {
      int result = GChart.NAI;
      SymbolType symType = getSymbol().getSymbolType();
      double xPx = symType.getCenterX(plotPanel, getSymbol(), iPoint);
      if (xPx!=xPx) return result; // NaN points not in any band
      double yPx = symType.getCenterY(
                      plotPanel, getSymbol(), iPoint, onY2());
      if (yPx!=yPx) return result; // NaN points not in any band

      // now, we've got a point with x,y values in some sort of band
     
      if (getSymbol().isHorizontallyBanded()) {
         if (yPx < 0)   
            result = 0; // off-chart point above chart
         else if (yPx >= (bandList.length-EXTRA_BANDS)*bandThickness)
            result = bandList.length-1; // off-chart point below chart
         else
            // inside a normal, chart-covering, band
            result = 1 + (int) Math.floor(yPx/bandThickness);
      }
      else { // vertically banded
         if (xPx < 0)
            result = 0// off-chart point to the left
         else if (xPx >= (bandList.length-EXTRA_BANDS)*bandThickness)
            result = bandList.length-1; // off-chart point to the right
         else
            // within one of the real bands covering the chart
            result = 1 + (int) Math.floor(xPx/bandThickness);
      }
      return result;
   }
  
   /*
    * Number of hit-test bands for this curve, for a given band
    * thickness.
    *
    */
   private int EXTRA_BANDS = 2;   // far left, right (top, bottom) bands
   private int getNBands(double bandThickness) {
      int result = EXTRA_BANDS;
      if (getSymbol().isHorizontallyBanded())
        result += (int) Math.ceil(getYChartSize()/bandThickness);
      else
        result += (int) Math.ceil(getXChartSize()/bandThickness);
      return result;
   }

   /*
    * Separates points on this curve into bins associated with
    * successive vertical (or horizontal) bands across the entire
    * decorated chart.
    * <p>
    *
    * Because busy charts typically distribute points evenly
    * across the chart, by jumping to the appropriate band's
    * list, we can (usually) greatly accelerate worst case mouse
    * hit testing. And because the bin organizing step only
    * requires a single pass over all the points (and less than a
    * two int memory overhead per point) it should almost always
    * be a "good deal" performance-wise (compared to a simple
    * full point-list scan with every hit test).<p>
    *
    * Points are placed into bins based on the (pixel) position
    * of the x (or, with horizontal bands, y) at the center of
    * the rendered symbol.  We choose bin size to guarantee that
    * bins are at least as wide (or high, for horizontally banded
    * hit testing) as the rendered symbols on this curve. This
    * simplifies hit testing, since bins are big enough to assure
    * that a single symbol straddles at most two adjacent bands.
    * Exploits fact that all symbols on the same curve have the
    * same size, and that curves with many points on them tend to
    * have smaller sized symbols.
    *
    * <p>
    *
    * Note that, for bin placement purposes, pie slices are
    * considered to have a "center" equal to the center of the
    * pie that contains them, and to have a width and height
    * equal to the diameter of the pie containing the slice
    * (the "worst-case slice").
    *
    * <p>
    *
    * Also note that a symbol whose center is in the right side
    * of a vertical band may overlap into the following band, and
    * one in the left side may overlap the preceding band. Thus
    * during hit testing, we must check not only the lists of
    * points in the bands "touched" by the mouse-cursor-centered
    * brush, but also 1) the band to the immediate left of the
    * leftmost touched band, whenever a left-side sub-band of that band
    * is touched by the brush and 2) the band to the immediate
    * right of the rightmost touched band, whenever a right-side
    * sub-band of that band is touched. The thickness of these left
    * and right side sub-bands equals half the symbol width.
    * Expanding the brush a half-symbol width on either edge
    * is the easiest way to apply these rules. Exactly
    * analogous statements apply to horizontal bands.  <p>
    *
    * A minimum band size is enforced to prevent the number of
    * bands from growing too large with small symbols. Each
    * symbol type defines if vertical or horizontal banding is
    * more appropriate, or if brush shape should determine
    * banding strategy (as of this writing, only horizontal bar
    * symbol types require horizontal hit-test bands).  Exploits the
    * fact that all symbols have a fixed size for at least one of
    * their dimensions (for example, vertical bars have variable
    * height but fixed width).<p>
    *
    * Note: this method must be called after the curve is
    * rendered during an update(), to assure that hit-test-bins
    * are consistent with rendered curves, and ready for use
    * before the first mouse hit testing is done.
    * <p>
    *
    * After running this method, points on this curve within a given
    * band can be enumerated as in the following code:
    * <p>
    *
    * <pre>
    *   Point p = null;
    *   for (int iPoint = bandList[iBand];
    *        iPoint != GChart.NAI;
    *        iPoint = p.getINextInBand()) {
    *      p = getPoint(iPoint);
    *      // do something requiring points in a given band...
    *   }
    *
    * </pre>
    *
    */
   private int[] bandList = null; // index of first point in each band
   private double bandThickness = Double.NaN;
   void clearBandList() {bandList = null;}
   void bandSeparatePoints() {
     bandThickness =
        getSymbol().getSymbolType().getBandThickness(plotPanel,
                                                     getSymbol(),onY2());
     int nBands = getNBands(bandThickness);
        
     if (bandList == null || bandList.length != nBands)
        bandList = new int[nBands];
     // else bandList already has required length, reuse it.

     // all bands contain NAI terminated, empty lists to start with
     for (int i = 0; i < bandList.length; i++)
       bandList[i] = GChart.NAI; 

     for (int iPoint = 0; iPoint < getNPoints(); iPoint++) {
       int iBand = getBand(iPoint, bandThickness);
       Point p = getPoint(iPoint);
       if (GChart.NAI == iBand) {
          // point isn't rendered at all, so isn't in a band (a next
          // link pointing to self means "I'm not in any band"). To let
          // us skip over these points quickly during rendering.
          p.setINextInBand(iPoint);
       }
       else {
     // Add point to front of list for whatever band it's in
     // (note that point order therefore gets reversed).
         p.setINextInBand(bandList[iBand]);
         bandList[iBand] = iPoint;
       }
      
     }
   }
/*
* Returns the index of the point on this curve whose rendered
* symbol intersects a rectangle with the specified width and
* height centered on the specified point (this rectangle is
* typically a point selection "brush", centered on the mouse
* cursor).  <p>
*
* In the event that more than one point's rendered symbol
* intersects with the specified rectangle, the point whose
* center is closest to the specified rectangle's center is
* returned. In the event of a tie, the point with the largest
* point index is returned. If no point "touches" the rectangle,
* <tt>GChart.NAI</tt> is returned.  <p>
*
* Assumes/requires up-to-date <tt>bandList</tt> array and
* related <tt>iNextInBand</tt> indexes (these get defined within
* the <tt>bandSeparatePoints</tt> method).
*
*/

   int getClosestTouchingPoint(int xBrush, int yBrush) {

     int result = GChart.NAI;
     // ANCHOR_MOUSE symbol type curves not band separated/hit tested
     if (null == bandList) return result;
     SymbolType symType = getSymbol().getSymbolType();
     double dBest = Double.MAX_VALUE; // closest touching pt's distance^2
 
     int iBandFirst;
     int iBandLast;

     int brushWidth = symType.getBrushWidth(getSymbol());
/*
* In every tested browser EXCEPT FF3, we don't need the +1 below to
* select a 1px tall, off-chart, symbol with a 1x1 px brush
* (specifically, to select the leftmost vertical bar on TestGChart28).
* The +1  below in effect adds 1 px to the height of the brush to
* workaround this problem.
*
*/
     int brushHeight = symType.getBrushHeight(getSymbol()) + 1;
     AnnotationLocation brushLocation = symType.getBrushLocation(
        getSymbol());
     int nBands = bandList.length;

     // Determine range of bands touched by brush, taking into
     // account potential for symbols whose centers are in one
     // band to "stick out" into an adjacent band by half-band
     // thickening of either end of the brush.
     //
     // Note that the 0th and (nBand-1)th bands represent
     // "pseudo-bands" that hold all points that fall to the left
     // or right (or above or below if horizontally banded) the
     // rectangle occupied by the decorated chart.  The tacit
     // assumption is that such completely off-the-chart points
     // are rare, so it's OK to bunch them up into just 2 bands.
     if (getSymbol().isHorizontallyBanded()) {
        // horizontal bars and some curves with "wider than high" brushes
       double top = brushLocation.getUpperLeftY(yBrush, brushHeight, 0);
       double bottom = top + brushHeight;
       top -= bandThickness/2.;
       bottom += bandThickness/2.;
       iBandFirst = (int) Math.max(0, Math.min(nBands-1,1+Math.floor(
          top / bandThickness)));
       iBandLast = (int) Math.max(0, Math.min(nBands-1, 1+Math.floor(
          bottom / bandThickness)));
     }
     else { // vertical bars, some curves with "tall or square" brushes
       double left = brushLocation.getUpperLeftX(xBrush, brushWidth, 0);
       double right = left + brushWidth;
       left -= bandThickness/2.0;
       right += bandThickness/2.0;      
       iBandFirst = (int) Math.max(0, Math.min(nBands-1, 1+Math.floor(
          left / bandThickness)));
       iBandLast = (int) Math.max(0, Math.min(nBands-1, 1+Math.floor(
          right / bandThickness)));
     }

     // Every point whose symbol touches the brush must be in one
     // of these bands. Search them to find closest touching point.
     for (int iBand = iBandFirst; iBand <= iBandLast; iBand++) {
       Point p = null;
       for (int iPoint = bandList[iBand];
            iPoint != GChart.NAI;
            iPoint=p.getINextInBand()) {
         if (iPoint < 0 || iPoint >= getNPoints())
             throw new IllegalStateException(
"Inappropriately terminated band-point-list, GChart bug likely. " +
"iPoint=" + iPoint + " nPoints=" + getNPoints() +
" iBand="+iBand+" iBandFirst="+iBandFirst+" iBandLast="+iBandLast +
" xBrush="+xBrush+" yBrush="+yBrush+" brushWidth="+brushWidth +
" brushHeight=" +brushHeight + " bandThickness=" + bandThickness);
         p  = getPoint(iPoint);
         if (symType.isIntersecting(plotPanel, getSymbol(),
                                    iPoint, onY2(),
                                    xBrush, yBrush,
                                    brushWidth, brushHeight)) {
           // this point touches the brush (keep it if closest) 
           double xPoint = symType.getCenterX(plotPanel,
                                         getSymbol(), iPoint);
           double yPoint = symType.getCenterY(plotPanel,
                                         getSymbol(), iPoint, onY2());
           double dx = getSymbol().xScaleFactor*(xPoint-xBrush);
           double dy = getSymbol().yScaleFactor*(yPoint-yBrush);
           double d = dx*dx + dy*dy;
           if (d < dBest) { 
             result = iPoint;
             dBest = d;     
           }
           else if (d == dBest && iPoint > result) {
              // in the case of ties, choose largest point index
              // (highest "z-order" -- the one "on top")
             result = iPoint;
             dBest = d;     
           }
             
         }
       }
     }
           
     return result;

   }
  
  /**
   * @deprecated
   *
   * This method is equivalent to:
   * <p>
   *<tt>getSymbol().getHovertextTemplate()</tt>
   * <p>
   *
   * It is retained only for GChart 1.1 compatibility purposes.
   *
   * @see Symbol#getHovertextTemplate() Symbol.getHovertextTemplate
   *
   */
    public String getHovertextTemplate() {
      return symbol.getHovertextTemplate();
    }

  /**
   ** Returns the HTML defining this curve's legend label.
   **
   ** @return the legend label HTML for this curve
   **
   ** @see #setLegendLabel setLegendLabel
   **
   **/
    public String getLegendLabel() {
      return legendHTML;
    }
 
  /**
   * Returns the number of points this curve contains.
   *
   * @return the number of points this curve contains.
   *
   * @see #getPoint getPoint
   * @see #addPoint(double, double) addPoint(double,double)
   * @see #addPoint(int,double,double) addPoint(int,double,double)
   * @see #removePoint removePoint
   * @see #clearPoints clearPoints
   */ 
    public int getNPoints() {
      return points.size();
    }

    /**
     * Returns a reference to the GChart that contains this
     * curve.
     *
     * @return GChart that contains this curve--its "parent".
     *
     */
    public GChart getParent() {return GChart.this; }
 
  /**
   * Convenience method equivalent to <tt>getPoint(getNPoints()-1)</tt>.
   * <p>
   * This method makes code more readable for the common case when
   * you first add a point to the end of a curve, and then modify that
   * point's attributes, as illustrated below:
   * <p>
   * <pre>
   *   class MyChart extends GChart {
   *     public MyChart() {
   *       addCurve();
   *       for (int i=0; i < 10; i++) {
   *         getCurve().addPoint(i,i);
   *         getCurve().getPoint().setAnnotationText("Point " + i);
   *       }
   *       update();
   *     }
   *   }
   * </pre>
   *
   * @return the point on the curve with the highest integer index
   *
   * @see #getPoint(int) getPoint(int)
   * @see #getNPoints() getNPoints()
   *
   */
    public Point getPoint() {
       Point result = getPoint(getNPoints()-1);
       return result;
    }

  /**
   * Returns a reference to the point at the specified
   * index.  The returned reference can be used to modify
   * various properties of the point, such as
   * its optional annotation (text label).
   *
   * <p>
   * @param iPoint the index of the point to be returned.
   * @return a reference to the Point at the specified index.
   *
   * @see #addPoint(double, double) addPoint(double,double)
   * @see #addPoint(int,double,double) addPoint(int,double,double)
   * @see #removePoint removePoint
   * @see #clearPoints clearPoints
   * @see #getNPoints getNPoints
   */
    public Point getPoint(int iPoint) {
       if (iPoint < 0 || iPoint >= points.size())
          throw new IllegalArgumentException(
           "Point index iPoint=" + iPoint + ". " +
           "is either < 0 or >= the number of points on the curve.");
       Point result = points.get(iPoint);
       return result;
    }

   
    /**
     * Returns the positional index (within this curve's list of
     * points) of the specified point.
     * <p>
     *
     * Returns <tt>GChart.NAI</tt> if the specified point is not found on
     * this curve's point list.
     *
     * <p>
     * @param point point whose list position is to be retrieved
     * @return position of point on this curve's point list, or
     *        <tt>GChart.NAI</tt>
     *        if not on the list.
     *
     * @see #getPoint() getPoint()
     * @see #getPoint(int) getPoint(int)
     * @see #addPoint addPoint
     * @see #removePoint removePoint
     * @see #clearPoints clearPoints
     * @see #getNPoints getNPoints
     */
    public int getPointIndex(Point point) {
       int result = points.indexOf(point);
       if (-1 == result) result = GChart.NAI;
       return result;
    }
  /**
    ** Returns the symbol associated with this curve.
    ** <p>
    **
    ** Though you cannot set the symbol itself (there is no
    ** <tt>setSymbol</tt> method) you can have essentially
    ** the same effect by setting the <tt>SymbolType</tt> (to get
    ** qualitatively different kinds of symbols, e.g.
    ** bar-chart bars vs. boxes) and by changing symbol
    ** attributes such as background color, height, and
    ** width.
    **
    ** @return the symbol used to represent points on this curve
    **
    ** @see Symbol#setSymbolType Symbol.setSymbolType
    ** @see Symbol#setBackgroundColor Symbol.setBackgroundColor
    ** @see Symbol#setBorderWidth Symbol.setBorderWidth
    ** @see Symbol#setBorderStyle Symbol.setBorderStyle
    ** @see Symbol#setWidth Symbol.setWidth
    ** @see Symbol#setHeight Symbol.setHeight
    ** @see Symbol#setModelWidth Symbol.setModelWidth
    ** @see Symbol#setModelHeight Symbol.setModelHeight
    **
    **/
     public Symbol getSymbol() {
       return symbol;
     }
 
  /**
   * Returns the y-axis (Y_AXIS or Y2_AXIS) this curve is
   * plotted on.
   *
   * @return an identifier, either Y_AXIS, or Y2_AXIS, indicating
   *   if this curve is plotted on the left (y) or right (y2) y-axis
   *
   ** @see #setYAxis setYAxis
   ** @see GChart#Y_AXIS Y_AXIS
   ** @see GChart#Y2_AXIS Y2_AXIS
   **
   */
    public YAxisId getYAxis() {
      return yAxisId;
    }

  /** Is this curve visible on the chart and legend key,
    ** or is it hidden from view.
    **
    ** @return true if the curve is visible, false otherwise.
    **
    ** @see #setVisible setVisible
    **/
     public boolean isVisible() {return isVisible;}

  /** Convenience method equivalent to <tt>getYAxis()==Y2_AXIS</tt>.
   *
   * @return true if curve is on second y-axis, else false
   *
   * @see #getYAxis getYAxis
   */
    public boolean onY2() {
       return yAxisId == Y2_AXIS;
    }
  /**
   * Removes the point at the specified index.
   *
   * @param iPoint index of point to be removed.
   *
   * @see #getPoint getPoint
   * @see #addPoint(double, double) addPoint(double,double)
   * @see #addPoint(int,double,double) addPoint(int,double,double)
   * @see #clearPoints clearPoints
   * @see #getNPoints getNPoints
   */
    public void removePoint(int iPoint) {
       if (iPoint < 0 || iPoint >= getNPoints())
          throw new IllegalArgumentException(
             "iPoint=" + iPoint + " iPoint arg must be >= 0 and < " +
              getNPoints() + ", the number of points on the curve.");
       invalidate();

       // simulate user moving away from point before it is deleted
       // (this assures that any required hoverCleanup gets called,
       //  and clears the otherwise dangling reference to the point)
       if (plotPanel.touchedPoint == getPoint(iPoint))
          plotPanel.touch(null)
      
       points.remove(iPoint);
    }

    /**
     * Removes the given point from this curve.
     * <p>
     *
     * If the given point is not on this curve, or is
     * <tt>null</tt>, an exception is thrown.
     *
     * @param p the point to be removed.
     *
     *
     */
    public void removePoint(Point p) {
       if (null == p)
          throw new IllegalArgumentException("p cannot be null.");
       int index = getPointIndex(p);
       if (GChart.NAI == index)
          throw new IllegalArgumentException("p must be a point on this curve " +
            "(whose curveIndex is " + getParent().getCurveIndex(this) + ")");
       removePoint(index);
    }
  /**
   ** @deprecated
   **
   ** This method is equivalent to:
   ** <p>
   **  <tt>getSymbol().setHovertextTemplate(hovertextTemplate)</tt>
   ** <p>
   ** It is retained only for GChart 1.1 compatibility purposes.
   **
   ** @see Symbol#setHovertextTemplate Symbol.setHovertextTemplate
   **/
    public void setHovertextTemplate(String hovertextTemplate) {
       symbol.setHovertextTemplate(hovertextTemplate);
    }
  /**
   ** Sets the HTML that defines the label shown to the
   ** right of the icon representing the curve's symbol in
   ** the chart's legend.
   **
   ** <p>
   ** Setting the legend label to <tt>null</tt> removes the
   ** entire row (the label and the icon) associated with
   ** this curve from the chart key.
   ** <p>
   ** Note that, since <tt>null</tt> is the default, unless
   ** you set at least one legend label, no chart key will
   ** appear at all.
   **
   ** @param legendHTML the HTML defining this curve's legend label.
   **                   or <tt>null</tt> to remove the curve from
   **                   the legend entirely.
   **                  
   ** @see #getLegendLabel getLegendLabel
   ** @see GChart#setLegendThickness setLegendThickness
   **
   **/
    public void setLegendLabel(String legendHTML) {
       chartDecorationsChanged = true;
       this.legendHTML = legendHTML;
    }
  /**
    ** Defines if this curve is visible both in the plotting
    ** region and on the legend key.
    ** <p>
    **
    ** <i>Notes:</i>
    **
    ** <ol>
    **  <li>A curve must also have a non-<tt>null</tt> legend label
    ** if it is to appear on the legend key.
    ** <p>
    **
    **  <li>Hidden curves are excluded from the computation
    ** of any auto-computed axis limits.
    **
    ** </ol>
    **
    ** @param isVisible false to hide curve, true to reveal it.
    **
    ** @see #isVisible() isVisible
    ** @see #setLegendLabel setLegendLabel
    **
    **/
    public void setVisible(boolean isVisible) {
       /* Axis curve count bookkeeping requires that curve be on the
        * list of curves.
        * <p>
        *
        * Developer refs to removed curves could screw up this
        * bookkeeping.
        * <p>
        *
        * Though often this would be a developer error, best not to
        * throw an exception because some developers could use deleted
        * curves as repositories for curve state data, or an event
        * sequence might produce setVisible calls through a dangling
        * curve reference after a curve had been removed, and best to
        * let developer get away with that kind of thing.  <p>
        *
        * No point in invalidating since removed curves can never
        * become part of a rendered GChart again.
        *
        */
      
        if (getIndexOf() == GChart.NAI) {
           this.isVisible = isVisible;
           return;         
        }
      
        invalidate();

        // hover selection feedback curves (which are system curves)
        // never impact curve counts, need to refresh decorations, etc.
        if (isSystemCurve()) {
           this.isVisible = isVisible;
           return;
        }
       
        if (this.isVisible != isVisible) {
           Axis yaxis = (getYAxis() == Y_AXIS) ?
                         GChart.this.getYAxis() :
                         GChart.this.getY2Axis();
           boolean axisCreatedOrDestroyed;
           if (isVisible) {
             axisCreatedOrDestroyed =
                (yaxis.getNCurvesVisibleOnAxis() == 0);
             getXAxis().incrementCurves();
             yaxis.incrementCurves();
           }
           else {
             getXAxis().decrementCurves();
             yaxis.decrementCurves();
             axisCreatedOrDestroyed =
               (yaxis.getNCurvesVisibleOnAxis() == 0);
           }

           if ((null != getLegendLabel() && isLegendVisible()) ||
               axisCreatedOrDestroyed)
             chartDecorationsChanged = true;

           this.isVisible = isVisible;
        }
     }
    
  /** Sets the y-axis that this curve is plotted on.
   ** <p>
   ** @param axisId must be either GChart.Y_AXIS or
   **               GChart.Y2_AXIS
   **
   ** @see #getYAxis getYAxis
   ** @see GChart#Y_AXIS Y_AXIS
   ** @see GChart#Y2_AXIS Y2_AXIS
   **
   **
   **/
    public void setYAxis(YAxisId axisId) {
       invalidate();
       if (isSystemCurve()) {
          yAxisId = axisId;
       }
       else if (axisId != yAxisId) {
          if (axisId == Y2_AXIS) { // from Y to Y2
           GChart.this.getYAxis().decrementCurves();
           GChart.this.getY2Axis().incrementCurves();
          }
          else {   // from Y2 to Y
           GChart.this.getY2Axis().decrementCurves();
           GChart.this.getYAxis().incrementCurves();
          }
          yAxisId = axisId;
       }
    }

    // Is this specific curve actually clipped to the plot area?
    private boolean getActuallyClippedToPlotArea() {
       boolean result = getClipToPlotArea();
       if (result) {
         // decorative, hover feedback curves are never clipped
         int rpIndex = getRenderingPanelIndex(getIndexOf());
         if (PlotPanel.DECORATIVE_RENDERING_PANEL_INDEX == rpIndex ||
             isHoverFeedbackRenderingPanel(rpIndex))
            result = false;
       }
       return result;
    }
   
    /*
     * Is this curve one of GChart's special, internally created, system
     * curves? These curves can't be directly accessed by users, and are
     * used by GChart to render special features of the chart, such as
     * the hover selection cursors, titles, footnotes, etc.
     *
     */
    private boolean isSystemCurve() {
       // negative curve indexes are reserved for system curves
       boolean result = (indexOf != GChart.NAI) &&
                        externalCurveIndex(indexOf) < 0;
       return result;
    }
  // renders the specified point of this curve on the given panel
    void realizePoint(PlotPanel pp,
                      GraphicsRenderingPanel grp,
                      AnnotationRenderingPanel arp,
                      int iPoint) {
     Point p = points.get(iPoint);
     double x = p.getX();
     double y = p.getY();
     // skip points at undefined locations
     if ((x!=x) || (y!=y)) return; // x!=x is a faster isNaN
     double prevX = Double.NaN;
     double prevY = Double.NaN;
     if (iPoint > 0) {
        Point prevP = points.get(iPoint-1);
        prevX = prevP.getX();
        prevY = prevP.getY();
     }
     double nextX = Double.NaN;
     double nextY = Double.NaN;
     Point nextP = null;
     if (iPoint < getNPoints()-1) {
        nextP = points.get(iPoint+1);
        nextX = nextP.getX();
        nextY = nextP.getY();
     }

     // if point was not assigned to any band, it's not drawn
     // at all (undefined x or y, or off chart entirely)
     boolean drawMainSymbol = (p.getINextInBand() != iPoint);

     getSymbol().realizeSymbol(pp, grp, arp, p.getAnnotation(), onY2(),
                               getActuallyClippedToPlotArea(),
                               getClipToDecoratedChart(),
                               drawMainSymbol,
                               x, y, prevX, prevY, nextX, nextY);
//    }
  }
  /**
   ** Represents a single point on one of the chart's
   ** curves. This includes the x, y values of the point in
   ** "model coordinates" (arbitrary, application-specific,
   ** units), as well as an optional annotation (text label)
   ** for the point.
   ** <p>
   ** To create points, use a curve's <tt>addPoint</tt> method.
   **
   ** @see Curve#addPoint addPoint
   **/
  public class Point {
    
     // x, y location (user coordinates) of point
     // (points are drawn using the containing curve's symbol)
     private double x;
     private double y;
     Annotation annotation = null;
     // Points to index of next point in a vertical or horizontal
     // band (used by the <tt>bandSeparatePoints</tt> method).
     private int iNextInBand = GChart.NAI;
     int getINextInBand() { return iNextInBand;}
     void setINextInBand(int iNext) {iNextInBand = iNext;}
     Point(double x, double y) {
       this.x = x;  
       this.y = y;
     }

     /**
      ** Returns true if annotation will be rendered in a bold,
      ** or false if in normal, weight font.
      **
      ** @return if this annotation is in bold or not.
      **
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      **/
     public String getAnnotationFontWeight() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getFontWeight();
     }

     /**
      ** Returns the color of the font used to display the point's
      **   annotation text.
      **  
      ** @return CSS color string defining the annotation's color
      **
      ** @see #setAnnotationFontColor setAnnotationFontColor
      **/
     public String getAnnotationFontColor() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getFontColor();
     }

    
     /**
      ** Returns the CSS font-style in which the text of this
      **  annotation will be rendered.
      **
      ** @return the font-style used by this annotation (italic,
      **   normal, etc.)
      **
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      **/
     public String getAnnotationFontStyle() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getFontStyle();
     }

     /**
      ** Returns the CSS font size of this point's annotation
      ** (text label), in pixels.
      **
      ** @return the font size of this point's annotation.
      **
      ** @see #setAnnotationFontSize setAnnotationFontSize
      **/
     public int getAnnotationFontSize() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getFontSize();
     }

    
    
     /** Returns the previously specified location, relative
      ** to the symbol representing the point, of the
      ** annotation (text label) associated with this point.
      **
      ** @return relative location of the point's annotation
      **
      ** @see #setAnnotationLocation setAnnotationLocation
      **
      **/
     public AnnotationLocation getAnnotationLocation() {
        if (null == annotation) annotation = new Annotation();  
        AnnotationLocation result = annotation.getLocation();
        if (null == result) result =
          getParent().getSymbol().getSymbolType().defaultAnnotationLocation();
        return result;
     }
     /**
      ** Returns the text of this point's annotation.
      **
      ** @return the text of the annotation, or <tt>null</tt> if this
      **   point either lacks an annotation or uses a widget-based
      **   annotation.
      **
      ** @see #setAnnotationText setAnnotationText
      **
      **/
     public String getAnnotationText() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getText();
     }

     /**
      * Returns the widget reference that defines this point's
      * annotation as previously specified by
      * <tt>setAnnotationWidget</tt>. Returns <tt>null</tt> if
      * the annotation has not yet been specified, or if it was
      * defined via <tt>setAnnotationText</tt>.
      *
      * @return reference to the widget defining this point's
      * annotation, or <tt>null</tt> if none.
      *
      * @see #setAnnotationWidget setAnnotationWidget
      * @see #setAnnotationText setAnnotationText
      *
      */

     public Widget getAnnotationWidget() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getWidget();
     }
    
     /**
      ** Returns true is the point's annotation is visible, false
      ** otherwise
      **
      ** @return if the a annotation defined for this point will
      **    be visible or not after the next update.
      **
      ** @see #setAnnotationVisible setAnnotationVisible
      **/
     public boolean getAnnotationVisible() {
        if (null == annotation) annotation = new Annotation();  
       return annotation.getVisible();
     }

     /**
      ** Returns the distance, in pixels, that this annotation
      ** will be shifted along the x-axis from it's default
      ** location.  <p>
      **
      ** @return amount annotation will be shifted along the x-axis,
      **   in pixels.
      **  
      ** @see #setAnnotationXShift setAnnotationXShift
      **/
      public int getAnnotationXShift() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getXShift();
      }
    
     /**
      ** Returns the distance, in pixels, that this annotation
      ** will be shifted along the y-axis from it's default
      ** location.  <p>
      **
      ** @return amount annotation will be shifted along the y-axis,
      **   in pixels.
      **  
      ** @see #setAnnotationYShift setAnnotationYShift
      **/
      public int getAnnotationYShift() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getYShift();
      }

      /** Returns the <tt>Curve</tt> that this point was added to.
       **
       ** @return a reference to the <tt>Curve</tt> that contains
       ** this point (its "parent").
       **
       **/
      public Curve getParent() {return Curve.this;}
     
     /** Returns the x-coordinate of this point in "model units"
      ** (arbitrary, application-specific, units).
      **
      ** @return the x-coordinate, in model units
      **
      ** @see #setX setX
      ** @see #setY setY
      ** @see #getY getY
      **
      **/
     public double getX() {
        return x;
     }
     /** Returns the y-coordinate of this point in "model units"
      ** (arbitrary, application-specific, units).
      **
      ** @return the y-coordinate, in model units
      **
      ** @see #getX getX
      ** @see #setX setX
      ** @see #setY setY
      **
      **/
     public double getY() {
        return y;
     }

     /**
      ** Specifies the weight of the font that will be used
      ** to render the text of this point's annotation.
      ** <p>
      **
      ** @param cssWeight A standard CSS font-weight
      **    specification such as normal, bold, bolder, lighter,
      **    100, 200, ... 900, or inherit
      **
      ** @see #getAnnotationFontWeight getAnnotationFontWeight 
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      **/
     public void setAnnotationFontWeight(String cssWeight) {
       getParent().invalidate();
       if (null == annotation) annotation = new Annotation();  
       annotation.setFontWeight(cssWeight);
     }
     /**
      ** Specifies the color of the annotation's font.
      **
      **
      ** <p>
      ** For more information on standard CSS color
      ** specifications see the discussion in
      ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
      ** <p>
      **       
      ** @param cssColor color of the font used to display this
      **    point's annotation message.
      **
      ** @see #getAnnotationFontColor getAnnotationFontColor 
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      **/
     public void setAnnotationFontColor(String cssColor) {
        getParent().invalidate();
        if (null == annotation) annotation = new Annotation();  
        annotation.setFontColor(cssColor);
     }

    
     /**
      ** Specifies the CSS font-style used by this point's annotation
      ** message.
      **
      ** @param cssStyle any valid CSS font-style, namely,
      **   normal, italic, oblique, or inherit.
      **
      ** @see #getAnnotationFontStyle getAnnotationFontStyle 
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      **/
     public void setAnnotationFontStyle(String cssStyle) {
        getParent().invalidate();
        if (null == annotation) annotation = new Annotation();  
        annotation.setFontStyle(cssStyle);
     }
     /**
      ** Specifies the CSS font size of this point's annotation, in
      ** pixels.
      **
      ** @param fontSize the font size of this point's annotation, in
      **   pixels.
      **
      ** @see #getAnnotationFontSize getAnnotationFontSize 
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      **/
     public void setAnnotationFontSize(int fontSize) {
        getParent().invalidate();
        if (null == annotation) annotation = new Annotation();  
        annotation.setFontSize(fontSize);
     }
     /**
      ** Specifies the location, relative to this point's symbol,
      ** of this point's annotation (text label).
      ** <p>
      **
      ** You can further adjust the position of a point's
      ** annotation by specifying non-zero positional shifts via
      ** the <tt>setAnnotationXShift</tt> and
      ** <tt>setAnnotationYShift</tt> methods.
      **
      **
      ** @param annotationLocation the relative location of
      ** the annotation
      **
      ** @see #getAnnotationLocation getAnnotationLocation
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationVisible setAnnotationVisible
      **
      **/
     public void setAnnotationLocation(AnnotationLocation
                                       annotationLocation) {
       getParent().invalidate();
       if (null == annotation) annotation = new Annotation();  
       annotation.setLocation(annotationLocation);
     }

     /**
      ** Specifies the text of this point's annotation
      ** (label).
      ** <p>
      **
      ** <p>By default text is plain text, though
      ** you can change the size, weight, style, and color of
      ** the text via the <tt>setAnnotationFont*</tt>
      ** family of methods.
      **
      ** <p>
      **
      ** <b>To use HTML, <i>your text must begin with</i>
      ** <tt>&lt;html&gt</tt></b> (otherwise, GChart will treat
      ** it as plain text). Note that the leading
      ** <tt>&lt;html&gt</tt> is stripped off by GChart before
      ** your HTML gets to the browser. Since it's just a flag
      ** for GChart, not a real HTML tag, you should <i>not</i>
      ** use a closing <tt>&lt;/html&gt</tt> at the end.
      **
      ** <p> <small> The idea for adding HTML support (only plain
      ** text was supported originally) came from <a
      ** href="http://groups.google.com/group/Google-Web-Toolkit/msg/cb89003dad2416fe">
      ** this GWT forum post by Malcolm Gorman</a>. The current
      ** HTML support (and, it's natural extension, Widget
      ** support) in tick labels and annotations, which seems so
      ** obvious in hindsight, might never have been added had it
      ** not been for this post. Thanks!</small>
      **
      ** <p>
      **
      ** <small><b>How to use the width and height upperbounds:</b>
      ** </small>
      **
      ** <p>
      **
      ** <blockquote><small>
      **
      **
      ** In most cases, you can safely ignore these two
      ** parameters, simply calling the {@link
      ** #setAnnotationText(String) 1-arg convenience method}
      ** and getting GChart to estimate them for you.
      ** <p>
      **
      ** The width and height upper-bounds define an invisible
      ** bounding box (a 1x1 GWT Grid, actualy) that is used to
      ** properly align and center your annotation.
      ** <p>
      **
      ** <p> Annotations can
      ** become misaligned if, say, due to the user zooming up
      ** their font size, an annotation's size exceeds these
      ** upperbounds. This misalignment problem can be fixed by
      ** specifying a larger width and/or height upperbound.
      ** But, larger upperbounds slow chart updates a bit.
      ** The defaults try to balance the performance and
      ** alignment tradeoff.
      ** <p>
      **
      ** There is one annoying but generally harmless side effect of
      ** using very large upperbounds: most browsers will extend their
      ** scroll regions to the right (and presumably below) the real
      ** page content so as to include the invisible bounding box. When
      ** this happens, it looks to the user as if there is a bunch of
      ** blank space on, say, the right edge of the page. In practice,
      ** I've always been able to prevent this problem simply by
      ** choosing at-least-somewhat-reasonably-tight upper bounds,
      ** though a little blank space may be unavoidable in some special
      ** cases.
      **
      ** </blockquote></small>
      **
      ** @param annotationText the text or (<tt>&lt;html&gt</tt>
      ** prefixed) HTML of this point's
      ** annotation, or <tt>null</tt> to remove all annotation.
      **
      ** @param widthUpperBound an upper bound on the width of
      ** the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
      ** get GChart to estimate this width using a heuristic
      ** that works fine most of the time.
      **
      ** @param heightUpperBound an upper bound on the height of
      ** the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
      ** get GChart to estimate this height using a heuristic
      ** that works fine most of the time.
      **
      ** @see #getAnnotationText getAnnotationText
      ** @see #setAnnotationText(String) setAnnotationText(String)
      ** @see #setAnnotationLocation setAnnotationLocation
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationWidget setAnnotationWidget
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationVisible setAnnotationVisible
      ** @see GChart.Axis#addTick(double,String,int,int) addTick
      **
      **/
     public void setAnnotationText(String annotationText,
                                   int widthUpperBound,
                                   int heightUpperBound) {
        getParent().invalidate();
        if (null == annotation) annotation = new Annotation();  
        annotation.setText(annotationText,
                           widthUpperBound,
                           heightUpperBound);
     }
     /**
      * Sets the text of an annotation.
      * <p>
      * This is a convenience method equivalent to
      * <tt>setAnnotationText(annotationText, GChart.NAI, GChart.NAI)</tt>. See
      * that method for further details.
      * <p>
      *
      ** @param annotationText the text or
      ** (<tt>&lt;html&gt</tt>-prefixed) HTML of this point's
      ** annotation, or <tt>null</tt> to remove all annotation.
      *
      * @see #setAnnotationText(String, int, int)
      *       setAnnotationText(String,int,int)
      *      
      */
     public void setAnnotationText(String annotationText) {
          setAnnotationText(annotationText, GChart.NAI, GChart.NAI);
     }

     /**
      ** Specifies a widget defining this point's annotation
      ** <p>
      ** This method is similar to <tt>setAnnotationText</tt>
      ** except that it uses a widget, rather than a string
      ** to define this point's annotation.
      ** Although the string based method is faster
      ** on first chart rendering, and uses less memory, the
      ** widget-based method allows you to change the annotation
      ** independently of the chart--potentially bypassing (or
      ** at least speeding up) expensive chart updates later on.
      ** <p>
      **
      ** You might use a widget-based annotation to pop-up a
      ** message whenever the user clicks on a button underneath
      ** a particular data point on the chart, to include a small
      ** GWT <tt>Grid</tt> as a table embedded in the upper left
      ** hand corner of the chart, to trigger mouse-over events
      ** when the user hovers over a transparent image-based
      ** annotation centered on a particular point, etc.
      ** <p>
      **
      ** <i>Tip:</i>If you need to instrument a chart using
      ** widgets precisely positioned on the chart, but not
      ** associated with any visible curve, add a curve just to
      ** hold these annotations, with one point per annotation,
      ** and set that curve's symbol type
      ** to <tt>SymbolType.NONE</tt>.
      ** <p>
      **
      ** <b><i>Warning:</i></b> If you use the exact same widget
      ** reference to define two different annotations, GChart
      ** will render only one of them, and <i>there is no easy
      ** rule</i> that lets you reliably determine which one. So,
      ** don't do that.  Instead, if you want to use the same
      ** widget for two different annotations, use two identical
      ** but distinct copies of that widget. Similarly, if you
      ** want to move a single widget annotation from one point
      ** to another, be sure to "<tt>null</tt> out" the first
      ** point's annotation (e.g.  via
      ** <tt>setAnnotationWidget(null)</tt>) or the widget may
      ** not be rendered where you expect. You should also
      ** <tt>null</tt> out the annotation widget reference before
      ** moving the annotation widget to a position in the DOM
      ** completely outside of the GChart. A little extra
      ** bookkeeping on your part makes it possible to
      ** significantly simplify and streamline GChart's rendering
      ** algorithms.
      **
      *  @param annotationWidget the GWT Widget that defines this
      *    point's annotation.
      *
      *  @param widthUpperBound an upper bound on the width of
      *  the Widget, in pixels. If this and the next
      *  parameter are omitted, GChart will use
      *  <tt>DEFAULT_WIDGET_WIDTH_UPPERBOUND</tt>.
      * 
      *  @param heightUpperBound an upper bound on the height of
      *  the Widget, in pixels. If this and the previous
      *  parameter are omitted, GChart will use <tt>
      *  DEFAULT_WIDGET_HEIGHT_UPPERBOUND</tt>
      *
      *
      * @see #getAnnotationWidget getAnnotationWidget
      * @see #setAnnotationText(String, int, int)
      *       setAnnotationText(String,int,int)
      * @see #setAnnotationWidget(Widget)
      * setAnnotationWidget(Widget)
      * @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
      * @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
      * @see SymbolType#NONE  SymbolType.NONE
      *
      **/
     public void setAnnotationWidget(Widget annotationWidget,
                                   int widthUpperBound,
                                   int heightUpperBound) {
        getParent().invalidate();
        if (null == annotation) annotation = new Annotation();  
     // accept "Not an Integer" (because setAnnotationText does)
       if (widthUpperBound == GChart.NAI)
          widthUpperBound = DEFAULT_WIDGET_WIDTH_UPPERBOUND;
       if (heightUpperBound == GChart.NAI)
          heightUpperBound = DEFAULT_WIDGET_HEIGHT_UPPERBOUND;
       annotation.setWidget(annotationWidget,
                            widthUpperBound,
                            heightUpperBound);
     }

     /**
      * Specifies a widget defining this point's annotation.
      * <p>
      * A convenience method equivalent to
      * <tt>setAnnotationWidget(annotationWidget,
      * DEFAULT_WIDGET_WIDTH_UPPERBOUND,
      * DEFAULT_WIDGET_HEIGHT_UPPERBOUND)</tt>
      *
      *  @param annotationWidget the GWT Widget that defines this
      *    point's annotation.
      *
      * @see #setAnnotationWidget(Widget,int,int)
      * setAnnotationWidget(Widget,int,int)
      * @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
      * @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
      *
      */
     public void setAnnotationWidget(Widget annotationWidget) {
        setAnnotationWidget(annotationWidget, DEFAULT_WIDGET_WIDTH_UPPERBOUND,
                          DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
     }

    
     /**
      ** Specifies if this point's annotation
      ** (label) is visible or not.
      ** <p>
      **
      ** @param isVisible use true to make the annotation
      **   visible, or false to hide it.
      **
      ** @see #getAnnotationVisible getAnnotationVisible
      ** @see #setAnnotationLocation setAnnotationLocation
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationText setAnnotationText
      **/
      public void setAnnotationVisible(boolean isVisible) {
        getParent().invalidate();
        if (null == annotation) annotation = new Annotation();  
        annotation.setVisible(isVisible);
      }

     

     /**
      ** Specifies the number of pixels (along the x-axis) to
      ** move this point's annotation from its default,
      ** <tt>AnnotationLocation</tt>-defined, position.  Negative
      ** values move the annotation in the negative x direction.
      **
      ** <p> For example, with the default <tt>xShift</tt> of 0,
      ** annotations with an <tt>AnnotationLocation</tt> of
      ** <tt>EAST</tt> will have their left edges flush against
      ** the right edge of, say, a box symbol representing the
      ** annotated point.  You could use an <tt>xShift</tt>
      ** setting of 10 to move the annotation 10 pixels to the
      ** right and thus introduce some space between the
      ** annotation and the box.
      ** <p>
      **
      ** <i>Special convention for pie slices:</i>
      ** Points on curves whose symbols represent pie
      ** slices always have the positive x-axis associated with
      ** the shifts specified by this method aligned with the
      ** outward-pointing pie radius that bisects the pie slice. This
      ** convention makes it easy to move pie slice annotations
      ** radially outward (via <tt>xShift > 0</tt>) or
      ** radially inward (via <tt>xShift < 0</tt>). For those
      ** rare situations where you may need to move a pie
      ** annotation perpendicularly to this radius, use
      ** <tt>setAnnotationYShift</tt>.
      **
      ** @param xShift number of pixels to move annotation
      **   along the x axis from
      **   it's default, <tt>AnnotationLocation</tt>-defined,
      **   location.
      **
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationLocation setAnnotationLocation
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      ** @see #getAnnotationXShift getAnnotationXShift
      **/
      public void setAnnotationXShift(int xShift) {
        getParent().invalidate();
        if (null == annotation) annotation = new Annotation();  
        annotation.setXShift(xShift);
      }
     /**
       ** Specifies the number of pixels (along the y-axis) to
      ** move this point's annotation from its default,
      ** <tt>AnnotationLocation</tt>-defined, position.  Negative
      ** values move the annotation in the negative y direction.
      **
      ** <p> For example, with the default <tt>yShift</tt> of 0,
      ** annotations with an <tt>AnnotationLocation</tt> of
      ** <tt>SOUTH</tt> will have their top edges flush against
      ** the bottom edge of, say, a box symbol representing the
      ** annotated point.  You could use a <tt>yShift</tt>
      ** setting of -10 to move the annotation down 10 pixels and
      ** thus introduce some spacing between the annotation and
      ** the box.
      ** <p>
      **
      ** <i>Special convention for pie slices:</i> The positive
      ** y-axis for pie slices always points one 90 degree
      ** counter-clockwise rotation from the direction of the
      ** outward-pointing pie radius that bisects the pie slice.
      ** This convention means that <tt>yShift</tt> moves pie
      ** slice annotations along a line <i>perpendicular to</i>
      ** this bisecting pie radius. Use the companion method
      ** <tt>setAnnotationXShift</tt> for the more common
      ** operation of moving the annotation along this bisecting
      ** radius.
      **
      ** @param yShift number of pixels to move annotation along
      **   the y-axis from it's default,
      **   <tt>AnnotationLocation</tt>-defined, location.
      **
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationLocation setAnnotationLocation
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      ** @see #getAnnotationXShift getAnnotationXShift
      **/
      public void setAnnotationYShift(int yShift) {
        getParent().invalidate();
        if (null == annotation) annotation = new Annotation();  
        annotation.setYShift(yShift);
      }

     /**
      * Defines the x-coordinate of this point in "model units"
      * (arbitrary, application-specific, units mapped to the
      * horizontal dimension of the plot area).
      * <p>
      *
      * <tt>Double.NaN</tt>, <tt>Double.MAX_VALUE</tt>, and
      * <tt>-Double.MAX_VALUE</tt> have special meanings. See the
      * <tt>addPoint</tt> method for details.
      *
      *
      * @param x the x-coordinate of the point in model units.
      *
      ** @see #getX getX
      ** @see #setY setY
      ** @see #getY getY
      ** @see #addPoint addPoint
      */
     public void setX(double x) {
        getParent().invalidate();
        this.x = x;
     }

     /**
      * Defines the y-coordinate of this point in "model units"
      * (arbitrary, application-specific, units mapped to the
      * vertical dimension of the plot area).
      * <p>
      *
      * <tt>Double.NaN</tt>, <tt>Double.MAX_VALUE</tt>, and
      * <tt>-Double.MAX_VALUE</tt> have special meanings. See the
      * <tt>addPoint</tt> method for details.
      *
      * @param y the y-coordinate of the point in model units.
      *
      ** @see #getX getX
      ** @see #setX setX
      ** @see #getY getY
      ** @see #addPoint addPoint
      **
      */
     public void setY(double y) {
        getParent().invalidate();
        this.y = y;
     }

     Annotation getAnnotation() {
        if (annotation == null) annotation = new Annotation();
        return annotation;
     }
     /**
      * Retrieves the expanded hovertext associated with this
      * point.
      * <p>
      *
      * The expanded hovertext is obtained by replacing any
      * embedded parameters in the hovertext template with
      * their values as evaluated at this point. For example,
      * references to <tt>${x}</tt> and <tt>${y}</tt> in
      * the hovertext template are replaced with
      * appropriately formatted representations of this
      * point's x and y properties.
      * <p>
      *
      * By default, GChart will display this expanded hovertext
      * whenever the user "touches" a point with the
      * curve-specific, rectangular, mouse-centered, brush.
      * <p>
      *
      * <i>Tip:</i> To define your own custom parameter names
      * that can be embedded within hovertext templates and
      * will be interpreted/expanded relative to the touched point, use
      * the <tt>setHoverParameterInterpreter</tt> method.
      *
      * @return the expanded hover text associated with this point.
      *
      * @see Symbol#setHovertextTemplate setHovertextTemplate
      * @see #setHoverParameterInterpreter setHoverParameterInterpreter
      *
      */
     public String getHovertext() {
        String result = HovertextChunk.getHovertext(
           getParent().getSymbol().getHovertextChunks(), this);
        return result;
    }
    
  } // end of class GChart.Curve.Point

  /*
   * Declares that this curve's rendering panel (its DOM representation)
   * is inconsistent with current curve specifications.
   *
   * <p>
   *
   * Sets the flag <tt>update</tt> uses to determine if a curve needs
   * to be re-rendered.
   *
   */
  void invalidate() {
     // The guard isn't just for speed; it keeps us out of trouble
     // when the system curves are being added/configured initially
     if (isValidated) {
       isValidated = false;
     // for efficiency, all background curves use a single rendering
     // panel, so invalidating one background curve invalidates them all
       if (indexOf < N_PRE_SYSTEM_CURVES) {
          for (int i = 0; i < N_PRE_SYSTEM_CURVES; i++)
             curves.get(i).isValidated = false;
       }
     }    
  }

  /*
   * Smallest rectangle containing curve's graphics (ignoring
   * annotations)
   * <p>
   *
   * Each curve has it's own canvas to allow for fast, single
   * curve, updates (and we hope this rendering independence will
   * facilitate additional features in future releases). So, we
   * have to economize on canvas size.  Moreover, because we
   * allow rendering outside of the decorated chart region, we
   * can't just set the size of the canvas to the size of the
   * plot area or decorated chart (even if we could afford to do
   * that, memory-wise).
   *
   */
  Rectangle getContainingRectangle(PlotPanel pp) {
     final Rectangle result = new Rectangle();
     if (getNPoints() == 0) {
        result.x = result.y = result.width = result.height = 0;
        return result;
     }
    
     double minX = Double.MAX_VALUE;
     double maxX = -Double.MAX_VALUE;
     double minY = Double.MAX_VALUE;
     double maxY = -Double.MAX_VALUE;
     boolean pointAtXAxisMin = false; // do keyword positioned points
     boolean pointAtXAxisMax = false; // exist on this curve?
     boolean pointAtYAxisMin = false;
     boolean pointAtYAxisMax = false;
     boolean isClippedToDecoratedChart = getClipToDecoratedChart();
     boolean isClippedToPlotArea = getActuallyClippedToPlotArea();
     // Find min, max for x,y and record each keyword position used
     int nPoints = getNPoints();
     for (int i = 0; i < nPoints; i++) {
        Point p = getPoint(i);
        double x = p.getX();
        double y = p.getY();
        if (Double.MAX_VALUE == x)
           pointAtXAxisMax = true;
        else if (-Double.MAX_VALUE == x)
           pointAtXAxisMin = true;
        else {
          if (x < minX) minX = x;
          if (x > maxX) maxX = x; 
        }
        if (Double.MAX_VALUE == y)
           pointAtYAxisMax = true;
        else if (-Double.MAX_VALUE == y)
           pointAtYAxisMin = true;
        else {
          if (y < minY) minY = y;
          if (y > maxY) maxY = y; 
        }
     }

     // apply "at min/max" keyword, clipping imposed limits
     if (pointAtXAxisMin)
        minX = Math.min(minX, pp.getXMin());
     if (isClippedToPlotArea)
        minX = Math.max(minX, pp.getXMin());
     else if (isClippedToDecoratedChart)
        minX = Math.max(minX, getXAxis().pixelToModel(0));
    
     if (pointAtXAxisMax)
       maxX = Math.max(maxX, pp.getXMax());
     if (isClippedToPlotArea)
       maxX = Math.min(maxX, pp.getXMax());
     else if (isClippedToDecoratedChart)
       maxX = Math.min(maxX, getXAxis().pixelToModel(
           pp.getXChartSizeDecoratedQuickly()));

     boolean onY2 = onY2();
     if (onY2) {
       if (pointAtYAxisMin)
         minY = Math.min(minY, pp.getY2Min());
       if (isClippedToPlotArea)
         minY = Math.max(minY, pp.getY2Min());
       else if (isClippedToDecoratedChart)
         minY = Math.max(minY, getY2Axis().pixelToModel(
             pp.getYChartSizeDecoratedQuickly()));
       if (pointAtYAxisMax)
         maxY = Math.max(maxY, pp.getY2Max());
       if (isClippedToPlotArea)
         maxY = Math.min(maxY, pp.getY2Max());
       else if (isClippedToDecoratedChart)
         maxY = Math.min(maxY, getY2Axis().pixelToModel(0));
     }
     else {
       if (pointAtYAxisMin)
         minY = Math.min(minY, pp.getYMin());
       if (isClippedToPlotArea)
         minY = Math.max(minY, pp.getYMin());
       else if (isClippedToDecoratedChart)
         minY = Math.max(minY, GChart.this.getYAxis().pixelToModel(
             pp.getYChartSizeDecoratedQuickly()));
       if (pointAtYAxisMax)
         maxY = Math.max(maxY, pp.getYMax());
       if (isClippedToPlotArea)
         maxY = Math.min(maxY, pp.getYMax());
       else if (isClippedToDecoratedChart)
         maxY = Math.min(maxY, GChart.this.getYAxis().pixelToModel(0));
     }

// finally, we need to convert to pixels while taking into account
// the size of the rendered symbol itself (e.g. pies can stick
// out from their x,y specified center point, etc.)    
     Symbol sym = getSymbol();
     SymbolType symType = sym.getSymbolType();
// in obscure cases, canvas could clip without this extra wiggle room
     int extraSpace = sym.getFillThickness();
     extraSpace += Math.abs(sym.getBorderWidth());
     double left0 = symType.getEdgeLeft(pp, sym, minX, onY2);
     double left1 = symType.getEdgeLeft(pp, sym, maxX, onY2);
     double right0 = symType.getEdgeRight(pp, sym, minX, onY2);
     double right1 = symType.getEdgeRight(pp, sym, maxX, onY2);
     double bottom0  = symType.getEdgeBottom(pp, sym, minY, onY2);
     double bottom1  = symType.getEdgeBottom(pp, sym, maxY, onY2);
     double top0 = symType.getEdgeTop(pp, sym, minY, onY2);
     double top1 = symType.getEdgeTop(pp, sym, maxY, onY2);

// baseline bars can flip order, so smallest x could be 'right', etc.
     double xPxMin = Math.min(Math.min(left0, left1),
                              Math.min(right0, right1));
     double xPxMax = Math.max(Math.max(left0, left1),
                              Math.max(right0, right1));
     double yPxMin = Math.min(Math.min(bottom0, bottom1),
                              Math.min(top0, top1));
     double yPxMax = Math.max(Math.max(bottom0, bottom1),
                              Math.max(top0, top1));
     result.x = xPxMin - extraSpace;
     result.y = yPxMin - extraSpace;
     result.width = xPxMax - xPxMin + 1 + 2*extraSpace;
     result.height = yPxMax - yPxMin + 1 + 2*extraSpace;      

     // result is (roughly) smallest rectangle that contains every
     // rendered symbol on this curve (ignoring annotations)
    
     return result;

  }

  // keeps track of if last rendering was canvas-based or not  
  private boolean wasCanvasRendered = false;
  void setWasCanvasRendered(boolean wasCanvasRendered) {
     this.wasCanvasRendered = wasCanvasRendered;
  }
  // is curve currently canvas rendered and up-to-date
  boolean isCanvasRendered() {
     return isValidated && wasCanvasRendered;
  }
 
} // end of class GChart.Curve      


     // Allows hovertext templates to be parsed into "chunks"
     // so that they can be expanded into hovertext faster.
     static class HovertextChunk {
        final static int HOVERTEXT_PARAM_NONE = 0; // plain old text
        final static int HOVERTEXT_PARAM_X = 1// ${x}
        final static int HOVERTEXT_PARAM_Y = 2// ${y}
        final static int HOVERTEXT_PARAM_PIESLICESIZE = 3; // ${pieSlicePercent}
        final static int HOVERTEXT_PARAM_USERDEFINED = 4; // ${mySpecialParameter}
        int paramId;      // id of substitution parameter
        String paramName;  // name of substitution parameter
        String chunkText; // plain text that follows this parameter
        HovertextChunk(int id, String name, String text) {
           paramId = id;
           paramName = name;
           chunkText = text;
        }
        // returns array of "chunks" corresponding to the given
        // hovertext template
        static HovertextChunk[] parseHovertextTemplate(
           String htTemplate) {
           if (htTemplate.equals("")) return new HovertextChunk[0];
           // takes "x=${x}; y=${y}" into {"x=", "x}; y=", "y}"}
           // Thus, except for the first, chunks contain a
           // keyword like part, followed by a string literal.
           String[] sChunk = htTemplate.split("\\$\\{");
           HovertextChunk[] result = new HovertextChunk[sChunk.length];

           for (int i = 0; i < sChunk.length; i++) {
              String sC = sChunk[i];
              if (0 == i)
              // leading (non-parametric) plain text chunk
                 result[i] = new HovertextChunk(HOVERTEXT_PARAM_NONE,
                                                null, sC);                
              else if (sC.startsWith("x}"))
                 result[i] = new HovertextChunk(
                                   HOVERTEXT_PARAM_X, "x",
                                   sC.substring("x}".length()));
              else if (sC.startsWith("y}"))
                 result[i] = new HovertextChunk(
                                   HOVERTEXT_PARAM_Y, "y",
                                   sC.substring("y}".length()));
              else if (sC.startsWith("pieSliceSize}"))
                 result[i] = new HovertextChunk(
                                  HOVERTEXT_PARAM_PIESLICESIZE,
                                  "pieSliceSize",
                                  sC.substring("pieSliceSize}".length()));
              else if (sC.matches("[a-zA-Z][a-zA-Z0-9_]*\\}.*")) {
                 // fits pattern for a user defined parameter
                 int closeCurlyIndex = sC.indexOf("}");
                 result[i] = new HovertextChunk(
                                   HOVERTEXT_PARAM_USERDEFINED,
                                   sC.substring(0, closeCurlyIndex),            
                                   sC.substring(closeCurlyIndex+1));
              }
              else {
              // leading "${" without "paramName}". Likely a
              // typo, but output verbatim to give them a clue:
                 result[i] = new HovertextChunk(HOVERTEXT_PARAM_NONE,
                                                null, "${" + sC);
              }
                
           }
           return result;
        }

     /* hovertext associated with parsed "chunks" for a given point */  
     static String getHovertext(HovertextChunk[] htc,
                                Curve.Point p) {
       String result = "";
       String xS = null;
       String yS = null;
       String pieSlicePercentS = null;
       HoverParameterInterpreter hpi =
         p.getParent().getParent().getHoverParameterInterpreter();
       for (int i = 0; i < htc.length; i++) {
          switch (htc[i].paramId) {
             case HovertextChunk.HOVERTEXT_PARAM_NONE:
                break;
             case HovertextChunk.HOVERTEXT_PARAM_X:
                if (null == xS) {
                   String hoverParam = (null == hpi) ? null :
                              hpi.getHoverParameter(htc[i].paramName, p);
                   if (null != hoverParam)
                      xS = hoverParam;
                   else {
                      Axis axis =
                        p.getParent().getParent().getXAxis();
                      xS = axis.formatAsTickLabel(p.getX());
                   }
                }
                result += xS;
                break;
             case HovertextChunk.HOVERTEXT_PARAM_Y:
                if (null == yS) {
                   String hoverParam = (null == hpi) ? null :
                              hpi.getHoverParameter(htc[i].paramName, p);
                   if (null != hoverParam)
                      yS = hoverParam;
                   else {
                      Axis axis = p.getParent().onY2() ?
                          p.getParent().getParent().getY2Axis() :
                          p.getParent().getParent().getYAxis();
                      yS = axis.formatAsTickLabel(p.getY());
                   }
                }
                result+=yS;
                break;
                 
             case HovertextChunk.HOVERTEXT_PARAM_PIESLICESIZE:
                if (null == pieSlicePercentS) {
                   String hoverParam = (null == hpi) ? null :
                              hpi.getHoverParameter(htc[i].paramName, p);
                   if (null != hoverParam)
                      pieSlicePercentS = hoverParam;
                   else {
                      double pieSliceSize =
                         p.getParent().getSymbol().getPieSliceSize();
                      Axis axis = p.getParent().onY2() ?
                              p.getParent().getParent().getY2Axis() :
                              p.getParent().getParent().getYAxis();
                      pieSlicePercentS =
                        axis.formatAsTickLabel(100*pieSliceSize) + "%";
                   }
                }
                result+=pieSlicePercentS;
                break;

             case HovertextChunk.HOVERTEXT_PARAM_USERDEFINED:
               
                String hoverParam = (null == hpi) ? null :
                           hpi.getHoverParameter(htc[i].paramName, p);
                if (null == hoverParam)
                // null means "unrecognized parameter" - so
                // regenerate the original, unparsed, param spec
                // to clue them in that it was not processed.
                   result += "${" + htc[i].paramName + "}";
                else
                   result += hoverParam;
                  
                break;
             default:
                throw new IllegalStateException(
                  "An illegal HOVERTEXT_PARAM_* id: " + htc[i].paramId +
                  " was encountered. A GChart bug is likely to blame.");  
          }
          result+=htc[i].chunkText;
       }
       return result;
     }
   }

  /**
    ** Defines a chart curve symbol. Each point on a curve
    ** is represented on the chart by an appropriate
    ** rendering of the curve's symbol.
    **
    ** @see Curve#getSymbol Curve.getSymbol
    ** @see SymbolType SymbolType
    **
    **/

  public class Symbol {

     private Annotation annotation = null;
     private String backgroundColor = DEFAULT_SYMBOL_BACKGROUND_COLOR;
// same as backgroundColor, but with extended RGBA collapsed to plain RGA
     private String backgroundColorCSS = DEFAULT_SYMBOL_BACKGROUND_COLOR;
     private double baseline = Double.NaN;
     private String borderColor = "black";
     private String borderColorCSS = "black";
     private String  borderStyle = DEFAULT_SYMBOL_BORDER_STYLE;
     private int  borderWidth = DEFAULT_SYMBOL_BORDER_WIDTH;
     private int brushHeight = DEFAULT_BRUSH_HEIGHT;
     private AnnotationLocation brushLocation = AnnotationLocation.CENTER;
     private int brushWidth = DEFAULT_BRUSH_WIDTH;
     private boolean fillHasHovertext = true;
     private double fillSpacing = Double.NaN;
     private int fillThickness = GChart.NAI;
     private int height = DEFAULT_SYMBOL_HEIGHT;
     private String hovertextTemplate=null;
     // holds specification for the hover annotation. Actual
     // hover annotation is generated on the fly when they hover
     private Annotation hoverAnnotation = null;
     private boolean hoverAnnotationEnabled = true;
     // allows hover annotation to use a different symbol type
     // than the symbol being hovered over. Main use expected to
     // be to place hover feedback at a fixed location on the chart (via
     // ANCHOR_* family of symbol types), for example, a status
     // bar message that changes depending on what the mouse
     // is touching.
     private SymbolType hoverAnnotationSymbolType = null;
     // encloses each symbol in a 1 px gray selection rectangle:
     private String hoverSelectionBackgroundColor = "transparent";
     private String hoverSelectionBorderColor = "gray";
     private String hoverSelectionBorderStyle = "solid";
     private int hoverSelectionBorderWidth = -1;
     private boolean hoverSelectionEnabled = true;
     private double hoverSelectionFillSpacing = Double.NaN;
     private int hoverSelectionFillThickness = GChart.NAI;
     private int hoverSelectionHeight = GChart.NAI;
     private String hoverSelectionImageURL = null;
     private int hoverSelectionWidth = GChart.NAI;
     private SymbolType hoverSelectionSymbolType = null;

     private HovertextChunk[] hovertextChunks = null;
     private String imageURL = null;
// XXX: Symbols are used independently of Curves by the
// realizeTick method. But it's probably better to render ticks
// via specialized system curves. If/when that's implemented,
// Symbol should become an inner class of Curve, and this
// explicit parent pointer will no longer be required. 
     private Curve parent = null;
// when specified, model width/height are in user-defined units.
     private double modelHeight = Double.NaN;
     private double modelWidth = Double.NaN;
// NaN means "begin this slice where last slice left off, or at
// initialPieSliceOrientation if it is the first slice to be rendered"
     private double pieSliceOrientation = Double.NaN;
     private double defaultPieSliceOrientation = 0.0;
// slices, by default, fill the entire pie (useful for drawing disks)
     private double pieSliceSize = 1;

     private SymbolType symbolType = DEFAULT_SYMBOL_TYPE;
    
     private int width = DEFAULT_SYMBOL_WIDTH;
     double xScaleFactor = 1.0;
     double yScaleFactor = 1.0;
    
     Symbol(Curve parent) {super(); this.parent = parent;}


    
     /** Returns the CSS background color of all the rectangular
      ** elements used in rendering the symbol.
      **
      ** @return the CSS background color used to fill in the
      **  central (non-border) part of each rectangular element
      **  used to render a curve's symbol.
      **
      ** @see #setBackgroundColor(String) setBackgroundColor
      **/
     public String getBackgroundColor() {
        return backgroundColor;
     }
     String getBackgroundColorCSS() {
        return backgroundColorCSS;
     }
     /** Returns the baseline value for this symbol,
      ** previously specified via <tt>setBaseline</tt>
      **
      **
      ** @return the previously specified baseline value for
      **   this symbol.
      **
      ** @see #setBaseline setBaseline
      **/
     public double getBaseline() {
        return baseline;
     }
    /** Returns the CSS border color of all the rectangular
     ** elements used in rendering the symbol.
     **
     ** <p>
     ** @return the color of the border of the rectangular elements
     **   used to render the symbol, in standard CSS format
     **
     ** @see #setBorderColor setBorderColor
     **
     **/
     public String getBorderColor() {
       return borderColor;
     }
     String getBorderColorCSS() {
        return borderColorCSS;
     }
     /**
     ** Returns the border style of all of the rectangular
     ** elements from which this symbol is built.
     ** <p>
     ** @return the CSS borderStyle of this symbol's elements
     **         (dotted, dashed, solid, etc. )
     ** 
     ** @see #setBorderStyle setBorderStyle
     **/
       public String getBorderStyle() {
          return borderStyle;
       }

     /**
     ** Returns the width of the border around each
     ** rectangular element used to render this symbol,
     ** in pixels.
     **
     ** <p>
     ** @return the previously set border width (in pixels).
     **
     ** @see #setBorderWidth setBorderWidth
      */
       public int getBorderWidth() {
          return borderWidth;
       }
  /**
     *
     * Returns the height of the rectangular "brush" that defines
     * how close the mouse cursor must be to a rendered symbol for
     * the symbol to be considered to have been "touched" (which
     * causes the point's hover feedback to pop up).
     *
     * @return the height of the "brush", in pixels, associated
     * with this symbol/curve.
     *
     * @see #setBrushHeight setBrushHeight
     *
     */
    public int getBrushHeight() {
      return brushHeight;
    }

     /**
      *
      * Returns the location of the rectangular brush relative to
      * the current x,y coordinates of the mouse cursor.
      * <p>
      *
      * @return the location of the rectangular brush relative to
      * the x,y coordinates of the mouse cursor.
      *
      * @see #setBrushLocation setBrushLocation
      */
    public AnnotationLocation getBrushLocation() {
       return brushLocation;
    }


  /**
     *
     * Returns the width of the rectangular "brush" that defines
     * how close the mouse cursor must be to a rendered symbol for
     * the symbol to be considered to have been "touched" (which
     * causes the point's hover feedback to pop up).  <p>
     *
     * @return the width of the "brush", in pixels, associated
     *   with this symbol/curve.
     *
     * @see #setBrushWidth setBrushWidth
     *
     */
    public int getBrushWidth() {
      return brushWidth;
    }

     /**
      ** @deprecated
      **
      ** Returns the value previously set by setFillHasHovertext.
      **
      ** @see #setFillHasHovertext setFillHasHovertext
      **
      **/
       public boolean getFillHasHovertext() {
         return fillHasHovertext;
       }
      
      
     /**
     ** Returns the spacing between successive rectangular
     ** elements used to emulate any required non-rectangular
     ** features of the symbol.  <p>
     **
     **
     **
     ** @return the previously set (or the default, if the
     ** fillSpacing has been set to <tt>Double.NaN</tt>) fill spacing
     ** (in pixels).
     **
     ** @see #setFillSpacing setFillSpacing
     ** @see #setFillThickness setFillThickness
     **
      */
       public double getFillSpacing() {
         if ((fillSpacing!=fillSpacing)) // x!=x is a faster isNaN
           return symbolType.defaultFillSpacing();
         else
           return fillSpacing;
       }

     /**
     ** Returns the "thickness" of rectangular elements used to
     ** emulate any required non-rectangular features of the symbol.
     ** <p>
     **
     **
     ** @return the previously set (or the default, if the
     ** fillThickness has been set to <tt>GChart.NAI</tt>) fill
     ** thickness (in pixels).
     **
     ** @see #setFillThickness setFillThickness
     ** @see #setFillSpacing setFillSpacing
      */
       public int getFillThickness() {
         if (fillThickness==GChart.NAI)
           return symbolType.defaultFillThickness();
         else
           return fillThickness;
       }

       /* Retrieves the annotation that defines the properties of
        * the internally generated annotations used to display
        * hover feedback. */
       Annotation getHoverAnnotation() {
         if (hoverAnnotation == null) hoverAnnotation = new Annotation();
         return hoverAnnotation;
       }
     /**
      * Retrieves a boolean that indicates if point-specific
      * annotations popup whenever you hover over a point on the
      * curve associated with this symbol.<p>
      *
      * @return true if hover-induced annotations popup, false otherwise.
      *
      * @see #setHoverAnnotationEnabled setHoverAnnotationEnabled
      *
      */
     public boolean getHoverAnnotationEnabled() {
       return hoverAnnotationEnabled;
     }
     /**
      ** Retrieves the weight of the font that will be used
      ** with this symbol's hover annotations.
      ** <p>
      **
      ** @return the standard CSS font-weight
      **    specification such as normal, bold, bolder, lighter,
      **    100, 200, ... 900, or inherit used by hover
      **    annotations
      **
      ** @see #setHoverFontWeight setHoverFontWeight 
      **
      **
      **/
    public String getHoverFontWeight() {
      if (hoverAnnotation == null) hoverAnnotation = new Annotation();
      String result = hoverAnnotation.getFontWeight();
      return result;
    }
     /**
      ** Retrieves the font color of this symbol's hover
      ** annotations.
      **
      ** @return color of the font used to display this
      **    symbol's hover annotations
      **
      ** @see #setHoverFontColor setHoverFontColor 
      **/
     public String getHoverFontColor() {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       String result = hoverAnnotation.getFontColor();
       return result;
     }

    
     /**
      ** Retrieves the CSS font-style used with this symbol's
      ** hover annotations.
      **
      ** @return the CSS font-style, namely,
      **   normal, italic, oblique, or inherit of text displayed
      **   in the hover annotations associated with this symbol
      **
      ** @see #setHoverFontStyle setHoverFontStyle 
      **/
     public String getHoverFontStyle() {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       String result = hoverAnnotation.getFontStyle();
       return result;
     }
     /**
      ** Retrieves the CSS font size used with this symbol's hover
      ** annotations, in pixels.
      **
      ** @return the font size used in the text displayed
      ** in the hover annotations associated with this symbol.
      **
      ** @see #setHoverFontSize setHoverFontSize
      **
      **/
     public int getHoverFontSize() {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       int result = hoverAnnotation.getFontSize();
       return result;
     }

    /**
     * Retrieves point-relative location of this symbol's hover
     * annotations.  <p>
     *
     * @return the relative location of the hover annotations for
     * all points on the curve associated with this symbol.
     *
     * @see #setHoverLocation setHoverLocation
     * @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
     *
     */
    public AnnotationLocation getHoverLocation() {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       AnnotationLocation result = hoverAnnotation.getLocation();
       if (null == result) result = getSymbolType().defaultHoverLocation();
       return result;
    }
    /**
     * Retrieves the symbol type that will determine how the
     * hover annotations for this symbol gets positioned.
     * <p>
     *
     * @return <tt>SymbolType</tt> used to position hover
     * annotations, or <tt>null</tt> if the symbol type of the
     * hovered over point is being used.
     *
     * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
     *
     */
    public SymbolType getHoverAnnotationSymbolType() {
       return hoverAnnotationSymbolType;  
    }
     /**
      * Retrieves the background color used to indicate that the mouse is
      * "touching" (hovering over) a point.
      *
      * @return a CSS color specification string that represents
      * the background color used to indicate "hover-selection".
      *
      * @see #setHoverSelectionBackgroundColor
      * setHoverSelectionBackgroundColor
      */
     public String getHoverSelectionBackgroundColor() {
       return hoverSelectionBackgroundColor;
     }
     /**
      * Retrieves the border color used to indicate that the mouse is
      * "touching" (hovering over) a point.
      *
      * @return a CSS color specification string that represents
      * the border color used to indicate "hover-selection".
      *
      * @see #setHoverSelectionBorderColor
      * setHoverSelectionBorderColor
      */
     public String getHoverSelectionBorderColor() {
       return hoverSelectionBorderColor;
     }
     /**
      * Retrieves the border style used to indicate that the mouse is
      * "touching" (hovering over) a point.
      *
      * @return a CSS border style specification string that represents
      * the border style used to indicate "hover-selection".
      *
      * @see #setHoverSelectionBorderStyle
      * setHoverSelectionBorderStyle
      */
     public String getHoverSelectionBorderStyle() {
       return hoverSelectionBorderStyle;
     }
     /**
      * Retrieves the width of the border around the perimeter of
      * rectangles used to indicate that the mouse is
      * "touching" (hovering over) a point.
      * <p>
      *
      *
      * @return the width of the border drawn around the perimeter of
      * the selected symbol's rectangles to indicate that it has
      * been "touched: by the mouse.
      *
      * @see #setHoverSelectionBorderWidth
      * setHoverSelectionBorderWidth
      */
     public int getHoverSelectionBorderWidth() {
       return hoverSelectionBorderWidth;
     }
     /**
      * Retrieves a boolean that indicates if hover selection
      * feedback will be provided for this curve.  <p>
      *
      * @return if true, hover selection feedback is enabled,
      *   if false, hovering over a point does not change its
      *   color.
      *
      * @see #setHoverSelectionEnabled setHoverSelectionEnabled
      *
      */
     public boolean getHoverSelectionEnabled() {
       return hoverSelectionEnabled;
     }

     /**
      * Returns the fill spacing that will be used when
      * rendering this curve's hover selection feedback.
      * <p>
      *
      * @return fill spacing used by hover selection feedback,
      * or <tt>GChart.NAI</tt> if the fill spacing setting
      * of the hovered-over curve is to be used.
      *
      * @see #setHoverSelectionFillSpacing
      *   setHoverSelectionFillSpacing
      *
      */
     public double getHoverSelectionFillSpacing() {
       return hoverSelectionFillSpacing;
     }
     /**
      * Returns the fill thickness that will be used when
      * rendering this curve's hover selection feedback.
      * <p>
      *
      * @return fill thickness used by hover selection feedback,
      * or <tt>GChart.NAI</tt> if the fill thickness setting
      * of the hovered-over curve is to be used.
      *
      * @see #setHoverSelectionFillThickness
      *       setHoverSelectionFillThickness
      *
      */
     public int getHoverSelectionFillThickness() {
       return hoverSelectionFillThickness;
     }
    
     /**
      * Returns the height of the symbol used to indicate
      * when a given point is being "hovered over" with the
      * mouse.
      * <p>
      *
      * @return the height of the symbol used to
      * indicate that that a point has been selected, or
      * <tt>GChart.NAI</tt> if the the height of the
      * symbol representing the selected point is being used.
      *
      *
      * @see #setHoverSelectionHeight setHoverSelectionHeight
      *
      */
      public int getHoverSelectionHeight() {
         return hoverSelectionHeight;
      }

     /**
      * Returns the URL that will be used for all of the
      * images used in rendering this symbol's selection feedback.
      * <p>
      *
      * @see #setHoverSelectionImageURL
      *
      * @return the url that defines the <tt>src</tt> property of all
      * images used to draw this the selection feedback associated
      * with this symbol.
      *
      */

      public String getHoverSelectionImageURL() {
         String result = (null == hoverSelectionImageURL) ?
                         getBlankImageURL() :
                         hoverSelectionImageURL;
         return result;                       
      }


    /**
     *
     * Returns the symbol type that GChart will use when generating
     * selection feedback. GChart indicates that a point is
     * selected by re-rendering the point as if it had this symbol
     * type.
     *
     *
     * @return the symbol type that in
     * part determines how selection feedback for a hovered over
     * point is drawn, or <tt>null</tt> if defaulting to the
     * symbol type of the hovered over point.
     *  
     * @see #setHoverSelectionSymbolType setHoverSelectionSymbolType
     *
     */
     public SymbolType getHoverSelectionSymbolType() {
       return hoverSelectionSymbolType;
     }
     /**
      * Returns the width of the symbol used to indicate
      * when a given point is being "hovered over" with the
      * mouse.
      * <p>
      *
      * @return the width of the symbol used to indicate that
      * that a point has been selected, or <tt>GChart.NAI</tt> if
      * using to the width of the symbol representing the
      * selected point.
      *
      *
      * @see #setHoverSelectionWidth setHoverSelectionWidth
      *
      *
      */
      public int getHoverSelectionWidth() {
         return hoverSelectionWidth;
      }
    
  /**
   ** Returns the hovertextTemplate of this symbol.
   ** <p>
   **
   ** @return hovertextTemplate of the symbol
   **
   **
   ** @see #setHovertextTemplate(String) setHovertextTemplate
   **
   **/
       public String getHovertextTemplate() {
         if (null == hovertextTemplate)
           return symbolType.defaultHovertextTemplate();
         else
           return hovertextTemplate;
       }

     /**
      * When widget-based hover annotations are being used
      * by the curve associated with this symbol, this method returns
      * the <tt>HoverUpdateable</tt> widget used within
      * those annotations. When simple text or HTML hover
      * annotations are being used, it returns null.
      *
      * @return the widget used to provide widget-based hover
      * annotations or null if hover annotations are not
      * widget-based.
      *
      * @see #setHoverWidget setHoverWidget
      *
      */
     public HoverUpdateable getHoverWidget() {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       return (HoverUpdateable) hoverAnnotation.getWidget();
     }



   
    /**
     * Retrieves the number of pixels (along the x-axis) that
     * this point's hover-annotation will be moved from its default,
     * <tt>setHoverLocation</tt>-defined, point-relative location. 
     * <p>
     *
     * @return x-shift, in pixels, of the hover annotation
     * 
     * @see #setHoverXShift getHoverXShift
     *
     */
    public int getHoverXShift() {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       int result = hoverAnnotation.getXShift();
       return result;
    }

    /**
     * Retrieves the number of pixels (along the y-axis) that
     * this point's hover annotation will be moved from its default,
     * <tt>setHoverLocation</tt>-defined, point-relative location. 
     * <p>
     *
     * @return y-shift, in pixels, of the hover annotation
     *
     * @see #setHoverYShift setHoverYShift
     *
     */
    public int getHoverYShift() {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       int result = hoverAnnotation.getYShift();
       return result;
    }
   
     /**
      * Returns the URL that will be used for all of the
      * images used in rendering this symbol.
      * <p>
      *
      * @see #setImageURL setImageURL
      * @see #setBlankImageURL setBlankImageURL
      *
      * @return the url that defines the <tt>src</tt> property of all
      * images used to draw this symbol on the chart.
      */
     public String getImageURL() {
        String result = (null == imageURL) ?
                        getBlankImageURL() :
                        imageURL;
        return result;
     }
       // returns an internal, parsed form of the hovertext template
       HovertextChunk[] getHovertextChunks() {
          if (null == hovertextChunks)
             hovertextChunks =  HovertextChunk.parseHovertextTemplate(
                getHovertextTemplate());

          return hovertextChunks;
       }
      
      /** Returns the <tt>Curve</tt> that contains this
       ** <tt>Symbol</tt>.
       **
       ** @return a reference to the <tt>Curve</tt> that
       **   contains this <tt>Symbol</tt> (its "parent")
       **
       **/  
       public Curve getParent() {return parent;}
      
    /**
     ** Returns the value, previously specified via
     ** <tt>setPieSliceOrientation</tt>, that defines the angular
     ** orientation of any pie slices associated with this
     ** symbol.  <p>
     **
     **
     ** @return the value, either <tt>Double.NaN</tt> or a value
     **         between 0 and 1 previously set via
     **         <tt>setPieSliceOrientation</tt>, that determines the
     **         angular orientation of any pie slice associated
     **         with this symbol.
     **
     ** @see #setPieSliceOrientation setPieSliceOrientation
     ** @see #setPieSliceSize setPieSliceSize
     **
      */
       public double getPieSliceOrientation() {
         return pieSliceOrientation;
       }
       // Used internally to translate <tt>Double.NaN</tt> into
       // an appropriate default slice orientation that, when pie
       // slice orientation isn't explicitly specified, results
       // in a series of adjacent slices that will form a pie
       // when the sum of the slice sizes equals 1.0
      
       double getDecodedPieSliceOrientation() {
         double result = pieSliceOrientation;
         if ((result!=result)) // x!=x is a faster isNaN
           result = defaultPieSliceOrientation;
         return result;
       }

       void setDefaultPieSliceOrientation(double defaultOrientation) {
          defaultPieSliceOrientation = defaultOrientation;
       }
       double getDefaultPieSliceOrientation() {
          return defaultPieSliceOrientation;
       }
      
    /**
     ** Returns the value, previously specified via
     ** <tt>setPieSliceSize</tt>, that defines the size of
     ** the angle subtended by any pie slice associated with this
     ** symbol.  <p>
     **
     ** @return the value, between 0 and 1 and previously set via
     **         <tt>setPieSliceSize</tt>, that defines the
     **         size of the "wedge of pie" as a fraction of
     **         the total pie, for any pie slice associated
     **         with this symbol.
     **
     ** @see #setPieSliceOrientation setPieSliceOrientation
     ** @see #setPieSliceSize setPieSliceSize
     **
      */
       public double getPieSliceSize() {
          return pieSliceSize;
       }


    /*
     * Returns the radius of the pie from which this
     * symbol's pie slice was extracted.
     *
     */
     double getPieSliceRadius(PlotPanel pp, boolean onY2) {
         double w = getWidth(pp);      // needed to decode model
         double h = getHeight(pp,onY2);// width,height into pixels
         double result = Math.sqrt(w*w + h*h)/2.;
  // Tweak radius to assure it is an even multiple of the fill
  // spacing. Makes it possible to assure regular band spacing
  // across pie at the expense of less precise control of pie
  // size (regular band spacing makes it look much better).
         double spacing = getFillSpacing();
         if (0 == spacing) spacing = 1;
         int nBands = (int) Math.round(result/spacing);
         result = nBands * spacing;
         return result;
       }

       // defines first, second edge angle in standard radian units
       double getPieSliceTheta0() {
         double result;
         result = (0.75 - getDecodedPieSliceOrientation())*2*Math.PI;
         return result;
       }
       double getPieSliceTheta1() {
         return getPieSliceTheta0() - 2.*Math.PI*getPieSliceSize();
       }

    /**
     ** Returns this symbol's height, as previously set by
     ** <tt>setHeight</tt>.
     **
     ** @return the previously set symbol height, in pixels.
     **
     ** @see #setHeight setHeight
      */
       public int getHeight() {
          return height;
       }
  /**
   ** Returns this symbol's height as previously set by
   ** <tt>setModelHeight</tt>.
   **
   ** @return the previously set symbol height, in model units
   **
   ** @see #setModelHeight setModelHeight
   ** @see #setModelWidth setWidth
   ** @see #setHeight setHeight
   ** @see #setWidth setWidth
   **
    */
     public double getModelHeight() {
        return modelHeight;
     }

    
     /**
     ** Returns this symbol's width as previously set by
     ** <tt>setModelWidth</tt>.
     **
     ** @return the previously set symbol width, in model units.
     **
     ** @see #setModelWidth setModelWidth
     ** @see #setModelHeight setModelHeight
     ** @see #setWidth setWidth
     ** @see #setHeight setHeight
     **
      */
       public double getModelWidth() {
          return modelWidth;
       }
  /** Returns this symbol's type.
    **
    ** @return the type of this symbol.
    ** @see #setSymbolType setSymbolType
    **
    **/
   public SymbolType getSymbolType() {
      return symbolType;
   }
  /**
   ** Returns this symbol's width
   ** as previously set by <tt>setWidth</tt>.
   ** <p>
   **
   ** <i>Warning:</i> This method won't return the correct
   ** pixel width associated with a <tt>setModelWidth</tt>
   ** setting, as you might have expected.  It only returns
   ** the pixel width you last explicitly specified via
   ** <tt>setWidth</tt>.
   **
   ** <p>
   **
   ** @return the previously set symbol width, in pixels
   **
   ** @see #setWidth setWidth
   ** @see #setModelWidth setModelWidth
    */
     public int getWidth() {
        return width;
     }

     /*
      * Do points on the curve associated with this symbol
      * use a horizontal (or vertical) binning strategy for
      * "what point is the mouse over" hit testing?
      *
      */
     boolean isHorizontallyBanded() {
        boolean result;
        if (null == symbolType.isHorizontallyBanded)
           // not fixed by symbol type: use brush shape determined banding
           // (we are guessing point distribution based on brush shape)
           result = brushHeight < brushWidth;          
        else
           result = symbolType.isHorizontallyBanded.booleanValue();

        return result;
     }

    
     /*
      * If passed an rgba-like string (rgba(255,255,128,0.5))
      * returns the collapsed-to-rgb version (rgb(255,255,128)).
      * Else returns the original string. Throws an exception
      * if string begins with rgba( but lacks required
      * format after that.
      *
      */
     private String collapseRGBAToRGB(String rgba) {
// an int in the range 0..255 for the "R,G,B" parts
        final String RGB =
           "([0-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9])|(25[0-5]))";
// a double in the range 0..1 for the "A" part
        final String A = "(0|1|(1[.]0*)|(0[.][0-9]*)|([.][0-9]+))";
// full RGBA pattern
        final String RGBA_PATTERN =
           "rgba[(]" + RGB + "[,]" + RGB + "[,]" + RGB + "[,]" + A + "[)]";
        String result = rgba;
        if (null != rgba && rgba.startsWith("rgba(")) {
           if (rgba.matches(RGBA_PATTERN)) {
             final int FIRST_PAREN = 4;
             int lastComma = rgba.lastIndexOf(",");
             result = "rgb" +
                      rgba.substring(FIRST_PAREN, lastComma) + ")";
           }
           else
              throw new IllegalArgumentException(
"Your RGBA color specification: '" + rgba + "'" +
" was not in the GChart-required form: rgba(Red,Green,Blue,Alpha)" +
" where Red, Green and Blue are integers in the range 0 to 255 and" +
" Alpha is a double in the range 0.0 to 1.0");
        }
        // else special keyword or else some (unchecked) CSS color format
        return result;
     }
    
  /**
   ** Specifies the background or fill color of this symbol.
   ** <p>
   **
   ** For example, this would define the color of the interior,
   ** non-border, part of bars in a bar-chart, the color of the
   ** interior of each shading bar in a banded-fill pie slice, or
   ** the canvas "fill" color of solid fill, canvas-rendered, pie
   ** slices. <p>
   **
   ** You can use one of the 16 standard HTML/CSS color literals,
   ** illustrated below, to quickly specify common colors:
   **
   ** <p><ul>
   ** <img
   ** src="{@docRoot}/com/googlecode/gchart/client/doc-files/gchartexample10.png">
   ** </ul>
   **
   ** <p> For more variety, use a standard CSS RGB (<b>r</b>ed,
   ** <b>g</b>reen, and <b>b</b>lue) color format such as "#FF0000"
   ** (same as "red"), "#00FF00" (same as "green"), "#0000FF" (same as
   ** "blue"), "#FFFFFF" (same as "white") or "#000000" (same as
   ** "black").  <p>
   **
   ** If you are using an external canvas library that supports
   ** them, feel free to use RGBA color specifications
   ** (e.g, <tt>rgba(255,255,255,0.5))</tt> for semi-transparent
   ** white).  GChart will automatically collapse these
   ** specifications down to CSS standard RGB (e.g.
   ** <tt>rgb(255,255,255)</tt>) as needed for any
   ** non-canvas-rendered parts of the curve.
   ** <p>
   **
   ** <small> To maintain <tt>GWTCanvas</tt> consistency, GChart requires that
   ** you use integers in the range <tt>0..255</tt> in the first three
   ** comma delimited RGBA slots, and a double in the range
   ** <tt>0..1</tt> in the fourth. An runtime exception will be raised
   ** if you use any other format. <p>
   ** </small>
   **
   ** <i>Why GChart sometimes deliberately drops your
   ** alpha-transparency specs (and how to workaround this limitation):</i>
   **
   ** <p>
   ** <blockquote>
   ** <small>
   ** <p>
   **
   ** The external <tt>GWTCanvas</tt> vector graphics library lets you
   ** use the RGBA format, which adds a fourth,
   ** <tt><b>a</b>lpha</tt> parameter in addition to the standard
   ** <tt><b>r</b>ed</tt>, <tt><b>g</b>reen</tt> and
   ** <tt><b>b</b>lue</tt>. This <tt>alpha</tt> lets you define the
   ** degree of transparency: from 0 (transparent) to 1 (opaque).<p>
   **
   ** Problem is, if you use this <tt>rgba(r,g,b,a)</tt> syntax to specify, say,
   ** the color of an HTML element's border, IE won't display anything
   ** but an error.  So, whenever GChart uses an HTML element to render
   ** a symbol's background or border (instead of your external vector
   ** graphics library) it collapses the RGBA into the more
   ** widely accepted RGB format, by dropping the fourth,
   ** <tt>alpha</tt>, parameter and changing the leading <tt>rgba</tt>
   ** identifier to <tt>rgb</tt>.<p>
   **
   ** GChart will render any "continuously filled" (you can use
   ** <tt>setFillSpacing(0)</tt> to request continuous filling)
   ** aspects of your symbols using whatever external vector
   ** graphics library you specified via
   ** <tt>setCanvasFactory</tt>. So, you can rely on the full
   ** RGBA syntax working for these aspects of your curve,
   ** provided that, like <tt>GWTCanvas</tt>, your external graphics
   ** library supports the <tt>rgba(r,g,b,a)</tt> syntax.
   ** Specifically, solid fill pie slices and their borders,
   ** solid fill area charts and their borders, and solid
   ** point-to-point connecting lines on a line chart all support
   ** RGBA-specified alpha-transparency.  <p>
   **
   ** On the other hand, due to a limitation of GChart's
   ** implementation, any rectangular aspects of your symbols,
   ** namely bar chart symbols and their borders, rectangular
   ** point markers and their borders, and banded-filled pie
   ** slices, are rendered via HTML and will collapse your
   ** <tt>rgba(r,g,b,a)</tt> specs into <tt>rgb(r,g,b)</tt>.<p>
   **
   ** But what if you need a semi-transparent bar chart,
   ** banded-fill pie slice, or rectangular point marker?
   ** Fortunately, there is a simple workaround: just pass the
   ** URL of an appropriately-sized semi-transparent image to the
   ** <tt>setImageURL</tt> method of the curve in question.
   **
   ** </small>
   ** </blockquote>
   ** <p>
   ** 
   ** <p>
   ** The default symbol background color is
   ** <tt>DEFAULT_SYMBOL_BACKGROUND_COLOR</tt>
   **
   **
   ** @param backgroundColor a standard CSS or canvas-library
   ** supported RGBA background color specification string.
   **
   **
   ** @see #getBackgroundColor getBackgroundColor
   ** @see #setBorderColor setBorderColor
   ** @see #DEFAULT_SYMBOL_BACKGROUND_COLOR DEFAULT_SYMBOL_BACKGROUND_COLOR
   ** @see #setImageURL setImageURL
   **
   **/
     public void setBackgroundColor(String backgroundColor) {
        getParent().invalidate();
        this.backgroundColor = backgroundColor;
// don't want to keep collapsing whenever we render, so save it:
        backgroundColorCSS = collapseRGBAToRGB(backgroundColor);
     }

     /** Specifies the baseline value for this symbol. Use a
      ** baseline value when you need to create bar charts whose
      ** bars extend up/down to a specified y baseline value (for
      ** vertical bar charts) or left/right to a specified x baseline
      ** value (for horizontal bar charts).
      **
      ** <p>
      **
      ** In greater detail:
      ** <p>
      ** <ul>
      **
      ** <li>For curves that employ symbol types with names of
      ** the form <tt>VBAR_BASELINE_*</tt>, a vertical bar is
      ** drawn that connects the x,y position of each data point
      ** to the horizontal line defined by the equation
      ** <tt>y=baseline</tt>.  For the default baseline setting
      ** of <tt>Double.NaN</tt>, the defining equation is
      ** <tt>y=(yMin+yMax)/2</tt> (i.e., a midpoint baseline).
      **
      ** <p> <li>For curves that employ symbol types with names
      ** of the form <tt>HBAR_BASELINE_*</tt>, a horizontal bar
      ** is drawn from the x,y position associated with each data
      ** point to the vertical line defined by the equation
      ** <tt>x=baseline</tt>. For the default baseline setting of
      ** <tt>Double.NaN</tt>, the defining equation is
      ** <tt>x=(xMin+xMax)/2</tt>.
      ** </ul>
      **
      ** <p>
      **
      ** @param baseline the y (or x) that defines the horizontal
      **   (or vertical) line to which any baseline-based vertical
      **   (or horizontal) bars are extended.
      **  
      ** @see #getBaseline getBaseline
      ** @see SymbolType#HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
      ** @see SymbolType#HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
      ** @see SymbolType#HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
      ** @see SymbolType#VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
      ** @see SymbolType#VBAR_BASELINE_EAST VBAR_BASELINE_EAST
      ** @see SymbolType#VBAR_BASELINE_WEST VBAR_BASELINE_WEST
      **
      **/
     public void setBaseline(double baseline) {
        getParent().invalidate();
        this.baseline = baseline;
     }

    
  /**
   ** Specifies the border color, as a CSS or RGBA color
   ** specification string.
   **
   ** <p>
   **
   ** Both standard CSS and the all-but-IE-supported
   ** RGBA extension (with the fourth paramter defining
   ** alpha-transparency, for example,
   ** <tt>rgba(255,255,255,0.5)</tt> to define a semi-transparent
   ** white) are allowed. However, GChart may collapse
   ** these extended specifications into ordinary RGB in
   ** some cases. See {@link Symbol#setBackgroundColor
   ** setBackgroundColor} for full details.
   **
   ** <p>
   **
   ** For example, for a square symbol, this would set the color
   ** of the line that indicates the outter perimeter of that
   ** square.  For a banded-fill pie slice, this would set the
   ** color of the outter perimeter of every shading bar used to
   ** fill in the pie slice. For a solid fill pie slice, with an
   ** external canvas library such as <tt>GWTCanvas</tt> plugged
   ** into GChart via <tt>setCanvasFactory</tt>, this method
   ** would instead set the color of the canvas-stroked line
   ** around the slice's perimeter.
   **
   ** <p>
   **
   ** This color also defines the color of the point-to-point
   ** connecting lines of a continuously connected line chart.
   ** <p>
   **
   **
   ** <i>Using <tt>TRANSPARENT_BORDER_COLOR</tt>:</i>
   ** <p>
   **
   ** <blockquote>
   ** <small>
   ** <p>Although you can use the special CSS keyword
   ** "transparent", due to a well-known bug, IE6 will usually render
   ** that as black. Plus, different browsers define the background
   ** color that shines through the transparent border
   ** differently, and external canvas libraries such as
   ** <tt>GWTCanvas</tt> may not support the use of "transparent".  So,
   ** GChart provides a browser-independent keyword
   ** <tt>TRANSPARENT_BORDER_COLOR</tt> that you can use instead, that
   ** emulates transparency by removing the border entirely, and then
   ** shifting and resizing the transparently bordered element so as to
   ** create the illusion that its transparent border is still there.
   ** That's not exactly the same thing as having a transparent border
   ** (the emulated borders can't "see" mouse moves over them, for
   ** example) but it's close enough for most charting purposes. Another
   ** simple way to workaround these transparent border inconsistencies is
   ** to use an explicitly specified border color equal to the chart's
   ** background color.
   ** </small>
   ** </blockquote>
   **
   **
   ** @param borderColor the color of the borders of this curve's rendered
   ** symbols, and of any point-to-point connecting lines. Use any
   ** valid CSS color specification string (including the
   ** RGBA extension), or the special
   ** GChart keyword <tt>TRANSPARENT_BORDER_COLOR</tt>.
   **
   ** For more information on standard CSS color specifications
   ** including
   ** how GChart handles the RGBA extended format, see
   ** {@link Symbol#setBackgroundColor setBackgroundColor}.
   **
   ** @see #TRANSPARENT_BORDER_COLOR TRANSPARENT_BORDER_COLOR
   ** @see #getBorderColor getBorderColor
   ** @see #setBackgroundColor setBackgroundColor
   ** @see #setCanvasFactory setCanvasFactory
   **
   **/
     public void setBorderColor(String borderColor) {
        getParent().invalidate();
        this.borderColor = borderColor;
        borderColorCSS = collapseRGBAToRGB(borderColor);
     }
    
  /**
   ** Sets the border style of the rectangular elements used
   ** to render this symbol.
   **
   ** <p>
   **
   ** <p>
   ** @param borderStyle a CSS border style such as
   ** "solid", "dotted", "dashed", etc.
   **
   ** @see #getBorderStyle getBorderStyle
   ** @see #setBackgroundColor setBackgroundColor
   ** @see #setBorderColor setBorderColor
   **/
     public void setBorderStyle(String borderStyle) {
       getParent().invalidate();
       this.borderStyle = borderStyle;
     }
  /**
   ** Sets the width of the border around the graphical
   ** element(s) used to render this curve, in pixels.
   ** <p>
   **
   ** If positive, the border is drawn inside each graphical
   ** element. If negative, the border is drawn outside of
   ** those elements. Note that, negative, external, borders do not
   ** increase the mouse hit-testing region associated with
   ** the rendered symbols.
   ** <p>
   **
   ** <blockquote><small> <i>Tip:</i> To get the hit test region
   ** around a symbol to, <i>in effect</i>, include the external border
   ** associated with a negative border width, simply increase the width
   ** and the height of the symbol's point selection brush by
   ** <tt>2*Math.abs(getBorderWidth())</tt></small> </blockquote>
   **
   **
   ** <p> If a rectangular symbol's width or height ever become
   ** less than twice the specified positive border width, the
   ** border will be shrunk down until it just fills up the
   ** entire rectangular area of the symbol.
   ** <p>
   **
   ** On backgrounds vs borders in bar charts when printing
   ** <p>
   **
   ** <blockquote><small> Because borders are usually printed more reliably
   ** than background colors cross-browser, using such oversized
   ** borders is a simple way to assure that on-screen and
   ** printed bar chart renderings are consistent. Consistency
   ** isn't everything: the removal of background colors can save
   ** ink. So, you might prefer to just design bar charts that
   ** look good with either solid-filled (on-screen) or outlined
   ** (printed w.  'print background colors' unchecked in FF, for
   ** example) bars.  </small></blockquote>
   **
   ** @param borderWidth the width of the symbol's border, in pixels
   ** @see #getBorderWidth getBorderWidth
   **/
     public void setBorderWidth(int borderWidth) {
        getParent().invalidate();
        this.borderWidth = borderWidth;
     }

  /**
     *
     * Sets the height of the rectangular point-selection
     * "brush". This brush defines how close the mouse
     * must get to a point on the chart in order to "touch" it.
     * <p>
     *
     * Whenever a point is touched, GChart displays that point's
     * hover feedback. By default, the selected point is
     * highlighted, the associated symbol's hovertext template
     * is expanded relative to the touched point, and the
     * resulting plain text or HTML is placed onto the chart at
     * an appropriate, point-relative, location. You can
     * override this default by passing a
     * <tt>HoverUpdateable</tt> <tt>Widget</tt> to the the
     * <tt>setHoverWidget</tt> method. In that case, instead of
     * expanding your hovertext template, the
     * <tt>HoverUpdateable</tt>
     * <tt>Widget</tt> is instead positioned properly relative to the
     * touched point, and its <tt>hoverUpdate</tt> method is
     * invoked with a reference to the touched point passed in
     * as its first argument.  <p>
     *
     * The rules for determining if the brush is "touching" a symbol
     * are as follows:
     *
     * <ol>
     *
     *   <li>For all symbols <i>except</i> pie slices, the symbol
     *   is considered to have been touched if the rectangle
     *   containing the rendered symbol, and a rectangle with
     *   the specified brush width and height, and centered
     *   on
     *   the current mouse position, intersect.
     *   <p>
     *
     *  
     *   <small><i>Note:</i> The brush is centered on the mouse by
     *   default. You can place the brush above, below, etc.
     *   the mouse position via the
     *   <tt>setBrushLocation</tt> method.
     *   </small>
     *   <p>
     *  
     *   For example, with a 10 x 10 pixel brush, a bar
     *   chart's bar gets "touched" whenever the mouse is either
     *   within, or within a 5 pixel border around, the bar.  Note
     *   that on line charts, only the individual (rectangular)
     *   point markers, not connecting lines between them, are
     *   touchable.  <p>
     *
     *   <li>For pie slices, the slice is considered to have been
     *   touched if the mouse cursor is within the angle subtended
     *   by the slice, and within <tt>sliceRadius + max(brushWidth,
     *   brushHeight)/2.0</tt> of the center of the pie containing
     *   the slice. Intuitively, the brush in effect aligns its
     *   longest dimension, compass-like, so that it points to the
     *   center of the pie containing the slice.
     *   <p>
     *   <small><i>Note:</i> Unlike other symbol types, pie
     *   slice hit testing works the same regardless
     *   of the brush location (c.f. <tt>setBrushLocation</tt>)
     *   setting.  </small>
     *  
     *  </ol>
     *
     * <p>
     * Even though each curve's symbol can have an
     * independently sized brush--which gives you a lot of
     * control over which curve's points are easiest to
     * select--using the same sized brush for all curves
     * produces results more consistent with the simple concept
     * of a single physical brush "touching" symbols on the
     * chart.
     *
     * <p>
     *
     * Finally, if the brush touches more than one symbol, the
     * symbol whose center is closest to the mouse cursor (by
     * default, that's also the brush center point) is
     * considered to have been touched.  (In these calculations,
     * the ordinary, Euclidean, definition of distance is used
     * by default; the <tt>setDistanceMetric</tt> method allows
     * for other definitions). For pie slices, the symbol's
     * center point is defined (to simplify the calculations) as
     * the point at the center of the pie that contains the
     * slice. In case of any remaining ties, the point later on
     * the point list (the one "on top") is selected.  <p>
     *
     *
     *
     * <small>
     * <i>Fine-print:</i> Known brush size limitations when
      * selecting off-chart points:
     *
     * <blockquote>
     *   GChart allows you to render points that fall outside
     *   of the GChart's containing rectangle.
     *   <p>
     *  
     *   But, GChart can't see mouse moves around such off-chart
     *   elements--the mouse has to be right on top of these
     *   elements before it's motion is detected by GChart (the
     *   browser sends mouse moves in these regions to whatever
     *   non-GChart related browser elements occupy this space).
     *  
     *   <p> Consequently, even though GChart still uses your
     *   brush settings for hit testing with such points,
     *   because it can't see mouse activity until the center of
     *   the mouse "touches" such elements, it will often act
     *   <i>as if</i> you were using a <tt>1x1</tt> pixel brush for such
     *   externally rendered points.  <p>
     *
     *   Unfortunately, this is a basic limitation of how GChart
     *   handles mouse events. But there are a number of
     *   possible workarounds, such as adding a transparent
     *   curve with similar, but larger,
     *   dimensions/point locations to the off-chart curve's
     *   points (so as to capture external mouse moves around
     *   external points) or adding a transparent
     *   annotation around external points (again, to create a
     *   mouse capture region around these points). Perhaps the
     *   easiest approach is to simply make the outter
     *   decoration regions around the chart big enough (via
     *   methods such as <tt>setAxisLabelThickness</tt> and
     *   similarly named <tt>set*Thickness</tt> methods) so that
     *   there is enough empty space around the plot area's
     *   perimeter so that points never fall outside the chart's
     *   bounding rectangle.
     *   <p>
     *
     *   Because most applications place their points on the
     *   chart, this is only an issue for advanced applications
     *   that deliberately draw outside of the chart's rectangle so
     *   as to overlay the chart onto the host page.
     *
     *
     * </blockquote></small>
     *
     *
     *
     * @param height the height of the rectangular point
     * selection brush used by points on the curve associated
     * with this symbol (in pixels).
     *
     * @see #getBrushHeight getBrushHeight
     * @see #setBrushWidth setBrushWidth
     * @see #setBrushSize setBrushSize
     * @see #setBrushLocation setBrushLocation
     * @see #setDistanceMetric setDistanceMetric
     * @see Symbol#setHoverWidget setHoverWidget
     * @see HoverUpdateable HoverUpdateable
     * @see #DEFAULT_BRUSH_WIDTH DEFAULT_BRUSH_WIDTH
     * @see #DEFAULT_BRUSH_HEIGHT DEFAULT_BRUSH_HEIGHT
     * @see #getTouchedPoint getTouchedPoint
     * @see #touch touch
     * @see GChart#setHoverTouchingEnabled setHoverTouchingEnabled
     *
     */
    public void setBrushHeight(int height) {
       brushHeight = height;
     }

     /**
      * Sets the location of the brush relative to the mouse
      * x,y coordinates.
      * <p>
      *
      * With the default setting of
      * <tt>AnnotationLocation.CENTER</tt>, the brush is centered
      * on the mouse cursor, which is usually acceptable.
      * <p>
      *
      * The most useful non-default settings, which facilitate
      * "single sided" point selection, are tabulated below:
      *
      * <table border>
      <tr><th>Location</th><th>Impact on point
      *  selection</th></tr>
      <tr><td><tt>AnnotationLocation.NORTH</tt></td> <td>The
      *  mouse
      *  only selects points when it is on or below them.</td></tr>
      <tr><td><tt>AnnotationLocation.SOUTH</tt></td> <td>The
      *  mouse
      *  only selects points when it is on or above them.</td></tr>
      <tr><td><tt>AnnotationLocation.WEST</tt></td> <td>The
      *  mouse
      *  only selects points when it is on them or to their right.</td></tr>
      <tr><td><tt>AnnotationLocation.EAST</tt></td> <td>The
      *  mouse
      *  only selects points when it is on them or to their left.</td></tr>
      *
      * </table>
      * <p>
      *
      * <i>Tip:</i> When a chart has two curves, setting one
      * curve to use <tt>NORTH</tt> as its brush location, and
      * the other to use <tt>SOUTH</tt>, and using a brush height
      * equal to the height of the decorated chart
      * (returned via <tt>getYChartSizeDecorated()</tt>) allows points
      * on one curve to be selected when the user is near the top
      * of the chart, and points on the other curve to be
      * selected when the user is near the bottom of the chart.
      * <p>
      *  
      * @see #setBrushHeight setBrushHeight
      * @see #setBrushWidth setBrushWidth
      * @see GChart#getYChartSizeDecorated getYChartSizeDecorated
      *
      * @param location the location of the rectangular brush,
      * relative to the x,y position of the mouse.
      *
      */
    public void setBrushLocation(AnnotationLocation location) {
       brushLocation = location;
    }



  /**
     *
     * Convenience method equivalent to:
     * <p>
     *
     * <pre>
     *   setBrushWidth(width);
     *   setBrushHeight(height);
     * </pre>
     *
     * <p>
     * For a full discussion of how GChart uses it's "brush" to
     * determine when hover feedback for a point gets displayed,
     * see <tt>setBrushHeight</tt>.
     * <p>
     *
     * @param width the width of this chart's brush, in pixels
     * @param height the height of this chart's brush, in pixels
     *
     *
     * @see #setBrushHeight setBrushHeight
     * @see #setBrushWidth setBrushWidth
     * @see #DEFAULT_BRUSH_WIDTH DEFAULT_BRUSH_WIDTH
     * @see #DEFAULT_BRUSH_HEIGHT DEFAULT_BRUSH_HEIGHT
     *
     * <p>
     *
     *
     */
    public void setBrushSize(int width, int height) {
       setBrushWidth(width);
       setBrushHeight(height);
    }



  /**
     *
     * Sets the width of the rectangular "brush" that defines how
     * close the mouse position must be to a rendered symbol for
     * that symbol to have been "touched".
     * <p>
     *
     * For a full discussion of how GChart uses it's "brush" to
     * determine when hover feedback for a point gets displayed,
     * see <tt>setBrushHeight</tt>.
     *
     * @param width width of the point selection brush, in pixels.
     *
     * @see #setBrushHeight setBrushHeight
     * @see #setBrushSize setBrushSize
     * @see #DEFAULT_BRUSH_WIDTH DEFAULT_BRUSH_WIDTH
     * @see #DEFAULT_BRUSH_HEIGHT DEFAULT_BRUSH_HEIGHT
     *
     *
     */
    public void setBrushWidth(int width) {
       brushWidth = width;
    }


     /**
      *
      * Allows you to change the x,y scale factors that define
      * the distance between the mouse cursor and each
      * rendered point; these distances determine which point is
      * "closest" to the mouse during hit testing.  <p>
      *
      * Whenever the mouse selection brush "touches" more than
      * one point, the point whose center is closest to the mouse
      * cursor is the one selected. For a point centered at (all
      * coordinates are in pixels) <tt>(xCenter, yCenter)</tt>
      * the distance to the mouse cursor at <tt>(xMouse,
      * yMouse)</tt> is given by: <p>
      *
      * <pre>
      *   dx = xScaleFactor*(xCenter-xMouse);
      *   dy = yScaleFactor*(yCenter-yMouse);
      *   distance = Math.sqrt(dx*dx + dy*dy);
      * </pre>
      * <p>
      *
      * In the above, <tt>(xCenter, yCenter)</tt> is the position
      * at the center of the rectangle associated with the
      * rendered point. For pie slices, it is the position at the
      * center of the pie containing the slice.
      *
      * <p>
      *
      *
      * <i>Tip:</i> Here are the most commonly used x-y scale factors,
      * and how they are typically used:
      * <p>
      * <ol>
      *
      *   <li>To select points based on the ordinary (Euclidean)
      *   distance use <tt>xScaleFactor = 1, yScaleFactor =
      *   1</tt> (this is the default).  <p>
      *  
      *   <li>To select points based only on how close the mouse
      *   x-coordinate is to the x-coordinate at the point's
      *   center (often a good choice for a time series chart) use
      *   <tt>xScaleFactor=1, yScaleFactor=0</tt>
      *   <p>
      *
      *   <li>To select points based only on how close the mouse
      *   y-coordinate is to the y-coordinate at the point's
      *   center (a good choice for a horizontally oriented bar
      *   chart) use <tt>xScaleFactor=0, yScaleFactor=1</tt>
      *   <p>
      *  
      *   <li>To ignore the distance to the point, and instead
      *   always select, from among those points touching the
      *   brush, the point rendered last, use <tt>xScaleFactor=0,
      *   yScaleFactor=0</tt>. This choice guarantees that points
      *   that are completely occluded by other points can never get
      *   selected.  But, because distances are ignored, this choice
      *   can lead to points further from the mouse being
      *   selected in preference to points closer to the mouse.
      *   You can minimize this effect by using a very small
      *   brush size.
      *  
      * </ol>
      * <p>
      *
      * <i>Warning:</i> Mixed metrics, like mixed metaphors, can
      * be confusing:
      * <blockquote>
      *
      * Since each curve gets it's own distance
      * metric, it's possible to preferentially select one curve
      * over another by giving it relatively smaller scale
      * factors.  This can produce very counter-intuitive
      * selection behaviors (selecting a point that is farther
      * away from the mouse than another point, for example).
      * Generally, <i><b>it's best to use the same distance metric
      * for all curves</i></b> except in special cases.
      * <p>
      *
      * For example, suppose you had a vertical bar chart on
      * curve 0 that was restricted to the top half of your
      * chart, and a horizontal bar chart on curve 1 restricted
      * to the bottom half. Then you might use
      * <tt>xScaleFactor=1, yScaleFactor=0</tt> for curve 0 and
      * <tt>xScaleFactor=0, yScaleFactor=1</tt> for curve 1. This
      * "mixed metric" would not create confusion, because the
      * user would view the region-specific selection behaviour
      * as sensibly coordinated with the orientation of the bars
      * in each region.  <p>
      *
      * </blockquote>
      *
      * Though using a relatively larger <tt>xScaleFactor</tt>,
      * <tt>yScaleFactor</tt> makes a curve's points relatively
      * harder to select during hit testing, to completely ignore
      * a curve's points during hit testing, you need to use
      * <tt>setHoverSelectionEnabled(false)</tt> and
      * <tt>setHoverAnnotationEnabled(false)</tt>.
      *
      *
      * @param xScaleFactor multiplies the x-pixel distance
      * between the mouse cursor and the point center (see
      * distance formula above).
      * @param yScaleFactor multiplies the y-pixel distance
      * between the mouse cursor and the point center (see
      * distance formula above).
      *
      * @see #setBrushSize setBrushSize
      * @see #setBrushLocation setBrushLocation
      * @see #setHoverSelectionEnabled setHoverSelectionEnabled
      * @see #setHoverAnnotationEnabled setHoverAnnotationEnabled
      *
      */
     public void setDistanceMetric(double xScaleFactor,
                                    double yScaleFactor) {
        this.xScaleFactor = xScaleFactor;
        this.yScaleFactor = yScaleFactor;
     }

     /**
      ** @deprecated
      **
      ** As of GChart 2.4, hover feedback has been completely
      ** redesigned. Though these changes are mostly positive,
      ** one downside is that, to simplify its hit-testing
      ** algorithms, GChart only provides hover feedback for the
      ** explicitly specified data points on a line chart; it can
      ** no longer provide feedback for the "filled in" points
      ** connecting successive data points.  If you need hover
      ** feedback on such interpolated points you will have to
      ** explicitly add individual data points to the curve
      ** representing the interpolated values.  <p>
      **
      ** Another difference is that you can no longer turn off
      ** hover feedback for a pie slice via this method. If you
      ** need to turn hover feedback off for a pie slice (or for
      ** any other symbol, for that matter) you can use the
      ** (new with 2.4) <tt>setHoverAnnotationEnabled</tt> and
      ** <tt>setHoverSelectionEnabled</tt> methods.
      **
      **
      ** @see #getFillHasHovertext getFillHasHovertext
      ** @see #setHovertextTemplate setHovertextTemplate
      ** @see #setBrushSize setBrushSize
      ** @see #setHoverAnnotationEnabled setHoverAnnotationEnabled
      ** @see #setHoverSelectionEnabled setHoverSelectionEnabled
      **
      **/
       public void setFillHasHovertext(boolean fillHasHovertext) {
          this.fillHasHovertext = fillHasHovertext;
       }
    
     /**
     ** Specifies the spacing between successive rectangular
     ** elements used to render any required non-rectangular
     ** features of the symbol.  <p>
     **
     ** The exact meaning of this spacing
     ** setting depends on the symbol type, and on if an external
     ** canvas factory has been specified via
     ** <tt>setCanvasFactory</tt>:
     **
     ** <p>
     **
     ** <table border>
     **   <tr><th>SymbolType</th><th>How spacing is interpreted</th><th>Default value</th>
     **   </tr>
     **   <tr><td>BOX_*</td>
     **  
     **   <td>The distance between the centers of the "dots" used to
     **    draw the dotted connecting lines between successive
     **    x,y data points on a curve.
     **    <p>
     **   
     **    If <tt>fillSpacing == 0</tt> ("continuously filled"),
     **    <tt>fillThickness > 0</tt>, and a canvas
     **    factory has been specified via <tt>setCanvasFactory</tt>, a
     **    continuous line connecting the centers of successive boxes is
     **    produced in exactly the same way as is done for the
     **    <tt>LINE</tt> symbol type. Without a canvas factory (the
     **    default) <tt>fillSpacing == 0</tt> works the same as
     **    <tt>fillSpacing == 1</tt>.
     **   
     **    </td>
     **   <td>4</td>
     **   </tr>
     **  
     **   <tr><td>LINE</td>
     **
     **  
     **  
     **   <td>The horizontal distance between the centers of the
     **   successive vertical bars, or the vertical distance
     **   between the centers of the successive horizontal bars,
     **   that GChart uses to render the point-to-point
     **   connecting lines of the LINE symbol type.
     **   <p>
     **  
     **   The defaults (no canvas factory specified, 0px spacing)
     **   provide the smoothest lines possible without using canvas, but
     **   also the longest chart update times. Spacing values larger
     **   than 1px will provide proportionally faster rendering of
     **   connecting lines (provided the connecting line segments are
     **   significantly longer than the specified spacing), but they
     **   give the lines a grainy, "stair-step" look.  <p>
     **  
     **   If <tt>fillSpacing == 0</tt> ("continuously filled"),
     **   <tt>fillThickness > 0</tt>, and
     **   a canvas
     **   factory has been specified via <tt>setCanvasFactory</tt>, a
     **   continuous, crisp (sans stair-steps) line connecting the
     **   centers of successive boxes is produced--quickly. Without any
     **   canvas factory specified, <tt>fillSpacing == 0</tt> will work the same
     **   as <tt>fillSpacing == 1</tt>.
     **  
     **    <p>
     **   
     **   <i>Tip:</i> To assure an unbroken connecting line, use a
     **   non-zero fill thickness setting greater than or equal to your
     **   fill spacing setting.</td>
     **  
     **   <td>0</td>
     **   </tr>
     **  
     **   <tr><td>PIE_SLICE_*</td>
     **  
     **   <td>The vertical or horizontal distance between
     **   the centers of the vertical, and/or horizontal,
     **   shading bars used to fill in the pie slice. With
     **   the default setting, this produces a banded-fill
     **   look.
     **  
     **     <p> If <tt>fillSpacing == 0</tt> ("continuously
     **    filled"), <tt>fillThickness > 0</tt>, and a canvas
     **    factory has been specified via
     **    <tt>setCanvasFactory</tt>, the pie slices are crisp,
     **    quickly-rendered, and solid-filled.  Without a canvas
     **    factory specified (the default) <tt>fillSpacing ==
     **    0</tt> works the same as <tt>fillSpacing == 1</tt>.
     **   
     **   </td>
     **   <td>4</td> </tr>
     **
     **   <tr>
      **   <td>VBAR_*</td>
     **
     **   <td>The horizontal distance between corresponding edges
     **      of the vertical bars used to fill in the trapezoidal
     **      areas linearly interpolated between successive
     **      vertical bars on a curve. 
     **
     **     <p> If <tt>fillSpacing == 0</tt> ("continuously
     **    filled"), <tt>fillThickness > 0</tt>, and a canvas
     **    factory has been specified via
     **    <tt>setCanvasFactory</tt>, a filled polygon whose
     **    perimeter connects the x,y points of the curve with a
     **    corresponding interval on the x-axis, x2-axis, or
     **    horizontal baseline is rendered, so as to create a
     **    vertical, solid-filled, area chart.  <p>
     **   
     **    Without a canvas factory, <tt>fillSpacing == 0</tt>
     **    works the same as <tt>fillSpacing == 1</tt>.
     **   
     **    </td>
     **    <td>0</td>
     **   
     **   <tr>
     **   <td>HBAR_*</td>
     **
     **   <td>The vertical distance between corresponding edges
     **      of the horizontal bars used to fill in the
     **      trapezoidal areas linearly interpolated between
     **      successive horizontal bars on a curve.
     **     <p> 
     **
     **    If <tt>fillSpacing == 0</tt> ("continuously filled"), <tt>fillThickness >
     **    0</tt>, and a canvas factory has been specified via
     **    <tt>setCanvasFactory</tt>, a filled polygon whose
     **    perimeter connects the x,y points of the curve with a
     **    corresponding interval on the y-axis, y2-axis, or
     **    vertical baseline will be rendered, so as to create a
     **    horizontal, solid-filled, area chart.
     **   
     **    <p>
     **   
     **    Without a canvas factory, <tt>fillSpacing == 0</tt> works the same as
     **    <tt>fillSpacing == 1</tt>.
     **
     **      </td>
     **   
     **    <td>0</td>
     **   </tr>
     **   <tr>
     **   <td>XGRIDLINE</td>
     **
     **   <td>The horizontal distance between corresponding edges
     **      of the vertical bars used to fill in the
     **      trapezoidal areas linearly interpolated between
     **      successive x-gridlines on a curve.
     **     <p> 
     **
     **    If <tt>fillSpacing == 0</tt> ("continuously filled"), <tt>fillThickness >
     **    0</tt>, and a canvas factory has been specified via
     **    <tt>setCanvasFactory</tt>, a filled polygon is drawn whose
     **    perimeter connects the x,y points of the curve with a
     **    corresponding interval on either the x-axis or x2-axis,
     **    depending on whichever axis is closest to the first
     **    point on the curve.
     **   
     **    <p>
     **
     **    Without a canvas factory, <tt>fillSpacing == 0</tt> works the same as
     **    <tt>fillSpacing == 1</tt>. <p>
     **
     **    <i>Tip:<i> To make a canvas-filled x-gridline curve fill
     **    in a rectangular region, set the y of each point to
     **    <tt>Double.MAX_VALUE</tt>.
     **   
     **      </td>
     **   
     **    <td>4</td>
     **   </tr>
     **   <tr>
     **   <td>YGRIDLINE</td>
     **
     **   <td>The vertical distance between corresponding edges
     **      of the horizontal bars used to fill in the
     **      trapezoidal areas linearly interpolated between
     **      successive y-gridlines on a curve.
     **     <p> 
     **
     **    If <tt>fillSpacing == 0</tt> ("continuously filled"), <tt>fillThickness >
     **    0</tt>, and a canvas factory has been specified via
     **    <tt>setCanvasFactory</tt>, a filled polygon is drawn whose
     **    perimeter connects the x,y points of the curve with a
     **    corresponding interval on either the y-axis or y2-axis,
     **    depending on whichever axis is closest to the first
     **    point on the curve.
     **   
     **    <p>
     **
     **    Without a canvas factory, <tt>fillSpacing == 0</tt> works the same as
     **    <tt>fillSpacing == 1</tt>. <p>
     **
     **    <i>Tip:<i> To make a canvas-filled y-gridline curve fill
     **    in a rectangular region, set the x of each point to
     **    <tt>Double.MAX_VALUE</tt>.
     **   
     **      </td>
     **   
     **    <td>4</td>
     **   </tr>
     ** </table>  
     ** 
     ** <p>
     **
     ** As of version 2.5 GChart provides support for
     ** canvas-based, crisp, quickly drawn, lines, 2-D pie
     ** slices, and area charts if a <tt>fillSpacing</tt> of
     ** <tt>0</tt> is specified ("continuously filled") along
     ** with a <tt>fillThickness > 0</tt>. However, you must
     ** bolt-on an external canvas library (plain vanilla GWT
     ** does not currently come pre-loaded with a browser
     ** independent canvas Widget. However, the fact that the GWT
     ** incubator project contains one implies that the GWT team
     ** is considering adding one). See the
     ** <tt>setCanvasFactory</tt> method for details.
     **
     ** <p>
     **
     ** By default, GChart does not use an external canvas library,
     ** and thus depends only on the standard GWT distribution and
     ** its own 3,000-odd semi-colon terminated lines of GWT Java. If you decide to stick with
     ** this default, the following tips may help you workaround
     ** GChart's rectangle-element-based limitations when
     ** used in this mode.
     ** <p>
     **
     ** First, if your goal is to produce a solid connecting
     ** line between points always use the <tt>LINE</tt> symbol
     ** type rather than the <tt>BOX_CENTER</tt> symbol type with a fill
     ** spacing of 1px. The <tt>LINE</tt> symbol type knows how
     ** to merge adjacent pixels into larger rectangular
     ** elements, and is therefore usually much more efficient,
     ** especially with curves that involve many near-vertical or
     ** near-horizontal connecting lines. For best performance,
     ** use the <tt>BOX_CENTER</tt> symbol only to produce
     ** dotted connecting lines that have widely spaced dots.
     ** <p>
     **
     ** In general, since the number of elements required is
     ** often inversely proportional to fill spacing, using a
     ** very small fill spacing like 1px, while allowed, could
     ** degrade performance unacceptably, especially for very
     ** large-sized charts. On the other hand, too large a fill
     ** spacing/thickness can degrade graphical quality
     ** unacceptably (e.g. due to too few "dots" on dotted
     ** connecting lines, "stair-step" solid connecting lines,
     ** or "grainy filled" pie slices).  <p>
     **
     ** <blockquote><small>
     **
     ** <i>Tip:</i> For pie slices as well as for dotted or solid
     ** connecting lines, scaling down the size of the chart via
     ** <tt>setXPixelSize</tt> and <tt>setYPixelSize</tt> can
     ** also speed up chart display, and thus will often provide
     ** a better-looking alternative to increasing the fill
     ** spacing.  <p>
     **
     **  In particular, for a typical curve whose x-values always
     **  increase with point index (i.e. no "doubling back")
     **  <tt>LINE</tt> symbol type curves often have a number of
     **  elements, and thus an update time, that is approximately
     **  equal to:
     ** 
     **   <p>
     **  
     **   <pre>
     **
     **     "Some Constant" * (xMaxInPixels-xMinInPixels)/fillSpacing
     **
     **   </pre> 
     **   <p>
     **
     **   So, for <tt>LINE</tt> curves, halving the x-axis range
     **   via <tt>setXPixelSize</tt> will provide approximately
     **   the same speedup as doubling the fill spacing setting,
     **   and, because the lines will be less "stair-steppy",
     **   will often provide a more acceptable visual result.
     **  
     **   </small>
     **   </blockquote>
     **
     ** Experience suggests that many applications will be able
     ** to find a combination of chart size and spacing/thickness
     ** settings that provide an acceptable level of both
     ** graphical quality and performance--particularly if your
     ** charting needs are more utilitarian than aesthetic. When
     ** that's not good enough, it's time to use
     ** <tt>setCanvasFactory</tt> to super-charge GChart's
     ** rendering with the power of a cross-browser vector
     ** graphics facility, such as <tt>GWTCanvas</tt>.
     **
     ** <p>
     **
     ** @param fillSpacing spacing between successive rectangular
     **   elements used to fill in non-rectangular symbols, in
     **   pixels. If a canvas factory has been specified,
     **   you can use a setting of <tt>0</tt> to produces
     **   "continuously filled" elements.
     **  
     **
     ** @see #getFillSpacing getFillSpacing
     ** @see #setFillThickness setFillThickness
     ** @see #setCanvasFactory setCanvasFactory
     **
      */
     public void setFillSpacing(double fillSpacing) {
        getParent().invalidate();
       // x!=x is a faster isNaN
        if (!(fillSpacing!=fillSpacing) &&
            fillSpacing != 0 && fillSpacing < 1)
         throw new IllegalArgumentException(
          "fillSpacing="+fillSpacing+"; "+
          "fillSpacing must either be >= 1, or else " +
          "equal to either 0 or Double.NaN.");
       this.fillSpacing = fillSpacing;
     }

     /**
     ** Sets the "thickness" of the rectangular elements used to
     ** render any required non-rectangular features of this symbol.
     ** <p>
     **
     ** The exact meaning of this thickness setting, as well as
     ** the default used whenever the thickness is set to the
     ** special undefined integer value recognized by GChart
     ** (<tt>GChart.NAI</tt>), depends on the symbol type, and
     ** if an external canvas factory has been specified via
     ** <tt>setCanvasFactory</tt>:
     ** <p>
     **
     ** <table border>
     **   <tr><th>SymbolType</th><th>How thickness is interpreted</th><th>Default value</th>
     **   </tr>
     **   <tr><td>BOX_*</td>
     **  
     **   <td>The height and width of rectangular "dots" used to
     **    draw the dotted connecting lines between successive
     **    x,y data points on a curve.
     **    <p>
     **
     **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>
     **    and a canvas factory has been specified via
     **    <tt>setCanvasFactory</tt>,
     **    <tt>fillThickness</tt> is the width of the continuous
     **    connecting lines between successive points.
     **    </td> <p>
     **
     **   <td>0 (implies no interpolated dots or connecting lines)</td>
     **   </tr>
     **  
     **   <tr><td>LINE</td>
     **  
     **   <td>The width of the vertical line segments placed
     **   end-to-end to render any "more-nearly-vertical"
     **   connecting lines of the curve, and the height of the
     **   horizontal line segments placed end-to-end to render
     **   any "more-nearly-horizontal" connecting lines on this
     **   curve.  Note that if you use a fill thickness less than
     **   the fill spacing, your line will not be continuously
     **   connected.
     **   <p>
     **
     **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>
     **    and a canvas factory has been specified via
     **    <tt>setCanvasFactory</tt>,
     **    <tt>fillThickness</tt> is the width of the continuous
     **    connecting lines drawn between successive points.
     **
     **   </td>
     **  
     **  <td>1</td>
     **   </tr>
     **  
     **   <tr><td>PIE_SLICE_*</td>
     **  
     **   <td>The width of vertical, and/or the height of
     **   horizontal, shading bars used to fill in the pie slice
     **   <p>
     **  
     **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>
     **    and a canvas factory has been specified via
     **    <tt>setCanvasFactory</tt>, pie slices are solidly
     **    and continously filled for any <tt>fillThickness &gt; 0</tt>.
     **
     **   </td>
     **   <td>2</td>
     **  
     **   </tr>
     **
     **   <tr>
      **   <td>VBAR_*</td>
     **
     **   <td>The width of vertical bars
     **      used to fill in the trapezoidal areas linearly
     **      interpolated between successive vertical
     **      bars on a curve.
     **    <p>
     **    
     **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>
     **    and a canvas factory has been specified via
     **    <tt>setCanvasFactory</tt>, these trapezoidal areas are
     **    merged together and solidly
     **    and continously filled for any <tt>fillThickness &gt; 0</tt>.
     **
     **      </td>
     **   
     **    <td>0 (implies no "area filling" between bars)
     **   
     **   <tr>
     **   <td>HBAR_*</td>
     **
     **   <td>The height of horizontal bars used to fill in the
     **      trapezoidal areas linearly interpolated between
     **      successive horizontal bars on a curve.
     **    <p>
     **    
     **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>
     **    and a canvas factory has been specified via
     **    <tt>setCanvasFactory</tt>, these trapezoidal areas are
     **    merged together and solidly
     **    and continously filled for any <tt>fillThickness &gt; 0</tt>.
     **   
     **      </td>
     **   
     **    <td>0 (implies no "area filling" between bars)
     **   </tr>
     **   <tr>
     **   <td>XGRIDLINE</td>
     **
     **   <td>The width of vertical bars used to fill in the
     **      trapezoidal areas linearly interpolated between
     **      successive x-gridlilnes on a curve.
     **    <p>
     **    
     **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>,
     **    <tt>fillThickness &gt; 0</tt>, and a canvas factory has been
     **    specified via <tt>setCanvasFactory</tt>, a continuously
     **    filled area-chart, as described in <tt>setFillSpacing</tt>,
     **    will be produced.
     **   
     **      </td>
     **   
     **    <td>0 (implies no "area filling" between gridlines)
     **   </tr>
     **   <tr>
     **   <td>YGRIDLINE</td>
     **
     **   <td>The height of horizontal bars used to fill in the
     **      trapezoidal areas linearly interpolated between
     **      successive y-gridlilnes on a curve.
     **    <p>
     **    
     **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>,
     **    <tt>fillThickness &gt; 0</tt>, and a canvas factory has been
     **    specified via <tt>setCanvasFactory</tt>, a continuously
     **    filled area-chart, as described in <tt>setFillSpacing</tt>,
     **    will be produced.
     **   
     **      </td>
     **   
     **    <td>0 (implies no "area filling" between gridlines)
     **   </tr>
     ** </table>  
     **
     **
     **
     ** <p> This fill thickness setting and the associated fill
     ** spacing setting (c.f. <tt>setFillSpacing</tt>) work
     ** together to define the look and efficiency of pie slice
     ** shading, connecting lines, etc.
     **
     ** @param fillThickness the fill thickness, in pixels
     **
     ** @see #setCanvasFactory setCanvasFactory
     ** @see #getFillThickness getFillThickness
     ** @see #setFillSpacing setFillSpacing
      */
       public void setFillThickness(int fillThickness) {
        getParent().invalidate();
         if (fillThickness!=GChart.NAI && fillThickness < 0)
           throw new IllegalArgumentException(
            "fillThickness="+fillThickness+"; "+
            "fillThickness must either be >= 0, or else " +
            "equal to GChart.NAI.");
          this.fillThickness = fillThickness;
       }

     /**
      * Sets a boolean that determines if point-specific
      * annotations will popup whenever you hover over a point on
      * the curve associated with this symbol.<p>
      *
      * By default, these hover-induced popups are enabled.
      * <p>
      *
      * Note that the point selection feedback on the
      * hovered-over point is controlled separately, via the
      * <tt>setHoverSelectionEnabled</tt> method.
      *
      * @param hoverAnnotationEnabled true if hover-induced annotations popup on this
      * curve, false otherwise.
      *
      * @see #getHoverAnnotationEnabled getHoverAnnotationEnabled
      * @see #setHoverSelectionEnabled setHoverSelectionEnabled
      * @see #setHovertextTemplate setHovertextTemplate
      * @see #setHoverWidget setHoverWidget
      * @see #setHoverLocation setHoverLocation
      * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
      * @see #setHoverXShift setHoverXShift
      * @see #setHoverYShift setHoverYShift
      *
      */
     public void setHoverAnnotationEnabled(boolean hoverAnnotationEnabled) {
       this.hoverAnnotationEnabled = hoverAnnotationEnabled;
     }
     /**
      ** Specifies the weight of the font that will be used
      ** to render the text of this point's hover annotations.
      ** <p>
      **
      ** @param cssWeight A standard CSS font-weight
      **    specification such as normal, bold, bolder, lighter,
      **    100, 200, ... 900, or inherit
      **
      ** @see #getHoverFontWeight getHoverFontWeight 
      ** @see #setHoverFontColor setHoverFontColor
      ** @see #setHoverFontStyle setHoverFontStyle
      ** @see #setHoverFontSize setHoverFontSize
      ** @see #setHoverLocation setHoverLocation
      ** @see #setHoverWidget setHoverWidget
      ** @see #setHoverXShift setHoverXShift
      ** @see #setHoverYShift setHoverYShift
      **
      **
      **
      **/
    public void setHoverFontWeight(String cssWeight) {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       hoverAnnotation.setFontWeight(cssWeight);     
    }
     /**
      ** Specifies the color of the hover annotations' font.
      **
      **
      ** <p>
      ** For more information on standard CSS color
      ** specifications see the discussion in
      ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
      ** <p>
      **       
      ** @param cssColor color of the font used to display this
      **    symbol's hover annotations.
      **
      ** @see #getHoverFontColor getHoverFontColor 
      ** @see #setHoverFontWeight setHoverFontWeight 
      ** @see #setHoverFontStyle setHoverFontStyle
      ** @see #setHoverFontSize setHoverFontSize
      ** @see #setHoverLocation setHoverLocation
      ** @see #setHoverWidget setHoverWidget
      ** @see #setHoverXShift setHoverXShift
      ** @see #setHoverYShift setHoverYShift
      **/
     public void setHoverFontColor(String cssColor) {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       hoverAnnotation.setFontColor(cssColor);     
     }

    
     /**
      ** Specifies the CSS font-style used by this symbol's hover
      ** annotations.
      **
      ** @param cssStyle any valid CSS font-style, namely,
      **   normal, italic, oblique, or inherit.
      **
      ** @see #getHoverFontStyle getHoverFontStyle 
      ** @see #setHoverFontWeight setHoverFontWeight 
      ** @see #setHoverFontColor setHoverFontColor
      ** @see #setHoverFontSize setHoverFontSize
      ** @see #setHoverLocation setHoverLocation
      ** @see #setHoverWidget setHoverWidget
      ** @see #setHoverXShift setHoverXShift
      ** @see #setHoverYShift setHoverYShift
      **/
     public void setHoverFontStyle(String cssStyle) {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       hoverAnnotation.setFontStyle(cssStyle);     
     }
     /**
      ** Specifies the CSS font size used in this symbol's hover
      ** annotations, in pixels.
      **
      ** @param fontSize the CSS font size used in the
      ** hover annotations associated with this symbol, in pixels.
      **
      ** @see #getHoverFontSize getHoverFontSize 
      ** @see #setHoverFontWeight setHoverFontWeight 
      ** @see #setHoverFontColor setHoverFontColor
      ** @see #setHoverFontStyle setHoverFontStyle
      ** @see #setHoverLocation setHoverLocation
      ** @see #setHoverWidget setHoverWidget
      ** @see #setHoverXShift setHoverXShift
      ** @see #setHoverYShift setHoverYShift
      **/
     public void setHoverFontSize(int fontSize) {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       hoverAnnotation.setFontSize(fontSize);     
     }
    /**
     *
     * Specifies the location of this point's hover annotations. Set
     * this property to <tt>null</tt> (the default) to use GChart's
     * default hover location, which varies with the hover annotation's
     * symbol type, as tabulated below. (The hover annotation symbol type defaults to the
     * symbol type of the hovered over curve, and can be specified
     * explicitly via the <tt>setHoverAnnotationSymbolType</tt> method).
     * <p>
     *
     * <table border>
     *   <tr><th>SymbolType used to<br>position hover annotation</th>
     *   <th>Default Hover<br>AnnotationLocation</th></tr>
     *   <tr><td>HBAR_BASELINE_*</td><td>FARTHEST_FROM_VERTICAL_BASELINE</td></tr>
     *   <tr><td>HBAR_*WEST</td><td>EAST</td></tr>
     *   <tr><td>HBAR_*EAST</td><td>WEST</td></tr>
     *   <tr><td>PIE_SLICE_*</td><td>OUTSIDE_PIE_ARC</td></tr>
     *   <tr><td>VBAR_SOUTH*</td><td>NORTH</td></tr>
     *   <tr><td>VBAR_BASELINE_*</td><td>FARTHEST_FROM_HORIZONTAL_BASELINE</td></tr>
     *   <tr><td>VBAR_NORTH*</td><td>SOUTH</td></tr>
     *   <tr><td>All others</td><td>NORTHWEST</td></tr>
     * </table>
     *
     * <p>
     *
     * You can further adjust the position of a point's
     * hover annotations by specifying non-zero positional shifts via
     * the <tt>setHoverXShift</tt> and
     * <tt>setHoverYShift</tt> methods, and via the
     * <tt>setHoverAnnotationSymbolType</tt> method.
     * <p>
     *
     * <i>Tip:</i> To position hover annotations at a fixed location
     * on the chart, (such as a status bar that displays
     * information about the hovered over point, an inset chart
     * that shows a detailed view, etc.)  pass one of the
     * <tt>ANCHOR_*</tt> symbol types to the
     * <tt>setHoverAnnotationSymbolType</tt> method.  <p>
     *
     * @param hoverLocation the relative location of
     * the hover annotations, or <tt>null</tt> to use a
     * symbol-type-specific default.
     *
     * @see #getHoverLocation getHoverLocation
     * @see #setHoverFontWeight setHoverFontWeight 
     * @see #setHoverFontColor setHoverFontColor
     * @see #setHoverFontStyle setHoverFontStyle
     * @see #setHoverFontSize setHoverFontSize
     * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
     * @see #setHoverWidget setHoverWidget
     * @see #setHoverXShift setHoverXShift
     * @see #setHoverYShift setHoverYShift
     * @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
     *
     */
    public void setHoverLocation(AnnotationLocation hoverLocation) {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       hoverAnnotation.setLocation(hoverLocation);     
    }

    /**
     *
     * Sets the symbol type that GChart will use when positioning
     * hover annotations. GChart positions each hover annotation
     * as if it were associated with a point with the
     * same x,y as the hovered over point, and mapped to the same
     * y-axis, but that appears on a curve with the symbol type
     * specified by this method.
     *
     * <p> If <tt>null</tt> is used (this is the default) GChart
     * will use the symbol type associated with the curve
     * containing the hovered over point.  Since normally you
     * will want hover annotations to be positioned as if they were
     * annotations of the hovered over points, this default is
     * usually appropriate.  <p>
     *
     * However, sometimes you would like the hover annotations to be
     * positioned differently. For example, you might prefer the
     * hover annotations to always appear within a status bar at the
     * bottom of the chart. To achieve this, you could set this
     * property to <tt>ANCHOR_SOUTHWEST</tt>.  Or suppose you
     * always wanted a pie chart's hover annotations to appear in
     * the center of the pie instead of along the outer
     * perimeter. Then you could use <tt>BOX_CENTER</tt>. Or, if you
     * wanted the hover annotations to be positioned relative to the
     * position that the mouse was at when the symbol was first
     * "touched", you could use <tt>ANCHOR_MOUSE</tt>.
     * <p>
     *
     * <i>Tip:</i> Pre v2.4 versions of GChart supported a much
     * simpler, "at-the-mouse", <tt>setTitle</tt>-based, hover
     * feedback that you can emulate using code such as: <p>
     *
     * <pre>
     *     Symbol sym = getCurve().getSymbol();
     *     sym.setHoverAnnotationSymbolType(SymbolType.ANCHOR_MOUSE);
     *     sym.setHoverLocation(AnnotationLocation.SOUTHEAST);
     *     sym.setHoverYShift(-20); // push 20px below mouse
     *                              // (kind of like setTitle does it).
     *
     *     // Convenience/transition-helper method
     *     // formatAsHovertext wraps plain text in appropriate
     *     // HTML so it looks kind of like setTitle-based hovertext.
     *    
     *     sym.setHovertextTemplate(
     *       GChart.formatAsHovertext("x=${x}, y=${y}"));
     * </pre> 
     * <p>
     *
     * @param hoverAnnotationSymbolType the symbol type that in part
     * determines how the hover annotations get positioned, or
     * <tt>null</tt> (the default) to use the symbol type of the
     * hovered over point.
     *  
     * @see #getHoverAnnotationSymbolType getHoverAnnotationSymbolType
     * @see #setHoverLocation setHoverLocation
     * @see #setHovertextTemplate setHovertextTemplate
     * @see #setHoverXShift setHoverXShift
     * @see #setHoverYShift setHoverYShift
     * @see GChart#formatAsHovertext GChart.formatAsHovertext
     */
     public void setHoverAnnotationSymbolType(
                   SymbolType hoverAnnotationSymbolType) {
       this.hoverAnnotationSymbolType = hoverAnnotationSymbolType; 
    }
     /**
      * Specifies the background color used to indicate that the mouse is
      * "touching" (hovering over) a point.
      * <p>
      *
      * Whenever the user "touches" a point on this curve with
      * the curve's mouse-centered "brush", GChart displays the hover
      * feedback for that point, and indicates that the point is
      * the one the hover feedback refers to changing its
      * background color to this color.
      * <p>
      *
      * The default hover selection background color is
      * "transparent". This allows the original symbol to appear
      * within selection rectangles that can be defined via the
      * <tt>setHoverSelectionBorderWidth</tt> and
      * <tt>setHoverSelectionBorderColor</tt> methods (1px thick
      * external gray selection rectangles are used by default).
      * <p>
      *
      * <i>Tip:</i> Because the background selection color
      * will often cover the original symbol, it's usually
      * best to choose a selection background color closely
      * related to the original symbol's background color.
      * For example, if the original symbol were blue, you
      * might use a lighter shade of blue.
      *
      * @param hoverSelectionBackgroundColor a CSS color
      * specification string that specifies the background color used to
      * indicate "hover-selection".
      *
      * @see #getHoverSelectionBackgroundColor
      * getHoverSelectionBackgroundColor
      * @see #setHoverSelectionBorderColor
      * setHoverSelectionBorderColor
      * @see #setHoverSelectionBorderStyle
      * setHoverSelectionBorderStyle
      * @see #setHoverSelectionBorderWidth
      * setHoverSelectionBorderWidth
      * @see #setBrushHeight setBrushHeight
      *
      */
     public void setHoverSelectionBackgroundColor(
        String hoverSelectionBackgroundColor) {
       this.hoverSelectionBackgroundColor = hoverSelectionBackgroundColor;
     }
     /**
      * Specifies the border color used to indicate that the mouse is
      * "touching" (hovering over) a point.
      * <p>
      *
      * Whenever the user "touches" a point on this curve with
      * the mouse-centered "brush", GChart displays the hover
      * feedback for that point, and indicates that the point is
      * the one the hover feedback refers to by drawing a border
      * around it with the given color.
      * <p>
      *
      * The width of this border, and if the is drawn outside or
      * just inside the rectangles associated with the symbol,
      * can be specified via
      * <tt>setHoverSelectionBorderWidth</tt>.
      *
      * The default hover selection border color is <tt>gray</tt>.
      *
      * @param hoverSelectionBorderColor a CSS color specification string that specifies
      * the color used to indicate "hover-selection", or the special
      * keyword TRANSPARENT_BORDER_COLOR for a cross-browser consistent
      * transparent border.
      *
      * @see #getHoverSelectionBorderColor
      * getHoverSelectionBorderColor
      * @see #setHoverSelectionBorderStyle
      * setHoverSelectionBorderStyle
      * @see #setHoverSelectionBorderWidth
      * setHoverSelectionBorderWidth
      * @see #setBrushHeight setBrushHeight
      *
      */
     public void setHoverSelectionBorderColor(String hoverSelectionBorderColor) {
       this.hoverSelectionBorderColor = hoverSelectionBorderColor;
     }

     /**
      * Specifies the border style used to indicate that the mouse is
      * "touching" (hovering over) a point.
      * <p>
      *
      * Whenever the user "touches" a point on this curve with
      * the mouse-centered "brush", GChart displays the hover
      * feedback for that point, and indicates that the point is
      * the one the hover feedback refers to by drawing a border
      * around it with the given style.
      * <p>
      *
      * The width of this border, and if the is drawn outside or
      * just inside the rectangles associated with the symbol,
      * can be specified via
      * <tt>setHoverSelectionBorderWidth</tt>.
      *
      * The default hover selection border style is <tt>solid</tt>.
      *
      * @param hoverSelectionBorderStyle a CSS border style
      * specification string that indicates the style of border used to
      * indicate "hover-selection".
      *
      * @see #getHoverSelectionBorderStyle
      * getHoverSelectionBorderStyle
      * @see #setHoverSelectionBorderWidth
      * setHoverSelectionBorderWidth
      * @see #setHoverSelectionBorderColor
      * setHoverSelectionBorderColor
      * @see #setBrushHeight setBrushHeight
      *
      */
     public void setHoverSelectionBorderStyle(String hoverSelectionBorderStyle) {
       this.hoverSelectionBorderStyle = hoverSelectionBorderStyle;
     }

    
     /**
      * Sets the width of the border around the perimeter of
      * rectangles used to indicate that the mouse is
      * "touching" (hovering over) a point.
      * <p>
      *
      * If positive, the border is drawn inside each rendered
      * rectangle of the selected symbol. If negative, the border
      * is drawn outside of those rectangles.
      * <p>
      *
      * <i>Tip:</i> To create the illusion that symbols
      * increase in size when they are "touched", use a hover
      * selection border color that matches the symbol's color
      * along with a negative hover selection border width.  <p>
      *
      * @param borderWidth the width of the border drawn around
      * the perimeter of the selected symbol's rectangles to
      * indicate that the symbol is being "touched: by the mouse.  A
      * negative value adds that border around the outside of the
      * existing rectangles, in effect increasing the selected
      * symbol's size (in pixels).
      *
      * @see #getHoverSelectionBorderWidth
      * getHoverSelectionBorderWidth
      * @see #setHoverSelectionBorderColor setHoverSelectionBorderColor
      * @see #setHoverSelectionBackgroundColor setHoverSelectionBackgroundColor
      * @see #setHoverSelectionBorderStyle
      * setHoverSelectionBorderStyle
      *
      *
      */
      public void setHoverSelectionBorderWidth(int borderWidth) {
         hoverSelectionBorderWidth = borderWidth;
      }



     
     /**
      * Specifies if hover selection feedback will be provided
      * for this curve.
      * <p>
      *
      * When enabled, whenever the user "touches" a point on this
      * curve with the mouse-centered "brush", GChart indicates
      * the hover-selected point by adding a selection border
      * around the point, etc.
      * <p>
      *
      * By default, hover selection feedback is enabled.
      * <p>
      *
      * Note that the pop-up hover annotation itself is
      * controlled separately, via the
      * <tt>setHoverAnnotationEnabled</tt> method.
      * <p>
      *
      * @param hoverSelectionEnabled a if true, hover selection feedback is enabled,
      *   if false, hovering over a point does not change its
      *   color.
      *
      * @see #getHoverSelectionEnabled getHoverSelectionEnabled
      * @see #setHoverAnnotationEnabled setHoverAnnotationEnabled
      * @see #setHoverSelectionBackgroundColor setHoverSelectionBackgroundColor
      * @see #setHoverSelectionBorderColor setHoverSelectionBorderColor
      * @see #setHoverSelectionBorderStyle
      * setHoverSelectionBorderStyle
      * @see #setHoverSelectionBorderWidth setHoverSelectionBorderWidth
      * @see #setHoverSelectionSymbolType setHoverSelectionSymbolType
      *
      */
     public void setHoverSelectionEnabled(boolean hoverSelectionEnabled) {
       this.hoverSelectionEnabled = hoverSelectionEnabled;
     }

     /**
      * Specifies the fill spacing that will be used when
      * rendering this curve's hover selection feedback.
      * <p>
      *
      * For more on fill spacing, see
      * <tt>setFillSpacing</tt>.
      * <p>
      *
      * @param selectionFillSpacing fill spacing, in pixels, used
      * when rendering this curve's hover selection feedback or
      * <tt>Double.NaN</tt> (the default) to adopt the curve's
      * fill spacing.
      *
      * @see #getHoverSelectionFillSpacing getHoverSelectionFillSpacing
      * @see #setFillSpacing setFillSpacing
      *
      */
    
      public void setHoverSelectionFillSpacing(double selectionFillSpacing) {
         hoverSelectionFillSpacing = selectionFillSpacing;
      }
     /**
      * Specifies the fill thickness that will be used when
      * rendering this curve's hover selection feedback.
      * <p>
      *
      * For more on fill thickness, see
      * <tt>setFillThickness</tt>.
      * <p>
      *
      * @param selectionFillThickness fill thickness, in pixels, used
      * when rendering this curve's hover selection feedback or
      * <tt>GChart.NAI</tt> (the default) to adopt the curve's
      * fill thickness.
      *
      * @see #getHoverSelectionFillThickness getHoverSelectionFillThickness
      * @see #setFillThickness setFillThickness
       *
      */
      public void setHoverSelectionFillThickness(int selectionFillThickness) {
         hoverSelectionFillThickness = selectionFillThickness;
      }

    
     /**
      * Sets the height of the symbol used to indicate
      * when a given point is being "hovered over" with the
      * mouse.
      * <p>
      *
      * With the default setting of <tt>GChart.NAI</tt>, GChart
      * simply gives the hover selection symbol the same height as
      * the symbol representing the point itself.  Though this
      * default is usually appropriate, you might want the
      * selection symbol to have a larger size so as to increase
      * the visibility of the selected point, etc.
      * <p>
      *
      * @param selectionHeight the height of the symbol used
      * to indicate that that a point has been selected, in
      * pixels, or <tt>GChart.NAI</tt> (the default) to use
      * the height of the symbol representing the selected
      * point.
      *
      *
      * @see #getHoverSelectionHeight getHoverSelectionHeight
      * @see #setHoverSelectionWidth setHoverSelectionWidth
      * @see #setHoverSelectionBorderColor setHoverSelectionBorderColor
      * @see #setHoverSelectionBackgroundColor setHoverSelectionBackgroundColor
      *
      *
      */
      public void setHoverSelectionHeight(int selectionHeight) {
         hoverSelectionHeight = selectionHeight;
      }

     /**
      * Specifies the URL that will define the image
      * used to render selection feedback for points on
      * the curve associated with this symbol.
      *
      * <p>
      *
      * Specify <tt>null</tt> to use the URL returned by
      * <tt>getBlankImageURL</tt> (this is the default, and gives you a
      * blank 1x1 pixel GIF). Since the image is transparent, the
      * <tt>setHoverSelectionBackgroundColor</tt> method can be used to
      * define the background color of the selection feedback.  <p>
      *
      * Though most applications will do just fine with this default,
      * you can use this method for special selection effects, such
      * creating a semi-transparent "screen" (say, by using an image
      * with alternating transparent and gray pixels) that overlays the
      * selected points.
      * <p>
      *
      * The image is applied in the same way as the symbol's own image
      * URL, but to the internal, system, curve GChart uses to render
      * the selection feedback. See <tt>setImageURL</tt> for additional
      * information.
      *
      * @see #getHoverSelectionImageURL getHoverSelectionImageURL
      * @see #setImageURL setImageURL
      * @see #setBlankImageURL setBlankImageURL

      * @param imageURL the url that defines the image used to generate
      * selection feedback for points rendered with this symbol, or
      * <tt>null</tt> to to use GChart's default selection image URL (a
      * 1x1 transparent blank GIF).
      *
      */
     public void setHoverSelectionImageURL(String imageURL) {
        hoverSelectionImageURL = imageURL;
     }
     
    /**
     *
     * Sets the symbol type that GChart will use when
     * generating selection feedback. GChart indicates that
     * a point is selected by re-rendering the point <i>as
     * if</i> it had this symbol type (this re-rendering
     * overlays, but need not completely cover, the
     * original rendering).
     *
     * <p> If <tt>null</tt> is used (this is the default) GChart
     * will use the symbol type associated with the original
     * point. This default, which overlays the selection feedback
     * on top of the rendered symbol, is usually appropriate.
     * <p>
     *
     * However, sometimes you would like the selection feedback
     * to use a different symbol type. For example, you might
     * prefer to indicate that a point is selected by drawing a
     * vertical gridline through the point. To achieve this, you
     * could use the <tt>XGRIDLINE</tt> symbol type. Or, you
     * might wish to indicate selection by dropping a vertical
     * line from the center of the selected point to the x-axis.
     * In this case, you could use <tt>VBAR_SOUTH</tt> as the
     * hover selection symbol type.
     * <p>
     *
     * <i>Note:</i> The special mouse related symbol types (those with
     * names matching <tt>ANCHOR_MOUSE*</tt>) are intended for use
     * in positioning hover popup annotations (via
     * <tt>setHoverAnnotationSymbolType</tt>). They are not expected to be
     * useful, and could potentially cause confusion, if used as the
     * symbol type passed to this method.
     *
     * <p>
     *
     * @param hoverSelectionSymbolType the symbol type that in
     * part determines how selection feedback for a hovered over
     * point is drawn, or <tt>null</tt> (the default) to use the
     * symbol type of the hovered over point.
     *  
     * @see #getHoverSelectionSymbolType getHoverSelectionSymbolType
     * @see Symbol#setHoverSelectionBackgroundColor
     * setHoverSelectionBackgroundColor
     * @see Symbol#setHoverSelectionBorderColor setHoverSelectionBorderColor
     * @see Symbol#setHoverSelectionBorderWidth setHoverSelectionBorderWidth
     * @see Symbol#setHoverSelectionHeight setHoverSelectionHeight
     * @see Symbol#setHoverSelectionWidth setHoverSelectionWidth
     * @see Symbol#setHoverAnnotationSymbolType setHoverAnnotationSymbolType
     *
     */
     public void setHoverSelectionSymbolType(
                   SymbolType hoverSelectionSymbolType) {
//       throwExceptionOnAnchorMouse(hoverSelectionSymbolType); 
       this.hoverSelectionSymbolType = hoverSelectionSymbolType; 
    }
     /**
      * Sets the width of the symbol used to indicate
      * when a given point is being "hovered over" with the
      * mouse.
      * <p>
      *
      * With the default setting of <tt>GChart.NAI</tt>, GChart
      * simply gives the hover selection symbol the same width as
      * the symbol representing the point itself.  Though this
      * default is usually appropriate, you might want the
      * selection symbol to have a larger size so as to increase
      * the visibility of the selected point, etc.
      * <p>
      *
      * @param selectionWidth the width of the symbol used to
      * indicate that that a point has been selected, in
      * pixels, or
      * <tt>GChart.NAI</tt> (the default) to use the width of the
      * symbol representing the selected point.
      *
      *
      * @see #getHoverSelectionWidth getHoverSelectionWidth
      * @see #setHoverSelectionHeight setHoverSelectionHeight
      * @see #setHoverSelectionBorderColor setHoverSelectionBorderColor
      * @see #setHoverSelectionBackgroundColor setHoverSelectionBackgroundColor
      *
      *
      */
      public void setHoverSelectionWidth(int selectionWidth) {
         hoverSelectionWidth = selectionWidth;
      }


    
  /**
   ** Defines the "hover-text" that appears whenever the user
   ** points their mouse at a point on the curve.
   ** <p>
   **
   ** HTML is supported within hover-text. As with
   ** <tt>setAnnotationText</tt>, you must prefix HTML template
   ** strings with <tt>&lt;html&gt;</tt> or they will be
   ** treated as plain text. <p>
   **
   ** <p> Three built-in parameters, <tt>${x}</tt>, <tt>${y}</tt>, and
   ** <tt>${pieSliceSize}</tt>
   ** are recognized within these hover text templates. Any
   ** occurrences of <tt>${x}</tt> in the string will be replaced with the
   ** x-coordinate of the point, formatted as per the specified
   ** tick label format of the x-axis. Any occurrences of
   ** <tt>${y}</tt> within the string will be replaced with the
   ** y-coordinate of the point, formatted either using the
   ** y-axis or y2-axis tick label format, depending on the axis
   ** on which the curve is displayed. Any occurrences of
   ** <tt>${pieSliceSize}</tt> within the string will be replaced with
   ** 100 times the specified <tt>pieSliceSize</tt> of the point,
   ** formatted the same way as <tt>${y}</tt>, except that a "%" is
   ** tacked onto the end.
   ** <p>
   **
   ** In addition to these built-in parameters, user-defined
   ** parameters are also supported. All parameter names must
   ** begin with a letter (<tt>a,b,...,z</tt> or
   ** <tt>A,B,...,Z</tt>) and be
   ** followed by a series of letters, digits (<tt>0,1,...,9</tt>), and
   ** underscores (<tt>_</tt>). For example, <tt>${myParam3}</tt> or
   ** <tt>${xyz_123}</tt>. Note that parameter names are
   ** case-sensitive.<p>
   **
   ** You define the rules for expanding these user-defined
   ** parameters relative to the hovered-over point, by
   ** instantiating a <tt>HoverParameterInterpreter</tt> and
   ** passing it to GChart's
   ** <tt>setHoverParameterInterpreter</tt> method. See the
   ** <tt>HoverParameterInterpreter</tt> interface's javadocs for
   ** full details.<p>
   **
   ** <blockquote>
   **
   ** <i>Tip:</i> If the <tt>${</tt> is not followed by a valid
   ** parameter name and then by <tt>}</tt>, the "invalid name",
   ** along with the original delimiters, passes through
   ** literally into the final hovertext (no exception is
   ** thrown). So, if you see keywords in your hovertext, it
   ** probably means you misspelled a keyword (e.g.  you entered
   ** <tt>${piesliceSize}</tt> instead of
   ** <tt>${pieSliceSize}</tt>), forgot the closing <tt>}</tt>,
   ** began a user-defined parameter name with a digit, and so on.
   **
   ** </blockquote>
   **
   **
   ** <p>
   **
   ** The default hovertext template, used automatically if
   ** hovertext template is <tt>null</tt>, is
   ** <tt>DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE</tt> for pie slice
   ** type symbols and <tt>DEFAULT_HOVERTEXT_TEMPLATE</tt> for
   ** all other symbol types.
   **
   ** <blockquote>
   ** <i>Tip:</i>If you notice unexplained whitespace to the right
   ** or below your pages, that comes and goes as you hover
   ** over the chart, it could be due to an overly large
   ** default estimate for the hidden "bounding box" GChart uses
   ** to properly center hover annotations. You can use the
   ** {@link #setHoverWidget setHoverWidget} method, with a <tt>null</tt>
   ** first widget parameter, to override these defaults
   ** and correct this problem.
   ** </blockquote>
   ** <p>
   **
   ** @param hovertextTemplate defines the hoverText to display when the mouse
   **   moves over a point on this curve, with <tt>${x}</tt>,
   **   <tt>${y}</tt> and
   **   <tt>${pieSliceSize}</tt> parameters replaced as described above, and
   **   custom parameters replaced as defined by the parent
   **   GChart's <tt>HoverParameterInterpreter</tt>.
   **
   ** @see #getHovertextTemplate getHovertextTemplate
   ** @see Curve.Point#getHovertext getHovertext 
   ** @see HoverParameterInterpreter HoverParameterInterpreter
   ** @see GChart#setHoverParameterInterpreter setHoverParameterInterpreter
   ** @see HoverUpdateable HoverUpdateable
   ** @see GChart.Curve.Point#setAnnotationText setAnnotationText
   ** @see #DEFAULT_HOVERTEXT_TEMPLATE DEFAULT_HOVERTEXT_TEMPLATE
   ** @see #DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
   **   DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
    **/
       public void setHovertextTemplate(String hovertextTemplate) {
          if (this.hovertextTemplate != hovertextTemplate)
             hovertextChunks = null; // invalidates prev chunk-parse
          this.hovertextTemplate = hovertextTemplate;
       }
    /**
     * Specifies a <tt>HoverUpdateable</tt> widget that will be
     * used to display the hover annotations associated with this
     * symbol. If <tt>null</tt>, GChart's built-in,
     * <tt>setHovertextTemplate</tt>-based, text or HTML
     * based hover annotations will instead be used.  <p>
     *
     * Whenever the rectangular "brush" centered on the current
     * mouse position "touches" a point on this symbol's parent
     * curve, GChart will first call the <tt>hoverUpdate</tt>
     * method of this "hover-widget", and then position it
     * appropriately relative to the touched point. Most
     * applications will want to implement <tt>hoverUpdate</tt> so as to
     * populate the hover widget with detailed information about
     * the touched point. For example, to emulate GChart's
     * default hover feedback, you could extend an <tt>HTML</tt>
     * widget and, within the <tt>hoverUpdate</tt> method, use
     * the <tt>setHTML</tt> method to set the widget's HTML to
     * the expanded hover text returned by
     * <tt>hoveredOverPoint.getHovertext()</tt><p>
     *
     * The exact position of the hover widget relative to the
     * touched point is defined by the companion methods,
     * <tt>setHoverLocation</tt>,
     * <tt>setHoverAnnotationSymbolType</tt>,
     * <tt>setHoverXShift</tt>, and <tt>setHoverYShift</tt>.
     *
     *
     * @param hoverWidget a <tt>Widget</tt> that
     * implements the <tt>HoverUpdateable</tt> interface that GChart will
     * use when generating this symbol's widget-based hover annotations, or
     * <tt>null</tt> to use GChart's text or HTML based hover
     * annotations (the other two parameters can still be used
     * to specify upper-bounds on the width and height of this
     * default hover text).
     *
     *  @param widthUpperBound an upper bound on the width of
     *  the widget (or default hover annotation) in pixels.
     *  Use GChart.NAI to get the GChart-determined default.
     * 
     *  @param heightUpperBound an upper bound on the height of the
     *  widget (or default hover annotation) in pixels. Use GChart.NAI
     *  to get the GChart-determined default.
     *
     * @see #getHoverWidget getHoverWidget
     * @see Curve.Point#getHovertext getHovertext
     * @see HoverUpdateable HoverUpdateable
     * @see #setHoverFontWeight setHoverFontWeight 
     * @see #setHoverFontColor setHoverFontColor
     * @see #setHoverFontStyle setHoverFontStyle
     * @see #setHoverFontSize setHoverFontSize
     * @see #setHoverLocation setHoverLocation
     * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
     * @see #setHovertextTemplate setHovertextTemplate
     * @see #setHoverXShift setHoverXShift
     * @see #setHoverYShift setHoverYShift
     *
     */
       public void setHoverWidget(HoverUpdateable hoverWidget,
                                  int widthUpperBound,
                                  int heightUpperBound) {
       if (null != hoverWidget && !(hoverWidget instanceof Widget))
          throw new IllegalArgumentException(
                         "hoverWidget must either be null or a Widget.");

       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       hoverAnnotation.setWidget((Widget) hoverWidget,
                                 widthUpperBound, heightUpperBound);      

    }

     /**
      * Specifies a <tt>HoverUpdateable</tt> widget that will be
      * used to display all hover annotations associated with this
      * symbol. If <tt>null</tt>, GChart's built-in,
      * <tt>setHovertextTemplate</tt>-based, text or HTML
      * based hover annotations will instead be used.  <p>
      *
      * <p>
      * A convenience method equivalent to
      * <tt>setHoverWidget(hoverWidget, GChart.NAI, GChart.NAI)</tt>
      *
      *
      *  @param annotationWidget the GWT Widget that defines this
      *    point's hover-induced annotation, or <tt>null</tt> to use the
      *    default hover annotation, which is based on expanding the
      *    hover text template relative to the hovered over point.
      *
      * @see #setHoverWidget(HoverUpdateable,int,int)
      * setHoverWidget(HoverUpdateable,int,int)
      * @see #setHovertextTemplate setHovertextTemplate
      * @see Curve.Point#getHovertext getHovertext
      * @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
      * @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
      *
      */
     public void setHoverWidget(HoverUpdateable annotationWidget) {
        setHoverWidget(annotationWidget, DEFAULT_WIDGET_WIDTH_UPPERBOUND,
                          DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
     }

    /**
     * Specifies the number of pixels (along the x-axis) to
     * move this symbol's hover annotations from their default,
     * <tt>AnnotationLocation</tt>-defined, point-relative
     * locations. 
     * <p>
     *
     * Actual positional shifts are defined via the same
     * conventions as are used by <tt>setAnnotationXShift</tt>.
     * See that method for further details.
     *
     * @see #getHoverXShift getHoverXShift
     * @see GChart.Curve.Point#setAnnotationXShift setAnnotationXShift
     * @see #setHoverFontWeight setHoverFontWeight 
     * @see #setHoverFontColor setHoverFontColor
     * @see #setHoverFontStyle setHoverFontStyle
     * @see #setHoverFontSize setHoverFontSize
     * @see #setHoverLocation setHoverLocation
     * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
     * @see #setHovertextTemplate setHovertextTemplate
     * @see #setHoverWidget setHoverWidget
     * @see #setHoverYShift setHoverYShift
     *
     */
    public void setHoverXShift(int xShift) {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       hoverAnnotation.setXShift(xShift);
    }

    /**
     * Specifies the number of pixels (along the y-axis) to
     * move this symbol's hover annotations from their default,
     * <tt>AnnotationLocation</tt>-defined, point-relative
     * locations. 
     * <p>
     *
     * Actual positional shifts are defined via the same
     * conventions as are used by <tt>setAnnotationYShift</tt>.
     * See that method for further details.
     *
     * @see #getHoverYShift getHoverYShift
     * @see GChart.Curve.Point#setAnnotationYShift setAnnotationYShift
     * @see #setHoverFontWeight setHoverFontWeight 
     * @see #setHoverFontColor setHoverFontColor
     * @see #setHoverFontStyle setHoverFontStyle
     * @see #setHoverFontSize setHoverFontSize
     * @see #setHoverLocation setHoverLocation
     * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
     * @see #setHovertextTemplate setHovertextTemplate
     * @see #setHoverWidget setHoverWidget
     * @see #setHoverXShift setHoverXShift
     *
     */
    public void setHoverYShift(int yShift) {
       if (hoverAnnotation == null) hoverAnnotation = new Annotation();
       hoverAnnotation.setYShift(yShift);
    }
   

     /**
      * Specifies the URL that will define the image
      * used to represent the points on this curve.
      *
      * <p>
      *
      * Specify <tt>null</tt> to use the URL returned by
      * <tt>getBlankImageURL</tt> (this is the default,
      * and gives you a blank 1x1 pixel GIF).
      * <p>
      *
      * Most applications will do just fine with the default.
      * However, this method lets you replace the default,
      * rectangular, chart symbols with custom images (e.g. a
      * five pointed star) or even a Google Chart API url to use
      * tiny 3-D pie charts for each point symbol (it
      * looks a bit strange, and your chart will no longer
      * be strictly client-side any more, but it does work).
      * <p>
      *
      * Note that if the symbol's width and height are bigger or
      * smaller than the specified image, the image will be
      * stretched to fit the symbol's size. Except for single
      * pixel images and such, this does not usually look that
      * great, so exactly matching up the symbol and image size
      * is often best.
      * <p>
      *
      * <small> <i>Tip:</i> By using a single pixel high or wide image
      * whose other dimension exactly matches the width or height of the
      * symbol, the image will stretch to produce a regular pattern of
      * horizontal or vertical lines. For example, this technique can be
      * used to produce a vertical or horizontal gradient effect in a
      * horizontal or vertical bar chart.  </small>
      *
      * <p>
      *
      * Be aware that GChart was originally designed with only
      * blank image URL's in mind, so it may take some effort to
      * adjust other settings (such as symbol type, width,
      * height, background color, border color, various legend
      * related settings, and curve order) so that the overall
      * chart looks right with your custom images for the curve
      * symbols. In particular, the legend icons are just
      * scaled-down versions of the image, which often doesn't
      * look that great.
      * <p>
      *
      * A alternative that gives you more control (but is less
      * efficient) is to use <tt>SymbolType.NONE</tt> with
      * <tt>setAnnotationWidget</tt> (or
      * <tt>setAnnotationText</tt>) and
      * <tt>setAnnotationLocation(AnnotationLocation.CENTER)</tt>
      * to use separate, centered, widget-based (or HTML based)
      * annotations in lieu of each point's image-based symbol.
      *
      *
      * @see #getImageURL getImageURL
      * @see #setBlankImageURL setBlankImageURL
      * @see GChart#setPlotAreaImageURL setPlotAreaImageURL
      * @see Curve.Point#setAnnotationWidget setAnnotationWidget
      * @see Curve.Point#setAnnotationText setAnnotationText
      * @see Curve.Point#setAnnotationLocation setAnnotationLocation
      * @see Symbol#setSymbolType setSymbolType
      * @see SymbolType#NONE SymbolType.NONE
      *
      * @param imageURL the url that defines the
      * image within all the rectangular elements used to draw
      * this symbol on the chart, or
      * <tt>null</tt> to revert to GChart's default (a 1x1 transparent
      * blank GIF).
      *
      */
     public void setImageURL(String imageURL) {
        this.imageURL = imageURL;
     }
    
  /**
   ** Sets the height of this symbol (including any specified border)
   ** in pixels.
   ** <p>
   **
   ** Symbols for drawing vertical bars and symbols defining
   ** vertical lines between points or across the entire chart,
   ** compute their heights automatically based on curve data,
   ** axes limits, specified baselines, etc. These symbols, namely
   ** <tt>XGRIDLINE</tt> and all those whose names begin with
   ** <tt>VBAR_</tt> will ignore this height setting.
   **
   ** <p>
   ** @param height height of this symbol, in pixels.
   **
   ** @see #getHeight getHeight
   **/
     public void setHeight(int height) {
        getParent().invalidate();
        this.height = height;
        this.modelHeight = Double.NaN;
     }
  /**
   ** Sets the height of this symbol (including any specified border)
   ** in model units (arbitrary, user-defined, units). Model
   ** units are the same units in which the points on the
   ** chart are specified and charted.
   ** <p>
   **
   ** Specification of the modelHeight undefines (that is, sets
   ** to <tt>GChart.NAI</tt>) any previous pixel-based
   ** specification made via <tt>setHeight</tt>.
   **
   ** <p> Symbols for drawing vertical bars and symbols defining
   ** vertical lines between points or across the entire chart,
   ** compute their heights automatically based on curve data,
   ** axes limits, specified baselines, etc. These symbols, namely
   ** <tt>XGRIDLINE</tt> and all those whose names begin with
   ** <tt>VBAR_</tt> will ignore this height setting.
   **
   ** <p>
   ** @param modelHeight height of this symbol, in model units
   **
   ** @see #getModelHeight getModelHeight
   ** @see #setHeight setHeight
   ** @see #setModelWidth setModelWidth
   ** @see #setWidth setWidth
   **/
     public void setModelHeight(double modelHeight) {
        getParent().invalidate();
        this.modelHeight = modelHeight;
     }
  /**
   ** Sets the width of this symbol (including any specified border)
   ** in model units. Model units are an arbitrary, user-defined
   ** units system associated with the x,y coordinates of
   ** points displayed on the chart.
   **
   ** <p> Specification of a symbol's model width undefines (that
   ** is, sets to <tt>GChart.NAI</tt>) any previous, pixel-based,
   ** width specification made via <tt>setWidth</tt><p>
   **
   ** Symbols for drawing horizontal bars, and symbols defining
   ** horizontal lines between points or across the entire chart,
   ** compute their widths automatically based on curve data,
   ** axes limits, specified baseline, etc. These symbols,
   ** namely <tt>YGRIDLINE</tt> and all those whose
   ** names begin with <tt>HBAR_</tt> will ignore this width
   ** setting.
   **
   ** <p>
   ** @param modelWidth width of this symbol, in model units.
   **
   ** @see #setModelHeight setModelHeight
   ** @see #setWidth setWidth
   ** @see #setHeight setHeight
   **
   **/
     public void setModelWidth(double modelWidth) {
        getParent().invalidate();
        this.modelWidth = modelWidth;
     }

    /**
     ** Specifies a value that defines the angular orientation of
     ** the first edge of the pie slice associated with this
     ** symbol.  (An additional clockwise rotation as defined by
     ** <tt>setPieSliceSize</tt> defines the angular orientation
     ** of the second edge of the pie slice).
     **
     ** <p> When specified explicitly, the value must be a
     ** fraction >= 0 and < 1, with 0 representing due south,
     ** 0.25 an additional clockwise angular rotation (starting
     ** at due south) that is 25% of the full, 360 degree
     ** rotation (and thus, if you can follow these gyrations, is
     ** due west), 0.5 representing a 50% clockwise angular
     ** rotation from due south (thus, due north), .75 a 75%
     ** clockwise rotation (and thus, due east), etc.
     **
     ** <p> If the specially recognized value,
     ** <tt>Double.NaN</tt>, is specified, orientation is
     ** chosen so as to make this slice appear adjacent to
     ** the previous slice, (assuming it has the same x,y
     ** as the previous slice and is thus part of the same
     ** pie figure). If this symbol/point represents the
     ** very first pie slice, <tt>Double.NaN</tt>
     ** causes the slice to be oriented as specified via
     ** the <tt>setInitialPieSliceOrientation</tt> method
     ** (by default, that's due south).
     **
     ** Note that though this value can be set regardless of the
     ** symbol's <tt>SymbolType</tt>, it only has an impact on
     ** how the symbol is rendered if the symbol has one of the
     ** pie slice symbol types (e.g.
     ** <tt>PIE_SLICE_VERTICAL_SHADING</tt>).
     **
     ** @param pieSliceOrientation angle at which first edge of pie
     ** slice appears, expressed as a fraction of a full
     ** 360 degree (2*Pi radians) clockwise rotation from an initial due
     ** south position (the 6 o'clock position) required to reach the first
     ** edge of the pie slice.
     **
     **
     **
     ** @see #getPieSliceOrientation getPieSliceOrientation
     ** @see #setPieSliceSize setPieSliceSize
     ** @see GChart#setInitialPieSliceOrientation setInitialPieSliceOrientation
     **
      */
     public void setPieSliceOrientation(
          double pieSliceOrientation) {
       invalidateDependentSlices(getCurveIndex(getParent()));
       if (pieSliceOrientation!=Double.NaN &&
           (pieSliceOrientation < 0 || pieSliceOrientation >=1))
         throw new IllegalArgumentException(
          "pieSliceOrientation="+pieSliceOrientation+"; "+
          "pieSliceOrientation must be >=0 and < 1, or else " +
          "equal to Double.NaN.");
       this.pieSliceOrientation = pieSliceOrientation;
     }

    /**
     ** Specifies a value that defines the angular size of
     ** any pie slice associated with this symbol.
     **
     ** <p> This must be value between 0 and 1.  0.25 represents
     ** a quarter pie slice, 0.5 a half pie, 1 a full pie, etc.
     **
     ** <p><i>Note:</i> To create a complete pie, you must arrange
     ** things so that the sum of all of your pie slice sizes adds up to
     ** exactly 1.0. If they sum to more than 1, some slices will cover
     ** up others; it they sum to less, your pie will have missing
     ** slices. You can assure you get a full pie, regardless of the
     ** original slice sizes by normalizing your slice sizes.
     ** Specifically, divide each original slice size by the sum over
     ** all of the original slice sizes.  For example, if the original
     ** slice sizes were 1, 2, and 2 you could divide them by their sum
     ** (1 + 2 + 2 = 5) to obtain normalized slice sizes of 0.2, 0.4,
     ** and 0.4.  <p>
     **
     ** Note that though this value can be set regardless of the
     ** symbol's current <tt>SymbolType</tt>, it only has an
     ** impact on how the symbol is rendered if the symbol has
     ** one of the pie slice symbol types (e.g.
     ** <tt>PIE_SLICE_VERTICAL_SHADING</tt>).
     **
     ** @param pieSliceSize Fraction of a full pie subtended by
     **  this particular pie slice. Must be between 0 and 1,
     **  inclusive.
     **
     ** @see #getPieSliceSize getPieSliceSize
     ** @see #setPieSliceOrientation setPieSliceOrientation
     **
      */
     public void setPieSliceSize(
          double pieSliceSize) {
       invalidateDependentSlices(getCurveIndex(getParent()));
       if (!withinRange(pieSliceSize,0,1))
         throw new IllegalArgumentException(
          "pieSliceSize="+pieSliceSize+"; the requirement: "+
          "0.0 <= pieSliceSize <= 1.0 must be satisfied.");
       this.pieSliceSize = pieSliceSize;
     }

  /**
   ** Sets the type of this symbol.
   ** <p>
   **
   ** <i>Note:</i> The special mouse related symbol types (those with
   ** names that begin with <tt>ANCHOR_MOUSE</tt>) are intended for use
   ** in positioning hover popup annotations (via
   ** <tt>setHoverAnnotationSymbolType</tt>). They are not expected to be
   ** useful, and could potentially cause confusion, if used as the
   ** symbol types of ordinary curves.
   **
   ** @param symbolType the new symbol type for this symbol.
   ** @see SymbolType SymbolType
   ** @see SymbolType#ANCHOR_MOUSE ANCHOR_MOUSE
   ** @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
   **
   */
     public void setSymbolType(SymbolType symbolType) {
//      throwExceptionOnAnchorMouse(symbolType); 
      getParent().invalidate();
      // will invalidate dependent slices if it was previously a pie slice 
      invalidateDependentSlices(getCurveIndex(getParent()))
      this.symbolType = symbolType;
      // will invalidate dependent slices if it is now a pie slice
      invalidateDependentSlices(getCurveIndex(getParent()))
   }

  /**
   ** Sets the width of this symbol (including any specified border)
   ** in pixels.
   ** <p>
   **
   ** Symbols for drawing horizontal bars, and symbols defining
   ** horizontal lines between points or across the entire chart,
   ** compute their widths automatically based on curve data,
   ** axes limits, specified baseline, etc. These symbols, namely
   ** <tt>YGRIDLINE</tt> and all those whose names begin with
   ** <tt>HBAR_</tt> will ignore this width setting.
   **
   ** <p>
   ** @param width width of this symbol, in pixels
   **
   ** @see #setHeight setHeight
   **
   **/
     public void setWidth(int width) {
       getParent().invalidate();
       this.width = width;
       this.modelWidth = Double.NaN;
     }

    
     /*
      * Copies properties of the "from" symbol to this symbol.
      *
      * This isn't a generic copy, but is used only when copying
      * the properties of the hovered-over curve into the
      * system curves used to render the selection feedback and
      * hover annotations (it contains some special logic needed
      * only in that context).
      *
      */
     void copy(Symbol from) {
       setBackgroundColor(from.getBackgroundColor());
       setBaseline(from.getBaseline());
       setBorderColor(from.getBorderColor());
       setBorderStyle(from.getBorderStyle());
       setBorderWidth(from.getBorderWidth());
       setFillSpacing(from.getFillSpacing());
       setFillThickness(from.getFillThickness());
//       setHoverAnnotationEnabled(from.getHoverAnnotationEnabled());
       setHoverFontColor(from.getHoverFontColor());
       setHoverFontSize(from.getHoverFontSize());
       setHoverFontStyle(from.getHoverFontStyle());
       setHoverFontWeight(from.getHoverFontWeight());
       setHoverLocation(from.getHoverLocation());
       setHoverAnnotationSymbolType(from.getHoverAnnotationSymbolType());
       setHoverSelectionBackgroundColor(from.getHoverSelectionBackgroundColor());
       setHoverSelectionBorderColor(from.getHoverSelectionBorderColor());
       setHoverSelectionBorderStyle(from.getHoverSelectionBorderStyle());
       setHoverSelectionBorderWidth(from.getHoverSelectionBorderWidth());
//       setHoverSelectionEnabled(from.getHoverSelectionEnabled());
       setHovertextTemplate(from.getHovertextTemplate());
       setHoverWidget(from.getHoverWidget());
       setHoverXShift(from.getHoverXShift());
       setHoverYShift(from.getHoverYShift());
       setImageURL(from.getImageURL());
       // Model and pixel variants of width/height actually
       // represent a single underlying property (setting one,
       // unsets the other, etc.). Logic below reflects this.
       if (!Double.isNaN(from.getModelHeight()))
          setModelHeight(from.getModelHeight());
       else
          setHeight(from.getHeight());
       if (!Double.isNaN(from.getModelWidth()))
          setModelWidth(from.getModelWidth());
       else
          setWidth(from.getWidth());

       setPieSliceOrientation(from.getPieSliceOrientation());
       setDefaultPieSliceOrientation(from.getDefaultPieSliceOrientation());
       setPieSliceSize(from.getPieSliceSize());
       setSymbolType(from.getSymbolType());

     }
     Annotation getAnnotation() {
       if (annotation == null) annotation = new Annotation();
       return annotation;
     }

     // Pixel height of symbol when rendered on given plot panel
     double getHeight(PlotPanel pp, boolean onY2) {
        double result;
        double mH = getModelHeight();
        if ((mH!=mH)) // x!=x is a faster isNaN
            result = getHeight();
        else
           result = pp.dyToPixel(mH,onY2);

        return result;
     }
     // Pixel width of symbol when rendered on given plot panel
     double getWidth(PlotPanel pp) {
        double result;
        double mW = getModelWidth();
        if ((mW!=mW)) // x!=x is a faster isNaN
            result = getWidth();
        else
           result = pp.dxToPixel(mW);

        return result;
     }

    
     /* Renders the symbol at the specified position within the
        plot panel, by creating appropriately positioned Image
        and Grid (for any Annotation associated with this symbol
        rendering) objects within the panel.  So-rendered symbols
        are used to represent: each point on a curve with any
        associated point annotations, axes, gridlines, ticks and
        their tick-labels. */
    
     void realizeSymbol(PlotPanel pp,
                        GraphicsRenderingPanel grp,
                        AnnotationRenderingPanel arp,
                        Annotation annotation,
                        boolean onY2,
                        boolean clipPlotArea,
                        boolean clipDecoratedChart,
                        boolean drawMainSymbol,
                        double x, double y,
                        double prevX, double prevY,
                        double nextX, double nextY) {
       getSymbolType().realizeSymbol(pp, grp, arp, this, annotation,
                                     onY2,
                                     clipPlotArea,
                                     clipDecoratedChart,
                                     drawMainSymbol,
                                     x, y,
                                     prevX, prevY, nextX, nextY);
    
     }




    
   } // end of class Symbol
  
  private static double lastPieSliceOrientation;
  static double getLastPieSliceOrientation() {
    return lastPieSliceOrientation;
  }
  static void setLastPieSliceOrientation(double lastOrientation) {
     lastPieSliceOrientation = lastOrientation%1.0;
  }
  private double initialPieSliceOrientation;

  /** Sets the default initial orientation for pie slices.
   **
   ** The default initial orientation is used as the first pie
   ** slice's first edge's orientation setting only if the symbol associated
   ** with that pie slice has the default, undefined, orientation
   ** setting of <tt>Double.NaN</tt>.
   ** <p>
   ** The default value of this setting is 0, which corresponds
   ** to due south (6 o'clock). The value specifies the
   ** fraction of a complete clockwise rotation, beginning
   ** at due south required to reach the first edge of the
   ** pie slice.
   **
   ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
   **
   ** @param orientation the orientation to use for the first
   **   edge of the first pie slice in this GChart, in cases
   **   in which that first pie slice's orientation is undefined
   **   (<tt>Double.NaN</tt>).
   **/
 
  public void setInitialPieSliceOrientation(double orientation) {
     if (orientation < 0 || orientation >=1)
         throw new IllegalArgumentException(
          "orientation="+orientation+"; "+
          "orientation must be >=0 and < 1.");
     this.initialPieSliceOrientation = orientation;
     invalidateAllSlices();
  }

  /**
   ** Returns a previously specified initial pie slice orientation.
   **
   ** @return the fraction of a clockwise rotation, beginning
   **   from the 6 o'clock postion, needed to reach the default
   **   initial pie slice orientation.
   **
   ** @see #setInitialPieSliceOrientation
   **   setInitialPieSliceOrientation
   **/
  public double getInitialPieSliceOrientation() {
    return initialPieSliceOrientation;
  }

   /**
    ** Specifies the type of symbol used by a curve. GChart
    ** includes a <tt>LINE</tt> symbol type (suitable for solidly
    ** connected line charts), various "box" symbol types
    ** (suitable for scatter and dotted-line charts),
    ** horizontal and vertical bars that extend to axis limits
    ** or a specified baseline (suitable for bar and area charts),
    ** and pie slices (suitable for pie charts) in these
    ** symbol types. Thus, choosing a curve's symbol type has a
    ** bigger impact on the kind of chart you create than in
    ** other charting APIs you may have used.
    **
    ** <p> One advantage of this symbol type based approach: you
    ** can place multiple pies, lines and/or bars on a single
    ** chart simply by creating multiple curves whose associated
    ** symbols have appropriately different symbol types.
    **
    ** <p> Note that, for line, area, or pie charts, the exact
    ** look of the non-rectangular aspects (connecting lines,
    ** filled-in areas, etc.) of these symbols in the chart is
    ** largely governed by the host <tt>Symbol</tt>'s
    ** <tt>fillSpacing</tt> and <tt>fillThickness</tt>
    ** properties.
    **
    ** <p> For instance, with the default <tt>fillThickness</tt>
    ** of 0 for the <tt>BOX_CENTER</tt> symbol, curves display
    ** only explicitly specified data points, without any
    ** connecting lines between them. But, if you set
    ** <tt>fillThickness</tt> to 1, GChart interpolates a series
    ** of 1 pixel by 1 pixel rectangular "dots" between successive
    ** data points, with an intra-dot spacing defined by the
    ** symbol's <tt>fillSpacing</tt> setting.  <p>
    **
    ** Since v2.5, a new <tt>fillSpacing==0</tt> setting, with the
    ** special meaning of "continuous filling", is allowed. If an
    ** external canvas library has been plugged into GChart via
    ** <tt>setCanvasFactory</tt>,
    ** higher quality, continuously filled pie, line, and area charts
    ** can be produced via the combination: <tt>fillSpacing==0</tt> and
    ** <tt>fillThickness > 0</tt> along with one of the pie, line, or
    ** bar symbol types described below.<p>
    **
    ** You must select each curve's symbol type from the predefined
    ** set of supported types listed in the "Field Summary"
    ** section below. The default symbol type is <tt>BOX_CENTER</tt>.
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setSymbolType setSymbolType
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    ** @see Symbol Symbol
    **
    **/
   public static class SymbolType {

      /*
       * For efficiency during hit testing, points get separated
       * into bins associated with adjacent vertical
       * (or horizontal) bands that cover the plot area.
       * <p>
       *
       * Subclasses (such as those for producing horizontal bar
       * charts) whose rendered symbols do not have a fixed width
       * across all the points on a single curve, MUST set this
       * field to true within their constructors, because
       * the hit testing approach assumes fixed "thickness"
       * symbols for simplicity/efficiency.
       * <p>
       *
       * Subclasses that have both a fixed width and height MAY
       * set this to true if they are typically used in a way
       * that tends to make horizontal banding a better (= tends
       * to place same # of points in each band) binning strategy.
       *
       * <p>
       *
       * If <tt>null</tt>, GChart uses a simple heuristic that assumes
       * that a brush that is wider than high implies developer is
       * trying to let user distinguish finer y differences, and thus
       * our bands should separate points more finely (and hence allow
       * for faster band-indexed hit testing) if we use horizontal
       * banding in this case (and vertical otherwise).
       * <p>
       *
       * See the <tt>bandSeparatePoints</tt> method for more info.
       *
       */
      Boolean isHorizontallyBanded = null;
      // Use smallest min band size, since I expect per band
      // cost to be small compared to per-point hit testing.
      protected final int MIN_BAND_SIZE = 1
//      Boolean isHorizontallyBanded() {return isHorizontallyBanded; }

     /* Thickness (in pixels) of hit-test-bands used with this
      * symbol type.
      * <p>
      *
      * Gets overriden for pie slice symbol types, which base
      * thickness on pie diameter.
      */
      protected double getBandThickness(PlotPanel pp,
                                        Symbol sym, boolean onY2) {
        double result;
        if (sym.isHorizontallyBanded())
           result = Math.max(MIN_BAND_SIZE,
                            sym.getHeight(pp, onY2));
        else
           result = Math.max(MIN_BAND_SIZE, sym.getWidth(pp));
        return result;
      }

      // is overridden by pie slices, which use a different brush shape
      protected int getBrushHeight(Symbol sym) {
         int result = sym.getBrushHeight();
         return result;
      }
      // again, so pie slices can override
      protected AnnotationLocation getBrushLocation(Symbol sym) {
         AnnotationLocation result = sym.getBrushLocation();
         return result;
      }
      // is overridden by pie slices, which use a different brush shape
      protected int getBrushWidth(Symbol sym) {
         int result = sym.getBrushWidth();
         return result;
      }
     /*
      * This symbol type provides a convenient anchor point at
      * one of the standard 9 named positions within the plot
      * area.  The actual x,y of the points using this symbol
      * type is ignored.  Useful for placing annotations around
      * and along the perimeter of the plot panel.<p>
      *
      * For example, chart decorations such as axis labels and
      * footnotes internally use symbols of this type (with
      * appropriate setAnnotationXShift or setAnnotationYShift
      * adjustments to position the decoration appropriately
      * relative to the anchor point). End-users can use a curve
      * with this symbol type, along with a single point and
      * appropriate widget-based annotation, to place a table in
      * the upper left corner of the plot area, etc.
      *
      */

     private static class AnnotationAnchor extends SymbolType {
        AnnotationLocation location;
        AnnotationAnchor(AnnotationLocation location) {
          super(0, 0, 0, 0, 0, 0);
          this.location = location;
        }
        // actual curve symbol zero-sized so it does not
        // appear--it's just for positioning the annotation.
        public double getAdjustedWidth(double width,
                                        double x,
                                        double xPrev, double xNext,
                                        double xMin, double xMax,
                                        double xMid) {
            return 0;
        }
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                         double yMin, double yMax,double yMid) {
           return 0;
         }
        
         // Just return one of the standard 9 positions, or the mouse
         // coordinates, based on the location defined in the
         // constructor.
        double getUpperLeftX(double width, double x,
                             double xPrev, double xNext,
                             double xMin, double xMax, double xMid,
                             int xMouse) {
           double result;
           if (AnnotationLocation.AT_THE_MOUSE == location)
              result = (GChart.NAI == xMouse) ? Double.NaN : xMouse;
           else if (AnnotationLocation.AT_THE_MOUSE_SNAP_TO_X == location)
              result = (GChart.NAI == xMouse) ? Double.NaN : x;
           else if (AnnotationLocation.AT_THE_MOUSE_SNAP_TO_Y == location)
              result = (GChart.NAI == xMouse) ? Double.NaN : xMouse;
           else if (AnnotationLocation.NORTHWEST == location ||
               AnnotationLocation.WEST == location ||
               AnnotationLocation.SOUTHWEST == location)
              result = xMin;
           else if (AnnotationLocation.NORTHEAST == location ||
                    AnnotationLocation.EAST == location ||
                    AnnotationLocation.SOUTHEAST == location)
              result = xMax;
           else // NORTH, CENTER, or SOUTH
              result = (xMin + xMax)/2;
          
           return result;
          
      }       
         double getUpperLeftY(double height, double y,
                              double yPrev, double yNext,
                              double yMin, double yMax, double yMid,
                              int yMouse)  {
           double result;
           if (AnnotationLocation.AT_THE_MOUSE == location)
              result = (GChart.NAI == yMouse) ? Double.NaN : yMouse;
           else if (AnnotationLocation.AT_THE_MOUSE_SNAP_TO_X == location)
              result = (GChart.NAI == yMouse) ? Double.NaN : yMouse;
           else if (AnnotationLocation.AT_THE_MOUSE_SNAP_TO_Y == location)
              result = (GChart.NAI == yMouse) ? Double.NaN : y;
           else if (AnnotationLocation.NORTHWEST == location ||
               AnnotationLocation.NORTH == location ||
               AnnotationLocation.NORTHEAST == location)
              result = yMin;
           else if (AnnotationLocation.SOUTHWEST == location ||
                    AnnotationLocation.SOUTH == location ||
                    AnnotationLocation.SOUTHEAST == location)
              result = yMax;
           else // WEST, CENTER, or EAST
              result = (yMin + yMax)/2;
           return result;
         }
     }


     private static class HBarBaseline extends SymbolType {
         HBarBaseline(int wm, int hm) {
            super(wm, hm, 0.5, 0.5, 0, 0,  Boolean.TRUE);
         }
         protected double defaultFillSpacing() {
           return DEFAULT_BAR_FILL_SPACING;
         }
         protected AnnotationLocation defaultHoverLocation() {
            return DEFAULT_HBAR_BASELINE_HOVER_LOCATION;
         }
         public double getAdjustedWidth(double width,
                                        double x,
                                        double xPrev, double xNext,
                                        double xMin, double xMax,
                                        double xMid) {
            return x - xMid;
         }

      double getUpperLeftX(double width, double x,
                           double xPrev, double xNext,
                           double xMin, double xMax, double xMid,
                           int xMouse) {
         return xMid;
      }  
        
        
         int getIconHeight(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
         int getIconWidth(int legendFontSize) {
            return legendFontSize;
         }

     } // end of class HBarBaseline
     private static class HBarLeft extends SymbolType {
         HBarLeft(int wm, int hm) {
             super(wm, hm, 0.5, 0.5, 0.5, 0.5,  Boolean.TRUE);
         }
         protected double defaultFillSpacing() {
           return DEFAULT_BAR_FILL_SPACING;
         }
         protected AnnotationLocation defaultHoverLocation() {
            return DEFAULT_HBARLEFT_HOVER_LOCATION;
         }
         public double getAdjustedWidth(double width,
                                     double x,
                                     double xPrev, double xNext,
                                     double xMin, double xMax, double xMid) {
            return x - xMin;
         }
         int getIconHeight(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
         int getIconWidth(int legendFontSize) {
            return legendFontSize;
         }

     } // end of class HBarLeft
    
      private static class HBarRight extends SymbolType {
         HBarRight(int wm, int hm) {
             super(wm, hm, 0.5, 0.5, 0.5, 0.5, Boolean.TRUE);
         }
        
         protected double defaultFillSpacing() {
           return DEFAULT_BAR_FILL_SPACING;
         }
         protected AnnotationLocation defaultHoverLocation() {
            return DEFAULT_HBARRIGHT_HOVER_LOCATION;
         }
         public double getAdjustedWidth(double width, double x,
                                        double xPrev, double xNext,
                                        double xMin, double xMax,
                                        double xMid) {
            return xMax - x;
         }
         int getIconHeight(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
         int getIconWidth(int legendFontSize) {
            return legendFontSize;
         }     
      } // end of class HBarRight


    // draws a connected straight line between successive points
    private static class LineSymbolType extends SymbolType {
         LineSymbolType() {
// same constructor as BOX_CENTER, which centers line segments on
// the points that they represent, as required.
             super(0, 0, 0, 0, 0, 0);
         }

     // fillSpacing to use when a symbol's fillSpacing is
     // set to GChart.NAI (an undefined integer)
      protected double defaultFillSpacing() {
        return DEFAULT_LINE_FILL_SPACING;
      }
     // fillThickness to use when a symbol's fillThickness is
     // set to GChart.NAI (an undefined integer)
      protected int defaultFillThickness() {
        return DEFAULT_LINE_FILL_THICKNESS;
      }
      int getIconHeight(int legendFontSize) {
            return 3; // leaves room for a 1px border and a center
      }
      int getIconWidth(int legendFontSize) {
          return Math.max(3, legendFontSize);
      }     
     /*
      * Draws an approximate line from x,y to nextX, nextY, using an
      * appropriate series of vertical (for a > 45 degree slope) or (for
      * a < 45 degree slope) horizontal line segments. If a GWT canvas
      * is available and if continuous fill (fillSpacing==0) was
      * requested the lineTo,stroke,etc. of the canvas Widget are
      * instead used to draw the line.
      * <p>
      *
      * The canvas part of this code assumes/requires that points
      * on a curve are rendered sequentially, and that on the
      * first point on the curve <tt>prevX</tt> and
      * <tt>prevY</tt>, and on the last point <tt>nextX</tt> and
      * <tt>nextY</tt>, are undefined (Double.NaN)
      *
      */
     
      void realizeSymbol(PlotPanel pp,
                         GraphicsRenderingPanel grp,
                         AnnotationRenderingPanel arp,
                         Symbol symbol,
                         Annotation annotation,
                         boolean onY2,
                         boolean clipPlotArea,
                         boolean clipDecoratedChart,
                         boolean drawMainSymbol,
                         double x, double y,
                         double prevX, double prevY,
                         double nextX, double nextY) {

       if ((x!=x) || (y!=y)) // this point undefined (isNaN)
          return;
       // else point itself is at least defined
      
       double spacing = symbol.getFillSpacing();
       int thickness = symbol.getFillThickness();
       GChartCanvasLite canvas = grp.getCanvas();
      
       if (0 == spacing && null != canvas && thickness > 0) {
          // when canvas is available and continuous fill requested,
          // BOX_CENTER and LINE work exactly the same way
          BOX_CENTER.realizeSymbol(
             pp, grp, arp, symbol, annotation,
             onY2,  clipPlotArea, clipDecoratedChart, drawMainSymbol,
             x,  y, prevX,  prevY, nextX,  nextY);
          return;
       }

       double xPx = pp.xToPixel(x);
       double yPx = pp.yToPixel(y, onY2);
       double nextXPx = pp.xToPixel(nextX);
       double nextYPx = pp.yToPixel(nextY, onY2);
      
       if (nextX==nextX && nextY==nextY &&  // next point defined
                thickness > 0 &&   // not a zero thickness connection
                (x!=nextX || y!=nextY) ) { // this/next point not overlayed
         // draw HTML-element rendered line segment
         
         // Continuous fill not supported; 1px is reasonable approx.
         if (0 == spacing) spacing = 1;
         double deltaX = nextXPx - xPx;
         double deltaY = nextYPx - yPx;
         boolean dXIsShorter = deltaX*deltaX < deltaY*deltaY;
         // increasing width by 1 adds half px on each edge
         // to heal the occasional roundoff-induced gap
         final double EPS = 1;
// TODO: the case in which the connecting line does not intersect the
// plot area, and off-plot-area points are not being drawn is handled
// very inefficiently, and not entirely correctly, by trying to draw
// the entire line and excluding each segment as we attempt to draw it.
// Need to compute intersecting sub-line-segment and just draw that
// instead, ignoring lines with no intersecting segments completely. Can
// make a huge difference with lots of off-chart points, such as a
// deliberately narrowed x axis range.
         if (deltaX == 0) { // special-case of vertical line

             realizeOneImageOfSymbol(pp, grp, arp, symbol, null,
                                     onY2,
                                     clipPlotArea,
                                     clipDecoratedChart,
                                     xPx,
                                     0.5*(yPx+nextYPx),
                                     Double.NaN, Double.NaN,
                                     nextXPx, nextYPx,
                                     thickness,
                                     Math.abs(nextYPx - yPx)+EPS);
         }
         else if (deltaY == 0) { // special case of horizontal line

             realizeOneImageOfSymbol(pp, grp, arp, symbol, null,
                                      onY2,
                                     clipPlotArea,
                                     clipDecoratedChart,
                                     0.5*(xPx+nextXPx),
                                     yPx,
                                     Double.NaN, Double.NaN,
                                     nextXPx, nextYPx,
                                     Math.abs(nextXPx - xPx)+EPS,
                                     thickness);
         }
         else if (dXIsShorter) { // series of vertical segments
           double xMin = (xPx < nextXPx) ? xPx : nextXPx;
           double xMax = (xPx > nextXPx) ? xPx : nextXPx;
           double yAtXMin = (xPx < nextXPx) ? yPx : nextYPx;
           double yAtXMax =(xPx > nextXPx) ? yPx : nextYPx;

           double xiPrev = xMin;
           double yiPrev = yAtXMin;
           double xi = xiPrev;
           double yi = yiPrev;
           // round up to err on side of providing more detail
           int N = (int) Math.ceil((xMax-xMin)/spacing);
           double dy = Math.abs((yAtXMax - yAtXMin)/N)+EPS;
           for (int i = 1; i <= N; i++) {
             xi = xMin + i*(xMax - xMin)/N;
             yi = yAtXMin + i * (yAtXMax - yAtXMin)/N;
             realizeOneImageOfSymbol(pp, grp, arp, symbol, null,
                    onY2,
                    clipPlotArea,
                    clipDecoratedChart,
                    0.5*(xiPrev+xi), 0.5*(yiPrev+yi),
                    Double.NaN, Double.NaN,
                    nextXPx, nextYPx,
                    thickness, dy);
             xiPrev = xi;
             yiPrev = yi;
            }
         }
         else { // dY is shorter. Series of horizontal segments
           double yMin = (yPx < nextYPx) ? yPx : nextYPx;
           double yMax = (yPx > nextYPx) ? yPx : nextYPx;
           double xAtYMin = (yPx < nextYPx) ? xPx : nextXPx;
           double xAtYMax = (yPx > nextYPx) ? xPx : nextXPx;

           double xiPrev = xAtYMin;
           double yiPrev = yMin;
           double xi = xiPrev;
           double yi = yiPrev;
           int N = (int) Math.ceil((yMax-yMin)/spacing);
           double dx = Math.abs((xAtYMax - xAtYMin)/N)+ EPS;
           for (int i = 1; i <= N; i++) {
             yi = yMin + i*(yMax - yMin)/N;
             xi = xAtYMin + i * (xAtYMax - xAtYMin)/N;
             realizeOneImageOfSymbol(pp, grp, arp, symbol, null,
                    onY2,
                    clipPlotArea,
                    clipDecoratedChart,
                    0.5*(xiPrev+xi),0.5*(yiPrev+yi),
                    Double.NaN, Double.NaN,
                    nextXPx, nextYPx,
                    dx, thickness);
             xiPrev = xi;
             yiPrev = yi;
            }
         }
       }
      
       // the "main" symbol (the one on the (x,y) point itself) is
       // rendered last to put it on top of interpolated images
       if (drawMainSymbol) {
          double w = symbol.getWidth(pp);
          double h = symbol.getHeight(pp, onY2);
          realizeOneImageOfSymbol(pp, grp, arp, symbol, annotation,
                                  onY2,
                                  clipPlotArea,
                                  clipDecoratedChart,
                                  xPx, yPx,
                                  Double.NaN, Double.NaN,
                                  nextXPx, nextYPx,
                                  w, h);
       }
     } // realizeSymbol

   }
      /* Symbols that are assigned this symbol type can be used
       * to represent a pie chart slice.
       *
       * The pivot point (center of containing pie) is at the x,y
       * location of the point. Typically, only a single point
       * per pie-slice curve is used (multiple points simply
       * translate the same pie slice symbol to another position,
       * such behavior is useful if you want to use
       * a pie slice as a traditional curve symbol, but
       * it isn't needed for a typical pie chart).
       * <p>
       *
       * The initial angle and angle subtended by the slice are
       * specified by the <tt>pieSliceOrientation</tt> and
       * <tt>pieSliceSize</tt> properties of the host
       * <tt>Symbol</tt> (these properties only have meaning with
       * pie slice symbol types). Typically, several curves share
       * a common pie center point (x,y) and have orientations
       * and sizes that are coordinated so that the slices fit
       * together to form a single complete pie. GChart
       * facilitates this by choosing (by default) the next
       * slice's orientation so that it is adjacent to the
       * preceeding slice. However, other useful idioms include,
       * for example, adjusting the x,y pivots to produce
       * "exploded pie charts", or using a single slice that
       * fills up the entire pie as a disc-like alternative to
       * <tt>BOX_CENTER</tt>.  <p>
       *
       * The radius of the slice is chosen as the radius such
       * that the rectangle defined by the hosting Symbol's width
       * and height just barely fits within a circle with that
       * radius. This convention allows users to define the
       * pie radius in terms of the x model coordinates, y model
       * coordinates, or in pixels, as desired.
       * <p>
       *
       * The host <tt>Symbol</tt>'s fillSpacing and fillThickness
       * properties, along with horizontallyShaded and
       * verticallyShaded properties of this SymbolType, govern
       * how the slice is filled in.
       *
       * For more information with example code, see the
       * discussion under the {@link #PIE_SLICE_OPTIMAL_SHADING
       * PIE_SLICE_OPTIMAL_SHADING} symbol type.
       *
       */
     
        private static class PieSliceSymbolType extends SymbolType {
          private boolean horizontallyShaded;
          private boolean verticallyShaded;
          private boolean optimallyShaded;

          PieSliceSymbolType(boolean horizontallyShaded,
                             boolean verticallyShaded,
                             boolean optimallyShaded,
                             double pixelPadLeft,
                             double pixelPadRight,
                             double pixelPadTop,
                             double pixelPadBottom) {
            // same as BOX_SOUTHEAST (allows shading bars to be
            // easily positions by their upper left corners):
             super(1,1, pixelPadLeft, pixelPadRight,
                   pixelPadTop, pixelPadBottom);

            this.horizontallyShaded = horizontallyShaded;
            this.verticallyShaded = verticallyShaded;
            this.optimallyShaded = optimallyShaded;
          }

           protected AnnotationLocation defaultHoverLocation() {
              return DEFAULT_PIE_SLICE_HOVER_LOCATION;
           }
          /*
           * @Override
           *
           * For simplicity, pie slices are given the upper bound
           * band thickness of a slice that occupies the entire pie.
           * <p>
           *
           * The case where hit test banding is most needed: lots
           * of very small full pies on a single curve (pie used as
           * circular alternative to a rectangular point marker)
           * won't suffer from this up-sizing approximation,
           * since it uses full pies anyway.
           *
           */
          protected double getBandThickness(PlotPanel pp, Symbol sym,
                                            boolean onY2) {
             double result = Math.max(MIN_BAND_SIZE,
                                      2*sym.getPieSliceRadius(pp, onY2));
            return result;
          }
        /*
         * @override
         *
         * Pie slices use a special radially oriented brush, whose radial
         * dimension is the larger of the specified brush width and
         * height.
         * <p>
         *
         * So, from the point of view of the banded/binned hit testing
         * algorithm, which works entirely with rectangles, it is as if
         * the brush were a square with side equal to the larger of the
         * brush width and height. Thus, regardless of if the pie uses
         * horizontal or vertical hit test banding, the as-if-rectangular
         * brush used in binned/banded hit testing is same square box,
         * given by this method and its companion, <tt>getBrushWidth</tt>,
         * below.
         * <p>
         *
         * This code also relies on the fact that for pie slices, only the
         * larger of width, height has an impact on the more exact,
         * slice/angle/radius closeness testing that is applied only to
         * the subset of nearby points determined by using the bins/bands.
         * So, making brush width and height the same for pie slices
         * doesn't cause any detail hit testing errors (as it would for ordinary
         * rectangular hit testing).
         * <p>
         *
         * TODO: Above works (I think) but is convoluted. Try to find a
         * clearer, simpler, way to express/handle pie slice differences.
         * The special case brush location handling for pies also seems
         * a bit obscure.
         *
         */
        protected int getBrushHeight(Symbol sym) {
           int result = Math.max(sym.getBrushHeight(),
                                    sym.getBrushWidth());
           return result;
        }
        // @override (pie slices always use a centered location)
        protected AnnotationLocation getBrushLocation(Symbol sym) {
           AnnotationLocation result = AnnotationLocation.CENTER;
           return result;
        }
        // @override (see comment on getBrushHeight above)
        protected int getBrushWidth(Symbol sym) {
           int result = Math.max(sym.getBrushHeight(),
                                    sym.getBrushWidth());
           return result;
        }


          protected double defaultFillSpacing() {
            return DEFAULT_PIE_SLICE_FILL_SPACING;
          }
          protected int defaultFillThickness() {
            return DEFAULT_PIE_SLICE_FILL_THICKNESS;
          }
          protected String defaultHovertextTemplate() {
            return DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE;
          }

          // min/max x (cosine) and y (sine) over "unit circle slice"
          private static class SliceLimits {
             double xMin;
             double xMax;
             double yMin;
             double yMax;
          }

          // Gets min/max sin, cos over slice cut from unit circle
          private SliceLimits getSliceLimits(double tMin,
                                             double tMax) {
            SliceLimits result = new SliceLimits();
            double xMin = 0; // origin of 0,0 present in every slice
            double xMax = 0; // (it's the pie center/slice pivot point)
            double yMin = 0;
            double yMax = 0;
            double tmp = 0;
            // points where each edge intersects the arc could be
            // extremal points--include them too.
            tmp = Math.cos(tMin);
            xMin = (xMin < tmp) ? xMin : tmp;   
            xMax = (xMax > tmp) ? xMax : tmp;
            tmp = Math.sin(tMin);
            yMin = (yMin < tmp) ? yMin : tmp;   
            yMax = (yMax > tmp) ? yMax : tmp;   

            tmp = Math.cos(tMax);
            xMin = (xMin < tmp) ? xMin : tmp;   
            xMax = (xMax > tmp) ? xMax : tmp;
            tmp = Math.sin(tMax);
            yMin = (yMin < tmp) ? yMin : tmp;   
            yMax = (yMax > tmp) ? yMax : tmp;

            // finally if slice includes any special extreme points
            // on the arc (namely, points of the arc that are
            // either due north, due south, due east or due west)
            // include those points in determining the min/max x
            // and min/max y included in the slice:
            double halfPi = Math.PI/2.;
            for (int i = (int) Math.ceil(tMin/halfPi);
                 i*halfPi < tMax; i++) {
              double t = i*halfPi;
              tmp =  Math.cos(t);
              xMin = (xMin < tmp) ? xMin : tmp;   
              xMax = (xMax > tmp) ? xMax : tmp;
              tmp = Math.sin(t);
              yMin = (yMin < tmp) ? yMin : tmp;   
              yMax = (yMax > tmp) ? yMax : tmp;
            }

            result.xMin = xMin;
            result.xMax = xMax;
            result.yMin = yMin;
            result.yMax = yMax;

            return result;

          }
         
          protected double getEdgeLeft(PlotPanel pp, Symbol symbol,
                                       double x, boolean onY2) {

             double r = symbol.getPieSliceRadius(pp, onY2);
             double theta0 = symbol.getPieSliceTheta0();
             double theta1 = symbol.getPieSliceTheta1();
             SliceLimits sl = getSliceLimits(theta1, theta0);
             double xPx = pp.xToPixel(x);
             // scale up the xMin on unit circle to get to left edge
             double result =  xPx + sl.xMin * r;
             return result;
          }
          protected double getEdgeRight(PlotPanel pp,
                                        Symbol symbol,
                                        double x,
                                        boolean onY2) {
             double r = symbol.getPieSliceRadius(pp, onY2);
             double theta0 = symbol.getPieSliceTheta0();
             double theta1 = symbol.getPieSliceTheta1();
             SliceLimits sl = getSliceLimits(theta1, theta0);
             double xPx = pp.xToPixel(x);
             // scale up the xMax on unit circle to get to right edge
             double result = xPx + sl.xMax * r;
             return result;
          }

         
          protected double getEdgeTop(PlotPanel pp, Symbol symbol,
                                      double y, boolean onY2) {

             double r = symbol.getPieSliceRadius(pp, onY2);
             double theta0 = symbol.getPieSliceTheta0();
             double theta1 = symbol.getPieSliceTheta1();
             SliceLimits sl = getSliceLimits(theta1, theta0);
             double yPx = pp.yToPixel(y, onY2);
             // minus for the Cartesian to pixel-coord transform
             double result = yPx - sl.yMax * r;            
             return result;
          }
          protected double getEdgeBottom(PlotPanel pp, Symbol symbol,
                                             double y, boolean onY2) {

             double r = symbol.getPieSliceRadius(pp, onY2);
             double theta0 = symbol.getPieSliceTheta0();
             double theta1 = symbol.getPieSliceTheta1();
             SliceLimits sl = getSliceLimits(theta1, theta0);
             double yPx = pp.yToPixel(y, onY2);
             // minus for the Cartesian to pixel-coord transform
             double result = yPx - sl.yMin * r;            
             return result;
          }



          // returns the y coordinate where a pie slice edge
          // intersects a given vertical line, or NaN if none.
          private static double yWherePieEdgeIntersectsVerticalLine(
            double xOfVerticalLine,
            double xPieCenter, double yPieCenter,
            double pieRadius, double pieEdgeAngle) {
            double result = Double.NaN;
            double dxToArc = pieRadius*Math.cos(pieEdgeAngle)
            if (dxToArc != 0) {
  // The fraction of the way (from pie center to pie perimeter
  // along the pie slice edge) that you must go to reach the point
  // at which the vertical line intersects with the pie slice
  // edge. For example, this fraction is 0.5 whenever the vertical
  // line bisects the pie slice edge. 
              double t = (xOfVerticalLine-xPieCenter)/dxToArc;
              if (GChart.withinRange(t,0,1)) {
                result = yPieCenter -
                         t * pieRadius * Math.sin(pieEdgeAngle);
              }
            }
            return result;
          }

          // returns the x coordinate where a pie slice edge
          // intersects a given horizontal line, or NaN if none.
          private static double xWherePieEdgeIntersectsHorizontalLine(
            double yOfHorizontalLine,
            double xPieCenter, double yPieCenter,
            double pieRadius, double pieEdgeAngle) {
            double result = Double.NaN;
            double dyToArc = pieRadius*Math.sin(pieEdgeAngle);
            if (dyToArc != 0) {
  // The fraction of the way (from pie center to pie perimeter
  // along the pie slice edge) that you must go to reach the point
  // at which the horizontal line intersects with the pie slice
  // edge. For example, this fraction is 0.5 whenever the horizontal
  // line bisects the pie slice edge. 
              double t = (yPieCenter - yOfHorizontalLine)/dyToArc;
              if (GChart.withinRange(t,0,1)) {
                result = xPieCenter +
                         t * pieRadius * Math.cos(pieEdgeAngle);
              }
            }
            return result;
          }
          /*
           * Returns the angle of a line extending from (0,0) to
           * (x,y) in radians in the standard range, 0 to 2*Pi. For
           * example, a line pointing due east such as (1,0) would
           * return 0, one pointing due north such as (0,0.5) would
           * return Pi/2, one pointing due west such as (-4.13,0)
           * would return Pi and the point (1,1) returns Pi/4. 
           * <p>
           *
           * x,y are in the ordinary cartesian coordinate system
           * (not in the typical graphics/pixel coordinates)
           *
           */
          private static double angle(double x, double y) {
            double result = Double.NaN;
            if (x == 0) {
              if (y > 0)
                result = Math.PI/2.;
              else if (y < 0)
                result = 3*Math.PI/2.;
            }
            else if (x> 0 && y >= 0)
              result = Math.atan(y/x);
            else if (x<0 && y >= 0)
              result = Math.PI - Math.atan(-y/x);
            else if (x <0 && y < 0)
              result = Math.PI + Math.atan(y/x);
            else if (x > 0 && y < 0)
              result = 2*Math.PI- Math.atan(-y/x);

            return result;
          }

          // is the given angle between the two angles given?
          private static boolean angleInRange(double angle,
                                                double theta0,
                                                double theta1) {

             if (theta0 > theta1)
                return angleInRange(angle, theta1, theta0);
            // angle is in standard 0 to 2*Pi range, but thetas
            // can be "wrapped around" several negative
            // multiples of 2*Pi less than the standard range;
            // this loop brings angle into same range as thetas
            while (angle > theta1)
               angle -= 2*Math.PI;

            boolean result = GChart.withinRange(angle, theta0, theta1);
            return result;
          }

        /*
         *
         * @Override
         *
         * The x, y coordinates at the "center" of the slice for
         * hit testing purposes.
         *
         * During hit testing, if more than one symbol touches
         * the brush, the point whose center is closest to
         * the mouse position is selected.
         *
         * To simplify the calculation, that center point is taken
         * to be the center of the pie containing the slice, rather
         * than the center of the slice per se. Though not an ideal
         * choice, it is unlikely to cause significant deviations
         * from user expectations, given how pie slices tend to be
         * used to compose full pies out of a series of
         * non-overlapping slices.
         *
         */
        protected double getCenterX(PlotPanel pp, Symbol symbol,
                                    int iPoint) {
           Curve.Point p = symbol.getParent().getPoint(iPoint);
           double result = pp.xToPixel(p.getX());
           return result;
        }
        /* @Override
         *
         * See comment on getCenterX above.
         *
         */
        protected double getCenterY(PlotPanel pp, Symbol symbol,
                                    int iPoint,
                                    boolean onY2) {
           Curve.Point p = symbol.getParent().getPoint(iPoint);
           double result = pp.yToPixel(p.getY(), onY2);
           return result;
        }
        /*
         * @Override
         *
         * Pie slices redefine what constitutes intersection of the
         * mouse-centered brush and the rendered symbol to be:
         * "mouse position within a radially-expanded
         * version of the slice". The pie radius is expanded by
         * half the larger dimension of the point selection brush.
         *
         */ 
        protected boolean isIntersecting(PlotPanel pp,
                                         Symbol symbol,
                                         int iPoint,
                                         boolean onY2,
                                         int xBrush,
                                         int yBrush,
                                         int brushWidth,
                                         int brushHeight) {

           boolean result = false;
           Curve.Point p = symbol.getParent().getPoint(iPoint);
           double x = p.getX()// pie center point (slice pivot)
           double y = p.getY();
           double xPx = pp.xToPixel(x);
           double yPx = pp.yToPixel(y, onY2);
           double dx = xBrush-xPx;
           // - represents switch from graphics to cartesian coordinates
           double dy = -(yBrush-yPx);

           double rSquared = dx*dx + dy*dy;
           double angle = angle(dx, dy);
           // pie angles grow clockwise but radians counter-clockwise,
           // hence the odd "0 into max, 1 into min" mapping below. 
           double thetaMax = symbol.getPieSliceTheta0();
           double thetaMin = symbol.getPieSliceTheta1();
           double rPiePlus = symbol.getPieSliceRadius(pp, onY2) +
                             0.5*Math.max(brushWidth, brushHeight);

           /*
            * Enforce a minimum slice angle for hit testing
            * purposes, equivalent to  +/- 1 px of play along the
            * arcs of tiny slices, to make them easier to select:<p>
            *
            * <pre>
            *   r*minDTheta = 1 px
            * </pre>
            *
            * This helps with tiny slices adjacent to large ones, but if
            * several tiny slices are adjacent to each other, or if both
            * adjacent slices come after the tiny slice in the curve
            * order (e.g. the tiny slice is the very first slice) it
            * still won't be selectable. Developers can switch
            * curve order to get around this, but it's not ideal.
            * <p>
            *
            * TODO: Integrate a "closest to slice angle" criterion
            * to resolve ties when hit testing slices to provide
            * a better hit testing behavior with small or overlapping
            * slices.
            *  
            */
           double minDTheta = (rPiePlus < 1)? 1.0 : 1./rPiePlus;
           if (thetaMax - thetaMin < 2*minDTheta) {
             double thetaMid = 0.5*(thetaMax + thetaMin);
             thetaMin = thetaMid - minDTheta;
             thetaMax = thetaMid + minDTheta;
           }
           if (rSquared <= rPiePlus*rPiePlus &&
               angleInRange(angle,thetaMin,thetaMax))
              result = true;

           return result;
        }


        void realizeSymbol(PlotPanel pp,
                           GraphicsRenderingPanel grp,
                           AnnotationRenderingPanel arp,  
                           Symbol symbol,
                           Annotation annotation,
                           boolean onY2,
                           boolean clipPlotArea,
                           boolean clipDecoratedChart,
                           boolean drawMainSymbol,
                           double x, double y,
                           double prevX, double prevY,
                           double nextX, double nextY) {

           if (!drawMainSymbol) return;
           double xPx = pp.xToPixel(x);
           double yPx = pp.yToPixel(y, onY2);
           double spacing = symbol.getFillSpacing();
           int thickness = symbol.getFillThickness();  
           double r = symbol.getPieSliceRadius(pp, onY2);
           double theta0 = symbol.getPieSliceTheta0();
           double theta1 = symbol.getPieSliceTheta1();
           GChartCanvasLite canvas = grp.getCanvas();         
           // x!=x is a faster isNaN 
           if ((xPx!=xPx) || (yPx!=yPx))
              return; // undefined slice pivot point
           else if (clipPlotArea &&
               !intersects(xPx-r, yPx-r, xPx+r, yPx+r,
                            0, 0, pp.getXChartSize(), pp.getYChartSize()))
              return; // rect containing pie is off plot area
           else if (clipDecoratedChart) {
               int yAxisWidth = pp.getYAxisEnsembleWidth();
               int titleThickness = pp.chartTitleThickness();
               if (!SymbolType.intersects(0.0 - yAxisWidth,
                           0.0 - titleThickness,
                           pp.getXChartSizeDecoratedQuickly()-yAxisWidth,
                           pp.getYChartSizeDecoratedQuickly()-titleThickness,
                           xPx-r, yPx-r, xPx+r, yPx+r))
             return; // rect containing pie is off decorated chart
           }
           // else bounding rectangle of pie containing the slice visible
          
           if (0 == spacing && null != canvas && thickness > 0) {
              // continuous fill pie slice and canvas is available
             
              /*
               * Solid fill pie slices implement the notion of internal vs
               * external borders a bit differently than rectangular
               * symbols.
               * <p>
               *
               * Internal borders are always drawn "centered",
               * that is, half internal, half external.  In part,
               * this is because that is how "stroke" of the
               * canvas API does it, so it's easier to implement.
               * But mainly it is because, when you assemble
               * several slices into a full pie, a centered
               * border, provided that each slice has the same
               * border color, is really the only choice that
               * looks right (this is a constraint of the
               * geometry how the slices fit together into a
               * pie).  <p>
               *
               * External borders (negative border width) are
               * drawn outside the slice proper by doubling the
               * thickness, and then over-filling the internal
               * part of the border by issuing the fill after,
               * instead of before, the border is drawn. Though
               * external borders don't look right within a pie
               * (because of how the slices occlude each other's
               * borders) they can be handy for making slice
               * selection borders that are drawn entirely
               * outside of the selected slice.
               *
               */
              int borderWidth = symbol.getBorderWidth();
              int adjustedBorderWidth = (borderWidth >= 0) ?
                  borderWidth : 2*Math.abs(borderWidth);
             
               /*
                * With incubator's <tt>GWTCanvas</tt>, IE7 & Chrome draw
                * 0 and 2*Pi slices incorrectly.  See issues #278
                * #282 for more information: <p>
                *
                * http://code.google.com/p/google-web-toolkit-incubator/issues/detail?id=278
                * http://code.google.com/p/google-web-toolkit-incubator/issues/detail?id=282
                * <p>
                *
                * Pies with > 1000 px radii are unlikely (tried
                * using 10000, but it didn't work in Chrome).
                *
                */
              final double MIN_DTHETA = 1./1000;
              final double MAX_DTHETA = 2*Math.PI - MIN_DTHETA;

              // canvas measures angles clockwise from +x-axis;
              // our angles are counter-clockwise from +x-axis
              double dTheta = theta0 - theta1;
              double angleStart = 2*Math.PI-theta0;
              double angleEnd = angleStart +
                  Math.max(MIN_DTHETA, Math.min(dTheta, MAX_DTHETA));

              if (dTheta >= MIN_DTHETA || borderWidth < 0) {
                 canvas.beginPath();
                 canvas.setLineWidth(adjustedBorderWidth);

                 canvas.arc(xPx - grp.x0, yPx - grp.y0, r,
                            angleStart, angleEnd, false);
                 if (dTheta <= MAX_DTHETA)
                    canvas.lineTo(xPx - grp.x0, yPx- grp.y0);
                 // else avoid "line to center" in full pies

                 canvas.closePath();

                 String borderColor = symbol.getBorderColor();
                 String backgroundColor = symbol.getBackgroundColor();
                 /*
                  * XXX: The approach to transparent border/fill
                  * used below is to simply not stroke the border
                  * or to not fill the inside of the path. This
                  * isn't exactly right, because the region where
                  * the border overlaps the filled area does not
                  * always become transparent when it should.
                  * These errors likely won't be noticed in most
                  * usage scenarios, and without the ability to
                  * replace filled/stroked regions with
                  * transparent pixels (I don't think GWTCanvas
                  * can do this?) there isn't an easy fix.
                  *
                  */


                 // non-negative borders fill before stroking (thus
                 // stroke overwrites internal half of border)
                 if (borderWidth >= 0 && thickness > 0 &&
                     TRANSPARENT_BORDER_COLOR != backgroundColor &&
                     // GWTCanvas thows an exception w "transparent"
                     "transparent" != backgroundColor) {
                     canvas.setFillStyle(backgroundColor);
                     canvas.fill();
                 }

                 // stroke whenever a border is present
                 if (borderWidth != 0 &&
                     TRANSPARENT_BORDER_COLOR != borderColor &&
                     "transparent" != borderColor) {
                    canvas.setStrokeStyle(borderColor);
                    canvas.stroke();
                 }

                 // negative borders fill AFTER stroking (thus zapping
                 // the internal half of the stroked border).
                 if (borderWidth < 0 && thickness > 0 &&
                     TRANSPARENT_BORDER_COLOR != backgroundColor &&
                     "transparent" != backgroundColor) {
                     canvas.setFillStyle(backgroundColor);
                     canvas.fill();
                  }
              }
              // else 0-sized slice, 0 or internal border, is just dropped
           }
           else {
              if (0 == spacing) spacing = 1;
              // if center point is on the chart, draw it:

              double prevXPx = pp.xToPixel(prevX);
              double prevYPx = pp.yToPixel(prevY, onY2);
              double nextXPx = pp.xToPixel(nextX);
              double nextYPx = pp.yToPixel(nextY, onY2);
              int nBands = (int) Math.round(r/spacing);
              /* Holds positions at which the current vertical or
               * horizontal "gridline-like band" intersects the outter
               * perimeter of the current pie slice. These positions
               * are used to define the location and size of shading
               * bars required for each pie slice.
               *
               * Note: Although most pie slice perimeters are convex
               * and thus have perimeters that intersect a gridline
               * in at most two points, pie slices that take up more
               * than half of the entire pie have perimeters that
               * can (across their pacman-like mouth) intersect a
               * gridline at up to four points.
               *
               */
              final int MAX_PIE_SLICE_PERIMETER_INTERSECTIONS = 4;
              double[] p = new double[MAX_PIE_SLICE_PERIMETER_INTERSECTIONS];
              final double EPS = 0.5;
              SliceLimits sl = getSliceLimits(theta1, theta0);
              boolean optimalIsVertical =
                                         (sl.yMax - sl.yMin) > (sl.xMax - sl.xMin);
              boolean isFullPie = (symbol.getPieSliceSize() == 1.0)
              // perform any vertical shading that may be required:
              if (nBands > 0 && (verticallyShaded ||
                                 (optimallyShaded && optimalIsVertical))) {
                 for (int i = (int) Math.round(nBands*sl.xMin);
                      i < sl.xMax*nBands; i++) {
                    int nP = 0;
                    double dxPx = r*(i+0.5)/nBands;
                    double dyPx = Math.sqrt(r*r - dxPx*dxPx);
                    // x of vertical line bisecting the shading band
                    double xi = xPx + dxPx;
                    // y-positions where this band crosses circle perimeter
                    double c1 = yPx - dyPx;
                    double c2 = yPx + dyPx;
                    // y-positions where this band crosses each slice edge
                    // (full pies don't have pie slice edges)
                    double e1 = isFullPie ?
                                Double.NaN :
                                yWherePieEdgeIntersectsVerticalLine(
                       xi,xPx,yPx,r,theta0);
                    double e2 = isFullPie?
                                Double.NaN :
                                yWherePieEdgeIntersectsVerticalLine(
                       xi,xPx,yPx,r,theta1);
                    // Exclude circle perimeter intercepts outside of
                    // the slice.  Note: Pixel y coordinates used in
                    // browser increase going down, but cartesian y
                    // coordinates used in trig functions increase
                    // going up, hence the sign-flipping on second arg
                    // of angle function below.
                    if (angleInRange(angle(xi-xPx,yPx-c1),theta0,theta1))
                       p[nP++] = c1;
                    // intersection points sorted by increasing y within p[]
                    if (e1 < e2) {
                       // x!=x is a faster isNaN  
                       if (!(e1!=e1)) p[nP++] = e1;
                       if (!(e2!=e2)) p[nP++] = e2;
                    }
                    else {
                       if (!(e2!=e2)) p[nP++] = e2;
                       if (!(e1!=e1)) p[nP++] = e1;
                    }

                    if (angleInRange(angle(xi-xPx, yPx-c2),theta0,theta1))
                       p[nP++] = c2;
                    for (int j = 1; j < nP; j++) {
                       // logic below avoids drawing a line across the
                       // non-convex "pacman mouth" that occurs with any
                       // bigger-than-half-pie-sized slices, by
                       // requiring that a line drawn from the pie
                       // center to an interpolated point on each
                       // shading bar forms an angle in the slice's
                       // angular range. We use a point 30% rather than
                       // 50% of the way inbetween to avoid ever hitting the
                       // center of the pie (where angle is ambiguous).
                       //
                       // Note that, due to roundoff error, you cannot
                       // ALWAYS rely on the (mathematically correct)
                       // fact that problematic bars always connect p[1]
                       // and p[2].
                       if (Math.abs(theta0-theta1) <= Math.PI ||
                           angleInRange(angle(xi-xPx,
                                              yPx-(0.3*p[j]+0.7*p[j-1])),
                                        theta0,theta1)) {
                          // widening of EPS pixels on either side fills in
                          // tiny intra-slice gaps (that can otherwise appear
                          // due to roundoff) by making each bar a tad bigger.
                          realizeOneImageOfSymbol(pp, grp, arp,
                             symbol, null,
                             onY2, 
                             clipPlotArea,
                             clipDecoratedChart,
                             xi-0.5*
                             thickness,
                             p[j-1]-EPS,
                             prevXPx, prevYPx,
                             nextXPx, nextYPx,
                             thickness,
                             p[j] - p[j-1] +2*EPS);
                       }
                    }
                 }
              }

              // Now do any required horizontal shading. This is
              // basically the same as the code for vertical shading
              // above (w appropriate transposition/adjustments).
              if (nBands > 0 && (horizontallyShaded ||
                                 (optimallyShaded && !optimalIsVertical))) {
                 for (int i = (int) Math.round(-nBands*sl.yMax);
                      i < -nBands * sl.yMin; i++) {
                    int nP = 0;
                    double dyPx = r*(i+0.5)/nBands;
                    double dxPx = Math.sqrt(r*r - dyPx*dyPx);
                    // y of the horizontal line bisecting the shading band
                    double yi = yPx + dyPx;

                    // x-positions where this band crosses circle perimeter
                    double c1 = xPx - dxPx;
                    double c2 = xPx + dxPx;

                    // x-positions where this band crosses each slice edge
                    // (full pies don't have pie slice edges)
                    double e1 = isFullPie ?
                                Double.NaN :
                                xWherePieEdgeIntersectsHorizontalLine(
                       yi,xPx,yPx,r,theta0);
                    double e2 = isFullPie ?
                                Double.NaN :
                                xWherePieEdgeIntersectsHorizontalLine(
                       yi,xPx,yPx,r,theta1);
                    // exclude circle perimeter intercepts outside of
                    // the slice
                    if (angleInRange(angle(c1-xPx, yPx-yi),theta0,theta1))
                       p[nP++] = c1;

                    // intersection points sorted by increasing x within p[]
                    if (e1 < e2) {
                       // x!=x is a faster isNaN
                       if (!(e1!=e1)) p[nP++] = e1;
                       if (!(e2!=e2)) p[nP++] = e2;
                    }
                    else {
                       if (!(e2!=e2)) p[nP++] = e2;
                       if (!(e1!=e1)) p[nP++] = e1;
                    }

                    if (angleInRange(angle(c2-xPx, yPx-yi),theta0,theta1))
                       p[nP++] = c2;

                    for (int j = 1; j < nP; j++) {
                       // c.f. comment on corresponding vertical code above. 
                       if (Math.abs(theta0-theta1) <= Math.PI ||
                           angleInRange(angle((0.3*p[j]+0.7*p[j-1])-xPx,
                                              yPx-yi),
                                        theta0,theta1)) {
                          // widening of EPS pixels on either side fills in
                          // tiny intra-slice gaps that can sometimes appear
                          // by making slices just a tad bigger.
                          realizeOneImageOfSymbol(pp, grp, arp,
                             symbol, null,
                             onY2, 
                             clipPlotArea,
                             clipDecoratedChart,
                             p[j-1]-EPS,
                             yi-0.5*
                             thickness,
                             prevXPx, prevYPx,
                             nextXPx, nextYPx,
                             p[j]-p[j-1] + 2*EPS,
                             thickness);
                       }
                    }
                 }
              }
           }

           // if the image has an attached label, realize that
           if (annotation!=null &&
               (annotation.getText() != null ||
                annotation.getWidget() != null) &&
               annotation.getVisible()) {

              // plus x-axis, for shifts, always corresponds to
              // outward pointing radius that bisects the slice,
              // with positive y axis, for shifts, at a 90 degree
              // counter-clockwise rotation from this x. Basic
              // trigonometry and this spec yeilds lines below.
              double thetaMid = (theta0+theta1)/2.;
              double dX = annotation.getXShift();
              double dY = annotation.getYShift();
              double sinTheta = Math.sin(thetaMid);
              double cosTheta = Math.cos(thetaMid);
              AnnotationLocation loc = annotation.getLocation();
              if (null == loc) loc = defaultAnnotationLocation();
              // note: pixel Y increases down but yShift & "trig Y"
              // increase going up, which explains dY sign reversal
              arp.renderAnnotation(annotation,
                                  loc.decodePieLocation(thetaMid),
                                  xPx+(r+dX)*cosTheta - dY*sinTheta,
                                  yPx-(r+dX)*sinTheta - dY*cosTheta,
                                  0, 0,
                                  symbol);
           }
        }
      } // end of class PieSliceSymbolType


     
      private static class VBarBottom extends SymbolType {
         VBarBottom(int wm, int hm) {
             super(wm, hm,0.5,0.5,0.5,0.5, Boolean.FALSE);
         }
         protected double defaultFillSpacing() {
            return DEFAULT_BAR_FILL_SPACING;
         }
         protected AnnotationLocation defaultHoverLocation() {
            return DEFAULT_VBARBOTTOM_HOVER_LOCATION;
         }
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                      double yMin, double yMax, double yMid) {
            return yMax - y;
         }
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
      } // end of class VBarBottom
      private static class VBarBaseline extends SymbolType {
         VBarBaseline(int wm, int hm) {
             super(wm, hm, 0, 0, 0.5, 0.5, Boolean.FALSE);
         }
         protected double defaultFillSpacing() {
            return DEFAULT_BAR_FILL_SPACING;
         }
         protected AnnotationLocation defaultHoverLocation() {
            return DEFAULT_VBAR_BASELINE_HOVER_LOCATION;
         }
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                         double yMin, double yMax,double yMid) {
           return y - yMid;
         }
        
         double getUpperLeftY(double height, double y,
                              double yPrev, double yNext,
                              double yMin, double yMax, double yMid,
                              int yMouse)  {
             return yMid;
         }
        
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
      } // end of class VBarBaseline
      /** Use vertical bars that extend from the top of the chart
       ** to each point on the curve.
       **/
      private static class VBarTop extends SymbolType {
         VBarTop(int wm, int hm) {
             super(wm, hm, 0.5, 0.5, 0.5, 0.5, Boolean.FALSE);
         }
         protected double defaultFillSpacing() {
            return DEFAULT_BAR_FILL_SPACING;
         }
         protected AnnotationLocation defaultHoverLocation() {
            return DEFAULT_VBARTOP_HOVER_LOCATION;
         }
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                      double yMin, double yMax, double yMid) {
            return y - yMin;
         }
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
      } // end of class VBarTop
      /**
       ** Points on curves with this symbol type are positioned
       ** at the center of the plot area, and do not have a
       ** visible symbol.<p>
       **
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the center of the plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_CENTER =
         new AnnotationAnchor(AnnotationLocation.CENTER);
     
      /**
       ** Points on curves with this symbol type are positioned
       ** at the center of the right edge of the plot area, and
       ** do not have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the center of the right
       ** edge of the plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_EAST =
         new AnnotationAnchor(AnnotationLocation.EAST);

      /**
       ** When passed to the <tt>setHoverAnnotationSymbolType</tt>
       ** method, this symbol type enables
       ** <tt>setTitle</tt>-like, "anchored at the mouse cursor"
       ** hover annotation positioning. Specifically, hover annotions act as
       ** if they were annotations of 1px x 1px
       ** points placed at the current mouse cursor position.
       ** <p>
       **
       ** Because this and its related symbol types,
       ** <tt>ANCHOR_MOUSE_SNAP_TO_X</tt> and
       ** <tt>ANCHOR_MOUSE_SNAP_TO_Y</tt>, are intended only to
       ** facilitate positioning of hover-induced pop-up annotations
       ** (via the <tt>setHoverAnnotationSymbolType</tt> method) I
       ** cannot imagine a scenario where it would make sense to use
       ** them as the symbol type of an ordinary, user defined, curve
       ** (if you find a use for this, please let me know).
       **
       **
       ** @see #ANCHOR_MOUSE_SNAP_TO_X ANCHOR_MOUSE_SNAP_TO_X
       ** @see #ANCHOR_MOUSE_SNAP_TO_Y ANCHOR_MOUSE_SNAP_TO_Y
       ** @see Symbol#setHoverLocation setHoverLocation
       ** @see Symbol#setHoverAnnotationSymbolType setHoverAnnotationSymbolType
       ** @see Symbol#setHovertextTemplate setHovertextTemplate
       ** @see Symbol#setHoverXShift setHoverXShift
       ** @see Symbol#setHoverYShift setHoverYShift
       ** @see Symbol#setHoverWidget setHoverWidget
       **/
      public static SymbolType ANCHOR_MOUSE = new AnnotationAnchor(
         AnnotationLocation.AT_THE_MOUSE);

      /**
       * The same as the ANCHOR_MOUSE symbol type, except that
       * the x coordinate of the rendered symbol is taken from
       * the x coordinate of the point, rather than the x
       * coordinate of the mouse.
       *
       * @see #ANCHOR_MOUSE ANCHOR_MOUSE
       * @see #ANCHOR_MOUSE_SNAP_TO_Y ANCHOR_MOUSE_SNAP_TO_Y
       *
       */
      public static SymbolType ANCHOR_MOUSE_SNAP_TO_X =
         new AnnotationAnchor(AnnotationLocation.AT_THE_MOUSE_SNAP_TO_X);
      /**
       * The same as the ANCHOR_MOUSE symbol type, except that
       * the y coordinate of the rendered symbol is taken from
       * the y coordinate of the point, rather than the y
       * coordinate of the mouse.
       *
       * @see #ANCHOR_MOUSE ANCHOR_MOUSE
       * @see #ANCHOR_MOUSE_SNAP_TO_X ANCHOR_MOUSE_SNAP_TO_X
       *
       */
      public static SymbolType ANCHOR_MOUSE_SNAP_TO_Y =
         new AnnotationAnchor(AnnotationLocation.AT_THE_MOUSE_SNAP_TO_Y);
      /**
       ** Points on curves with this symbol type are positioned
       ** at the center of the top edge of the plot area, and do
       ** not have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the center of the top edge of
       ** the plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_NORTH =
         new AnnotationAnchor(AnnotationLocation.NORTH);
      /**
       ** Points on curves with this symbol type are positioned
       ** at the upper right corner of the plot area, and do not
       ** have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the upper right corner of the
       ** plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_NORTHEAST =
         new AnnotationAnchor(AnnotationLocation.NORTHEAST);

      /**
       ** Points on curves with this symbol type are positioned
       ** at the upper left corner of the plot area, and do not
       ** have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the upper left corner of the
       ** plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_NORTHWEST =
         new AnnotationAnchor(AnnotationLocation.NORTHWEST);

      /**
       ** Points on curves with this symbol type are positioned
       ** at the center of the bottom edge of the plot area, and
       ** do not have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the center of the bottom edge
       ** of the plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_SOUTH =
         new AnnotationAnchor(AnnotationLocation.SOUTH);


      /**
       ** Points on curves with this symbol type are positioned
       ** at the lower right corner of the plot area, and do not
       ** have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the lower right corner of the
       ** plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_SOUTHEAST =
         new AnnotationAnchor(AnnotationLocation.SOUTHEAST);


      /**
       ** Points on curves with this symbol type are positioned
       ** at the lower left corner of the plot area, and do not
       ** have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the lower left corner of the
       ** plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_SOUTHWEST =
         new AnnotationAnchor(AnnotationLocation.SOUTHWEST);

      /**
       ** Points on curves with this symbol type are positioned
       ** at the center of the left edge of the plot area, and do
       ** not have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the center of the left edge of
       ** the plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_WEST =
         new AnnotationAnchor(AnnotationLocation.WEST);

     /** Use rectangles horizontally and vertically centered
       ** on each point of the curve */
      public static SymbolType BOX_CENTER =
           new SymbolType(0,0,0,0,0,0);
      /** Use rectangles just to the right of, and
       ** vertically centered on, each point of the curve */
      public static SymbolType BOX_EAST =
           new SymbolType(1, 0, 0.5, -0.5, 0, 0);
      /** Use rectangles just above, and horizontally centered
       ** on, each point of the curve */
      public static SymbolType BOX_NORTH =
         new SymbolType(0, -1,0,0,-0.5,0.5);
     
      /** Use rectangles just above, and to the right of,
       ** each point of the curve */
      public static SymbolType BOX_NORTHEAST =
         new SymbolType(1, -1, 0.5,-0.5,-0.5,0.5);
     
      /** Use rectangles just above and to the left of,
       ** each point of the curve */
      public static SymbolType BOX_NORTHWEST =
          new SymbolType(-1, -1, -0.5, 0.5, -0.5, 0.5);

      /** Use rectangles just below, and horizontally centered
       ** on, each point of the curve */
      public static SymbolType BOX_SOUTH =
         new SymbolType(0, 1, 0, 0, 0.5, -0.5);

      /** Use rectangles just below, and to the right of,
       ** each point of the curve */
      public static SymbolType BOX_SOUTHEAST =
         new SymbolType(1, 1, 0.5, -0.5, 0.5, -0.5);

      /** Use rectangles just below, and to the left of,
       ** each point of the curve */
      public static SymbolType BOX_SOUTHWEST =
        new SymbolType(-1, 1, -0.5, 0.5, 0.5, -0.5);

      /** Use rectangles just to the left of, and vertically centered
       ** on, each point of the curve */
      public static SymbolType BOX_WEST =
        new SymbolType(-1, 0, -0.5, 0.5, 0, 0);
      /**
       ** Use horizontal bars that extend from the x,y position
       ** associated with each point, to the x position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and that are
       ** vertically centered on the data point.
       **
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       ** @see Symbol Symbol
       **
       **/
      public static SymbolType HBAR_BASELINE_CENTER =
        new HBarBaseline(0,0);
      /**
       ** Use horizontal bars that extend from the x,y position
       ** associated with each point, to the x position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and whose
       ** bottom edge passes through the data point.
       **
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       ** @see Symbol Symbol
       **
       **/
      public static SymbolType HBAR_BASELINE_NORTH =
        new HBarBaseline(0,-1);
      /**
       ** Use horizontal bars that extend from the x,y position
       ** associated with each point, to the x position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and whose
       ** top edge passes through the data point.
       **
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       **
       **/
      public static SymbolType HBAR_BASELINE_SOUTH =
        new HBarBaseline(0,1);
      /** Use horizontal bars that extend from the right y-axis
       ** to each point on the curve, and that are vertically
       ** centered on the point.
       **/
      public static SymbolType HBAR_EAST = new HBarRight(1,0);
      private static SymbolType line  = new LineSymbolType();  
      /**
       ** @deprecated
       **
       ** As of version 2.4, this symbol has been redefined to
       ** be synonomous with the LINE symbol type.
       ** <p>
       **
       ** Prior to v2.4, this symbol drew a horizontal bar from
       ** each point to the x coordinate of the next point.  Some
       ** applications may need to use a revised point set in
       ** order to produce the same curves using <tt>LINE</tt>
       ** that they used to produce with this symbol.  <p>
       **
       ** See the discussion within the {@link #VBAR_NEXT
       ** VBAR_NEXT} symbol for more information about why
       ** support for these vertically and horizontally constrained
       ** connecting line symbol types was dropped.
       **
       ** @see #LINE LINE
       ** @see #HBAR_PREV HBAR_PREV
       ** @see #VBAR_PREV VBAR_PREV
       ** @see #VBAR_NEXT VBAR_NEXT
       **
       **/     
      public static SymbolType HBAR_NEXT = line;
      /** Use horizontal bars that extend from the right y-axis
       ** to each point on the curve, and that are vertically
       ** just above the point.
       **/
      public static SymbolType HBAR_NORTHEAST = new HBarRight(1,-1);


      /** Use horizontal bars that extend from the left y-axis
       ** to each point on the curve, and that are vertically
       ** just above point.
       **/
      public static SymbolType HBAR_NORTHWEST  = new HBarLeft(-1,-1);
      /**
       ** @deprecated
       **
       ** As of version 2.4, this symbol has been redefined to
       ** be synonymous with the LINE symbol type.
       ** <p>
       **
       ** Prior to v2.4, this symbol drew a horizontal bar from
       ** each point to the x coordinate of the previous point.
       ** Some applications may need to use a revised point set
       ** in order to produce the same curves using <tt>LINE</tt>
       ** that they used to produce with this symbol.  <p>
       **
       ** See the discussion within the {@link #VBAR_NEXT
       ** VBAR_NEXT} symbol for more information about why
       ** support for these vertically and horizontally constrained
       ** connecting line symbol types was dropped.
       **
       ** @see #LINE LINE
       ** @see #HBAR_NEXT HBAR_NEXT
       ** @see #VBAR_PREV VBAR_PREV
       ** @see #VBAR_NEXT VBAR_NEXT
       **
       **
       **/
      public static SymbolType HBAR_PREV = line;
      /** Use horizontal bars that extend from the right y-axis
       ** to each point on the curve, and that are vertically
       ** just below the point.
       **/
      public static SymbolType HBAR_SOUTHEAST = new HBarRight(1,1);
      /** Use horizontal bars that extend from the left y-axis
       ** to each point on the curve, and that are vertically
       ** just below the point.
       **/
      public static SymbolType HBAR_SOUTHWEST  = new HBarLeft(-1,1);

      /** Use horizontal bars that extend from the left y-axis
       ** to each point on the curve, and that are vertically
       ** centered on the point.
       **/
      public static SymbolType HBAR_WEST  = new HBarLeft(-1,0);  


      /**
       ** This symbol type draws a continuous straight line between
       ** successive individual data points.  By default, the line is
       ** drawn via an appropriate series of rectangular HTML elements,
       ** which can produce a "stair-step" look at certain angles.
       **
       ** <small><blockquote> <i>Tip:</i> You can get
       ** order-of-magnitude faster, and crisper, line charts by
       ** adding an external vector graphics library to GChart
       ** via the <tt>setCanvasFactory</tt> method.</small> <p>
       **
       ** Apart from this connecting line, the individual data
       ** points are displayed exactly as they would have been
       ** displayed via BOX_CENTER.  <p>
       **
       ** Produces a connecting line similar to what could be
       ** produced via BOX_CENTER with a fill spacing of 1px,
       ** except that it uses a more efficient representation
       ** that merges vertical or horizontal "dot blocks" into
       ** single HTML elements whenever possible.
       ** <p>
       **
       ** To produce a line without showing the individual data
       ** points as separate rectangular symbols, set width and
       ** height to match your symbol's specified
       ** <tt>fillThickness</tt>.
       **
       ** @see #BOX_CENTER BOX_CENTER
       ** @see #setCanvasFactory setCanvasFactory
       ** @see Symbol#setFillThickness setFillThickness
       **
       **
       **
       **/
      public static SymbolType LINE = line;

      /** @deprecated
       **
       ** In GChart 2.3, you had to use this special symbol type
       ** to get GChart to use an external canvas library to
       ** draw crisp connecting lines.
       ** <p>
       **
       ** As of GChart 2.5, the <tt>LINE</tt> symbol type will,
       ** by default, be rendered with whatever external canvas
       ** library you provide to GChart via the
       ** <tt>setCanvasFactory</tt> method.
       **
       ** <p>
       **
       ** So now, <tt>LINE_CANVAS</tt> is just another name
       ** for <tt>LINE</tt>. Please replace <tt>LINE_CANVAS</tt> with
       ** <tt>LINE</tt> in your code.
       ** <p>
       **
       ** <small> Note that <tt>LINE</tt> only draws continuous lines if
       ** fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>. With
       ** fill spacing > 0, it uses the old HTML-rendering method. Since
       ** <tt>0</tt> is now the new default fill spacing for the
       ** <tt>LINE</tt> symbol type, normally <tt>LINE</tt> works
       ** exactly like <tt>LINE_CANVAS</tt> did. But, if you had
       ** explicitly set the fill spacing, you may have to remove this
       ** specification, or set it to <tt>0</tt>, to get the same
       ** behavior you had before with <tt>LINE_CANVAS</tt><p>
       ** </small>
       **
       ** @see #LINE LINE
       ** @see #setCanvasFactory setCanvasFactory
       ** @see Symbol#setFillSpacing setFillSpacing
       **
       **/
      public static SymbolType LINE_CANVAS  = line;
      /**
       ** A symbol type that does not draw any main symbol. Use
       ** this symbol type for curves whose points exist solely
       ** for the purpose of positioning their associated
       ** annotations. Note that if <tt>fillThickness</tt> is
       ** non-zero, any connecting dots between the points will
       ** still be drawn.  <p>
       **
       ** Equivalent to using the <tt>BOX_CENTER</tt> symbol
       ** type, but with the host symbol's width and height both
       ** set to zero, so that no box symbol is ever visible.
       ** <p>
       **
       ** On Disabling hover selection feedback via <tt>NONE</tt>:
       ** <p>
       **
       ** <blockquote><small>
       ** Note that if the border width of the host symbol is negative,
       ** consistent with a 0 x 0 px <tt>BOX_CENTER</tt> symbol type, an
       ** external border will still appear around the
       ** <tt>SymbolType.NONE</tt> symbol. Because the default hover
       ** selection border width is <tt>-1</tt>, when passing
       ** <tt>SymbolType.NONE</tt> to
       ** <tt>setHoverSelectionSymbolType</tt>, you generally will also
       ** need to add a code line such as:
       **
       ** <pre>
       **   getCurve().getSymbol().setHoverSelectionBorderWidth(0);
       ** </pre>
       ** <p>
       **
       ** If your intention is to disable hover selection feedback,
       ** it's probably easier to just use
       ** <tt>setHoverSelectionEnabled(false)</tt>, rather than
       ** setting the hover selection symbol type to <tt>NONE</tt>.
       **
       **</small></blockquote>
       **
       ** @see #BOX_CENTER BOX_CENTER
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setHoverSelectionSymbolType
       ** setHoverSelectionSymbolType
       ** @see Symbol#setHoverSelectionEnabled setHoverSelectionEnabled
       **/
      public static SymbolType NONE =
         new SymbolType(0, 0, 0, 0, 0, 0) {
         public double getAdjustedWidth(double width, double x,
                                     double xPrev, double xNext,
                                     double xMin, double xMax, double xMid) {
            return 0;
         }
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                      double yMin, double yMax, double yMid) {
            return 0;
         }
         int getIconHeight(int legendFontSize) {
            return 0;
         }
         int getIconWidth(int legendFontSize) {
            return 0;
         }

      };
      /**
       ** Draws a pie slice whose area is shaded using horizontal
       ** bars.
       **
       ** <p>
       ** The vertical distance between corresponding edges of
       ** successive bars is governed by the symbol's fill
       ** spacing property; the height of each bar is defined by
       ** the symbol's fill thickness property; the border and
       ** background of each shading bar are defined by the
       ** symbol's border color, border width, border style, and background
       ** color properties.
       **
       ** <p> The radius of the pie slice (length of the non-arc
       ** sides of the slice) is chosen such that a circle with
       ** this radius circumscribes the host <tt>Symbol</tt>'s
       ** width/height determined rectangle. The slice pivot point
       ** is defined by each point's x,y position, and the
       ** orientation and size of the slice by the corresponding
       ** properties (see links below) of the host <tt>Symbol</tt>.
       **
       ** @see Symbol#setFillSpacing setFillSpacing
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setBorderColor setBorderColor
       ** @see Symbol#setBorderWidth setBorderWidth
       ** @see Symbol#setBackgroundColor setBackgroundColor
       ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
       ** @see Symbol#setPieSliceSize setPieSliceSize
       ** @see Curve.Point#setX setX
       ** @see Curve.Point#setY setY
       ** @see #PIE_SLICE_VERTICAL_SHADING PIE_SLICE_VERTICAL_SHADING
       ** @see #PIE_SLICE_HATCHED_SHADING PIE_SLICE_HATCHED_SHADING
       ** @see #PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
       ** @see Symbol Symbol
       **
       **
       **/
      public static SymbolType PIE_SLICE_HORIZONTAL_SHADING =
        new PieSliceSymbolType(true, false, false, 0, 0, 0, 0);
      /**
       ** Draws a pie slice whose area is shaded using vertical
       ** bars.
       ** <p>
       **
       ** The horizontal distance between corresponding edges of
       ** successive bars is governed
       ** by the symbol's fill spacing property; the width
       ** of each bar is defined by the symbol's fill thickness
       ** property; the border and background of each
       ** shading bar are defined by the symbol's border color,
       ** border width, and background color properties.
       **
       ** <p> The radius of the pie slice (length of the non-arc
       ** sides of the slice) is chosen such that a circle with
       ** this radius circumscribes the host <tt>Symbol</tt>'s
       ** width/height determined rectangle. The slice pivot point
       ** is defined by each point's x,y position, and the
       ** orientation and size of the slice by the corresponding
       ** properties (see links below) of the host <tt>Symbol</tt>.
       **
       ** @see Symbol#setFillSpacing setFillSpacing
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setBorderColor setBorderColor
       ** @see Symbol#setBorderWidth setBorderWidth
       ** @see Symbol#setBackgroundColor setBackgroundColor
       ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
       ** @see Symbol#setPieSliceSize setPieSliceSize
       ** @see Curve.Point#setX setX
       ** @see Curve.Point#setY setY
       ** @see #PIE_SLICE_HORIZONTAL_SHADING PIE_SLICE_HORIZONTAL_SHADING
       ** @see #PIE_SLICE_HATCHED_SHADING PIE_SLICE_HATCHED_SHADING
       ** @see #PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
       ** @see Symbol Symbol
       **
       **
       **/
      public static SymbolType PIE_SLICE_VERTICAL_SHADING =
        new PieSliceSymbolType(false, true, false, 0, 0, 0, 0);
      /**
       ** Draws a pie slice whose area is shaded using both vertical
       ** and horizontal bars, which produces a "cross-hatched"
       ** pattern.
       ** <p>
       **
       ** The distance between corresponding edges of successive
       ** bars is governed by the symbol's fill spacing
       ** property; the thickness of each bar is defined by the
       ** symbol's fill thickness property; the border and
       ** background of each shading bar are defined by the
       ** symbol's border color, border width, border style, and background
       ** color properties.
       **
       ** <p> The radius of the pie slice (length of the non-arc
       ** sides of the slice) is chosen such that a circle with
       ** this radius circumscribes the host <tt>Symbol</tt>'s
       ** width/height determined rectangle. The slice pivot point
       ** (i.e. pie center) is defined by each point's x,y position, and the
       ** orientation and size of the slice by the corresponding
       ** properties (see links below) of the host <tt>Symbol</tt>.
       **
       ** @see Symbol#setFillSpacing setFillSpacing
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setBorderColor setBorderColor
       ** @see Symbol#setBorderWidth setBorderWidth
       ** @see Symbol#setBackgroundColor setBackgroundColor
       ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
       ** @see Symbol#setPieSliceSize setPieSliceSize
       ** @see Curve.Point#setX setX
       ** @see Curve.Point#setY setY
       ** @see #PIE_SLICE_VERTICAL_SHADING PIE_SLICE_VERTICAL_SHADING
       ** @see #PIE_SLICE_HORIZONTAL_SHADING PIE_SLICE_HORIZONTAL_SHADING
       ** @see #PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
       ** @see Symbol Symbol
       **
       **
       **/
      public static SymbolType PIE_SLICE_HATCHED_SHADING =
        new PieSliceSymbolType(true, true, false, 0, 0, 0, 0);
      /**
       ** Draw a pie slice whose area is shaded using either
       ** vertical bars or horizontal bars--whichever
       ** renders the slice more efficiently. Specifically, pie
       ** slices that are wider than they are tall use horizontal
       ** shading and pie slices that are taller than they are
       ** wide use vertical shading. These choices minimize the
       ** the number of shading bars (and thus memory and time)
       ** required to render the pie slice.
       **
       ** <p>
       **
       ** The distance between corresponding edges of successive
       ** bars is governed by the symbol's fill spacing property;
       ** the thickness of each bar is defined by the symbol's
       ** fill thickness property; the border and background of
       ** each shading bar are defined by the symbol's border
       ** color, border width, and background color properties.
       ** <p>
       **
       ** The pie slice radius is always determined by the
       ** formula:
       **
       ** <p>
       ** <blockquote>
       **   <pre>
       **   sqrt(symbolWidth^2+symbolHeight^2)/2
       **   </pre>
       ** </blockquote>
       **
       ** <p>
       ** Here <tt>symbolWidth</tt> and <tt>symbolHeight</tt> are the pie
       ** slice symbol's width and height, in pixels.
       ** <p>
       **
       ** Note that this formula implies that the pie slice
       ** radius is the one associated with the circle that
       ** circumscribes the symbol, that is, the smallest circle
       ** that is big enough to completely contain the symbol's
       ** width/height defined bounding rectangle. Equivalently,
       ** the length of the pie slice radius equals the half the
       ** length of the diagonal across the symbol's bounding
       ** rectangle.
       **
       ** <p>
       **
       ** To assure an integral number of shading bars and thus
       ** improve the visual look of the pie chart, GChart
       ** automatically rounds the radius to the nearest
       ** multiple of the specified <tt>fillSpacing</tt>.  For
       ** example, if the radius computed from the above formula
       ** were 96 pixels and the <tt>fillSpacing</tt> were 10
       ** pixels, GChart would actually use a radius of 100 pixels.
       **
       ** <p>
       **
       ** <i>Tip:</i> To produce a pie slice with a radius, r, set
       ** the symbol's height to 0, and its width to 2*r (or
       ** visa-versa). To specify the radius in pixels, use the
       ** symbol's <tt>setWidth</tt> and <tt>setHeight</tt>
       ** methods; to specify the radius in "model units" (which
       ** scale up or down with the chart dimensions) use
       ** <tt>setModelWidth</tt> and <tt>setModelHeight</tt> instead.
       **
       ** <p>
       **
       **
       ** <p>
       ** The slice pivot point (i.e. pie center)
       ** is defined by each point's x,y position, and the
       ** orientation and size of the slice by the
       ** <tt>setPieSliceOrientation</tt> and
       ** <tt>setPieSliceSize</tt> methods
       ** of the host <tt>Symbol</tt>.
       ** <p>
       **
       ** Creating a pie chart from such pie slices requires
       ** that you define a separate curve for each slice,
       ** as illustrated in the code below:
       **
       ** {@code.sample ..\..\..\..\..\..\gcharttestapp\src\com\googlecode\gchart\gcharttestapp\client\GChartExample09.java}
       **
       ** <p>
       **
       ** Which produces this: <p>
       **
       ** <img
       ** src="{@docRoot}/com/googlecode/gchart/client/doc-files/gchartexample09.png">
       **
       ** <p> Note how, because
       ** <tt>PIE_SLICE_OPTIMAL_SHADING</tt> was used, vertical
       ** or horizontal shading is automatically selected so as
       ** to minimize the number of shading bars in each slice.
       **
       ** @see Symbol Symbol
       ** @see Symbol#setFillSpacing setFillSpacing
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setBorderColor setBorderColor
       ** @see Symbol#setBorderWidth setBorderWidth
       ** @see Symbol#setBackgroundColor setBackgroundColor
       ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
       ** @see Symbol#setPieSliceSize setPieSliceSize
       ** @see Symbol#setWidth setWidth
       ** @see Symbol#setHeight setHeight
       ** @see Symbol#setModelWidth setModelWidth
       ** @see Symbol#setModelHeight setModelHeight
       ** @see Curve.Point#setX setX
       ** @see Curve.Point#setY setY
       ** @see #PIE_SLICE_VERTICAL_SHADING PIE_SLICE_VERTICAL_SHADING
       ** @see #PIE_SLICE_HORIZONTAL_SHADING PIE_SLICE_HORIZONTAL_SHADING
       ** @see #PIE_SLICE_HATCHED_SHADING PIE_SLICE_HATCHED_SHADING
       **
       **/
      public static SymbolType PIE_SLICE_OPTIMAL_SHADING =
        new PieSliceSymbolType(false, false, true, 0, 0, 0, 0);
     
      /**
       ** Use vertical bars that extend from the x,y position
       ** associated with each point, to the y position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and that are
       ** horizontally centered on the data point.
       **
       ** @see Symbol Symbol
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       **
       **/
      public static SymbolType VBAR_BASELINE_CENTER = new VBarBaseline(0,0);
      /**
       ** Use vertical bars that extend from the x,y position
       ** associated with each point, to the y position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and whose
       ** right edge passes through the data point.
       **
       ** @see Symbol Symbol
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       **
       **/
      public static SymbolType VBAR_BASELINE_WEST = new VBarBaseline(-1,0);
      /**
       ** Use vertical bars that extend from the x,y position
       ** associated with each point, to the y position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and whose
       ** left edge passes through the data point.
       **
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       **
       **/
      public static SymbolType VBAR_BASELINE_EAST = new VBarBaseline(1,0);
      /**
       ** @deprecated
       **
       ** As of version 2.4, this symbol has been redefined to
       ** be synonomous with the LINE symbol type.
       ** <p>
       **
       ** Prior to v2.4, this symbol drew a vertical bar from
       ** each point to the y coordinate of the next point. Some
       ** applications may need to use a revised point set in
       ** order to produce the same curves with <tt>LINE</tt>
       ** that they used to produce with this symbol.
       ** <p>
       **
       ** Support was dropped because:
       **
       ** <p>
       ** <ol>
       **
       **   <li>Continued support would have complicated
       ** implementation of the new hover feedback system
       ** introduced with v2.4 (these are the only symbols whose
       ** hit-testing-related size depends on preceding or
       ** subsequent points).<p>
       **
       **   <li>With the introduction of
       ** <tt>LINE</tt> the main reason for this, and related,
       ** vertically (or horizontally) constrained line drawing
       ** symbol types had been eliminated (had <tt>LINE</tt>
       ** existed at the beginning, these constrained line drawing
       ** symbol types would never have been added).
       ** <p>
       **
       ** </ol>
       ** <p>
       **
       ** Finally, note that if lines are vertical or horizontal,
       ** and solidly connected, <tt>LINE</tt> automatically
       ** collapses them into a single element, so no
       ** element-based efficiency losses need be associated with
       ** replacing curves using such rectilinear symbol types
       ** with equivalent curves rendered via the
       ** <tt>LINE</tt> symbol type.
       ** <p>
       **
       **
       ** @see #HBAR_PREV HBAR_PREV
       ** @see #HBAR_NEXT HBAR_NEXT
       ** @see #LINE LINE
       ** @see #VBAR_PREV VBAR_PREV
       **
       **/
      public static SymbolType VBAR_NEXT = line;
/*
        new SymbolType(0,0, 0, 0, 0.5, 0.5) {
         public double getAdjustedHeight(double height,
                                      double y,
                                      double yPrev, double yNext,
                                      double yMin, double yMax,
                                      double yMid) {
            // x!=x is a faster isNaN
            return yNext - y;
         }
         public double getUpperLeftY(double height, double y,
                                  double yPrev, double yNext,
                                  double yMin, double yMax, double yMid) {
            return y;
         }
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return (int) Math.round(legendFontSize/4.);
         }

      };
*/     
      /** Use vertical bars that extend from the top of the chart
       ** to each point on the curve, and are horizontally
       ** centered on the point.
       **/
      public static SymbolType VBAR_NORTH = new VBarTop(0, -1);
      /** Use vertical bars that extend from the top of the chart
       ** to each point on the curve, and are horizontally
       ** to the right of the point.
       **/
      public static SymbolType VBAR_NORTHEAST = new VBarTop(1, -1);
     
      /** Use vertical bars that extend from the top of the chart
       ** to each point on the curve, and are horizontally
       ** to the left of the point.
       **/
      public static SymbolType VBAR_NORTHWEST = new VBarTop(-1, -1);
      /**
       ** @deprecated
       **
       ** As of version 2.4, this symbol has been redefined to
       ** be synonomous with the LINE symbol type.
       ** <p>
       **
       ** Prior to v2.4, this symbol drew a vertical bar from
       ** each point to the y coordinate of the previous point.
       ** Some applications may need to use a revised point set
       ** in order to produce the same curves using <tt>LINE</tt>
       ** that they used to produce with this symbol.  <p>
       **
       ** See the discussion within the {@link #VBAR_NEXT
       ** VBAR_NEXT} symbol for more information about why
       ** support for these vertically and horizontally constrained
       ** connecting line symbol types was dropped.
       **
       ** @see #LINE LINE
       ** @see #HBAR_PREV HBAR_PREV
       ** @see #HBAR_NEXT HBAR_NEXT
       ** @see #VBAR_NEXT VBAR_NEXT
       **
       **/
     
      public static SymbolType VBAR_PREV = line;
/*         new SymbolType(0,0,0,0,0.5,0.5) {
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                      double yMin, double yMax, double yMid) {
            return yPrev - y;
         }
         public double getUpperLeftY(double height, double y,
                                  double yPrev, double yNext,
                                  double yMin, double yMax, double yMid) {
            return y;
         }
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return (int) Math.round(legendFontSize/4.);
         }

      };
*/     
      /** Use vertical bars that extend from the x-axis
       ** to each point on the curve, and that are horizontally
       ** centered on the point.
       **/
      public static SymbolType VBAR_SOUTH = new VBarBottom(0, 1);
      /** Use vertical bars that extend from the x-axis
       ** to each point on the curve, and that are horizontally
       ** to the right of the point.
       **/
      public static SymbolType VBAR_SOUTHEAST = new VBarBottom(1, 1);
     
      /** Use vertical bars that extend from the x-axis
       ** to each point on the curve, and that are horizontally
       ** to the left of the point.
       **/
      public static SymbolType VBAR_SOUTHWEST =
          new VBarBottom(-1, 1);
      /**
       ** Represents a single x-axis grid-line. You can use
       ** this symbol to draw a single vertical bar
       ** across the chart.
       **
       **/
      public static SymbolType XGRIDLINE =
         new SymbolType(0,0,0,0,0.5,0.5,Boolean.FALSE) {
         public double getAdjustedHeight(double height, double y,
                                        double yPrev, double yNext,
                                        double yMin, double yMax, double yMid) {
            return yMax - yMin;
         }
         public double getUpperLeftY(double height, double y,
                                  double yPrev, double yNext,
                                  double yMin, double yMax, double yMid,
                                  int yMouse) {
            return yMin;
         }
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return 1;
         }

      };
      /**
       ** Represents a single y-axis (or y2-axis) grid-line. You
       ** can use this symbol to draw a single horizontal line (or
       ** bar) across the chart, for example, to display an upper
       ** bound or control limit.
       **
       **/
      public static SymbolType YGRIDLINE =
         new SymbolType(0,0,0.5,0.5,0,0, Boolean.TRUE) {
         public double getAdjustedWidth(double width, double x,
                                     double xPrev, double xNext,
                                     double xMin, double xMax, double xMid) {
            return xMax - xMin;
         }
         public double getUpperLeftX(double width, double x,
                                     double xPrev, double xNext,
                                     double xMin, double xMax, double xMid,
                                     int xMouse) {
            return xMin;
         }
         int getIconHeight(int legendFontSize) {
            return 1;
         }
         int getIconWidth(int legendFontSize) {
            return legendFontSize;
         }

      };

      /**
       **  @deprecated
       **
       ** This symbol is the same as <tt>YGRIDLINE</tt> and
       ** was added by mistake in version 1.
       ** (the y-axis isn't defined by the symbol type, but
       **  rather by the curve's <tt>setYAxis</tt> method).
       ** <p>
       ** Please use <tt>YGRIDLINE</tt> instead.
       **
       ** @see #YGRIDLINE YGRIDLINE
       ** @see GChart.Curve#setYAxis setYAxis
       **
       **/
    
      public static SymbolType Y2GRIDLINE = YGRIDLINE;

     // play similar role as same-named fields of AnnotationLocation
      protected int heightMultiplier;
      protected int widthMultiplier;
       /*
       * If a symbol's left, right, top, or bottom edge
       * represents the x or y location associated with this
       * symbol, the corresponding pixel paddings are 0.
       *
       * If a position 1/2 pixel to the right, left, below, or
       * above (respectively) those edges represents the position
       * of the x or y in question, the values are 0.5.
       *
       * Why is this needed? Because GChart uses 1 px gridlines
       * whose center represents the point associated with the
       * gridline, and to get a corresponding edge to perfectly
       * overlay a gridline when its associated position is
       * the same as that gridline, we need to associated the
       * position of the represented x or y coordinate not
       * with the symbol's box edge itself, but rather
       * with a position 1/2 px towards the center of the
       * symbol. If you don't specify an extra half pixel
       * for, say, a vertical bar, it won't align right
       * on top of gridlines when it has the same height
       * as the associated gridline.
       * <p>
       *
       * In effect, we deliberately add a 1/2 px error to
       * certain symbols so that they appear to line up
       * perfectly with the gridlines.
       *
       */
      protected double pixelPadLeft;
      protected double pixelPadRight;
      protected double pixelPadTop;
      protected double pixelPadBottom;
    
      // symbols are part of the internals of a GChart,
      // so only we should instantiate them.
      private SymbolType(int widthMultiplier,
                         int heightMultiplier,
                         double pixelPadLeft,
                         double pixelPadRight,
                         double pixelPadTop,
                         double pixelPadBottom,
                         Boolean isHorizontallyBanded) {
         validateMultipliers(widthMultiplier, heightMultiplier);
         this.widthMultiplier = widthMultiplier;
         this.heightMultiplier = heightMultiplier;
         this.pixelPadLeft = pixelPadLeft;
         this.pixelPadRight = pixelPadRight;
         this.pixelPadTop = pixelPadTop;
         this.pixelPadBottom = pixelPadBottom;
         this.isHorizontallyBanded = isHorizontallyBanded;
      }
     
      private SymbolType(int widthMultiplier,
                         int heightMultiplier,
                         double pixelPadLeft,
                         double pixelPadRight,
                         double pixelPadTop,
                         double pixelPadBottom) {
         this(widthMultiplier,heightMultiplier,
              pixelPadLeft,
              pixelPadRight,
              pixelPadTop,
              pixelPadBottom, null);
      }


      double getAdjustedHeight(double height, double y,
                                        double yPrev, double yNext,
                                        double yMin, double yMax, double yMid) {
         return height;
      }

      double getAdjustedWidth(double width, double x,
                                      double xPrev, double xNext,
                                      double xMin, double xMax, double xMid) {
         return width;
      }

      /*
       * Pixel x-coordinate at center of bounding rectangle surrounding
       * given symbol rendered with this symbol type.
       * <p>
       *
       * This method defines the x-coordinate of the rendered
       * symbol's center-point (used for hit testing purposes) for all
       * symbols except pie slices.
       *
       */
      protected double getCenterX(PlotPanel pp, Symbol symbol,
                                  double prevX, double x, double nextX) {
        double xMin = pp.getXMin();
        double xMax = pp.getXMax();
        double xMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
        double xMinPx = pp.xToPixel(xMin);
        double xMaxPx = pp.xToPixel(xMax);
        double xMidPx = pp.xToPixel(xMid);
        double xPx = pp.xToPixel(x);
        double prevXPx = pp.xToPixel(prevX);
        double nextXPx = pp.xToPixel(nextX);
        double width = symbol.getWidth(pp);
       
        double symWidth = getAdjustedWidth(width, xPx,
                                          prevXPx, nextXPx,
                                          xMinPx, xMaxPx, xMidPx);
        if ((symWidth!=symWidth)) return Double.NaN;
       
        double xLeft = getUpperLeftX(width, xPx,
                                     prevXPx, nextXPx,
                                     xMinPx, xMaxPx, xMidPx,
                                     pp.getXMousePlotArea());
        if ((xLeft!=xLeft)) return Double.NaN;
       
        double xCenter = xLeft + symWidth/2.;

        return xCenter;

      }

      /*
       * Pixel x-coordinate at center of the symbol, used for
       * hit-testing purposes by rectangular symbol types.
       * <p>
       *
       * Overridden by pie slice symbol types.
       *
       */
      protected double getCenterX(PlotPanel pp, Symbol symbol,
                                  int iPoint) {

        Curve c = symbol.getParent();
        Curve.Point p  = c.getPoint(iPoint);
        double prevX = Double.NaN;
        double x = p.getX();
        double nextX = Double.NaN;
        if (iPoint > 0)
            prevX = c.getPoint(iPoint-1).getX();
        if (iPoint+1 < c.getNPoints())
            nextX = c.getPoint(iPoint+1).getX();
        
        double result = getCenterX(pp, symbol,
                                   prevX, x, nextX);

        return result;
      }

     
      /*
       * Pixel y-coordinate at center of bounding rectangle surrounding
       * given symbol rendered with this symbol type.
       * <p>
       *
       * This method defines the y-coordinate of the rendered
       * symbol's center-point (used for hit testing purposes) for all
       * symbols except pie slices.
       *
       */
      protected double getCenterY(PlotPanel pp, Symbol symbol,
                     double prevY, double y, double nextY, boolean onY2) {

        // the cartesian data and pixel Y coordinates are
        // flipped, hence the (counter-intuitive) min/max
        // interchange below:
        double yMin =  onY2?pp.getY2Max():pp.getYMax();
        double yMax =  onY2?pp.getY2Min():pp.getYMin();
        double yMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
        double yMinPx = pp.yToPixel(yMin,onY2);
        double yMaxPx = pp.yToPixel(yMax,onY2);
        double yMidPx = pp.yToPixel(yMid,onY2);
        double yPx = pp.yToPixel(y, onY2);
        double prevYPx = pp.yToPixel(prevY, onY2);
        double nextYPx = pp.yToPixel(nextY, onY2);
        double height = symbol.getHeight(pp, onY2);

        double symHeight = getAdjustedHeight(height, yPx,
                               prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
        if ((symHeight!=symHeight)) return Double.NaN;
       
        double yTop = getUpperLeftY(height, yPx,
                                    prevYPx, nextYPx,
                                    yMinPx, yMaxPx, yMidPx,
                                    pp.getYMousePlotArea());
        if ((yTop!=yTop)) return Double.NaN;
       
        double yCenter =  yTop + symHeight/2.;

        return yCenter;

      }

      /*
       * Pixel y-coordinate at center of the symbol, used for
       * hit-testing purposes by rectangular symbol types.
       * <p>
       *
       * Overridden by pie slice symbol types.
       *
       */
      protected double getCenterY(PlotPanel pp, Symbol symbol,
                                  int iPoint,
                                  boolean onY2) {

         Curve c = symbol.getParent();
         Curve.Point p = c.getPoint(iPoint);
         double prevY = Double.NaN;
         double y = p.getY();
         double nextY = Double.NaN;
         if (iPoint > 0)
            prevY = c.getPoint(iPoint-1).getY();
         if (iPoint+1 < c.getNPoints())
            nextY = c.getPoint(iPoint+1).getY();
        
         double result = getCenterY(pp, symbol,
                                    prevY, y, nextY, onY2);

         return result;
      }

      // pixel coordinate of left edge of symbol if rendered at given x
      // Note: this can actually be the right edge if the symbol
      // width is negative, as can occur with baseline-based bars
      protected double getEdgeLeft(PlotPanel pp, Symbol symbol,
                                   double x, boolean onY2) {
        double xMin = pp.getXMin();
        double xMax = pp.getXMax();
        double xMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
        double xMinPx = pp.xToPixel(xMin);
        double xMaxPx = pp.xToPixel(xMax);
        double xMidPx = pp.xToPixel(xMid);
        double xPx = pp.xToPixel(x);
        double prevXPx = Double.NaN;
        double nextXPx = Double.NaN;
        double width = symbol.getWidth(pp);
       
        double symWidth = getAdjustedWidth(width, xPx,
                                          prevXPx, nextXPx,
                                          xMinPx, xMaxPx, xMidPx);
        if ((symWidth!=symWidth)) return Double.NaN;
       
        double xLeft = getUpperLeftX(width, xPx,
                                     prevXPx, nextXPx,
                                     xMinPx, xMaxPx, xMidPx,
                                     pp.getXMousePlotArea());
        if ((xLeft!=xLeft)) return Double.NaN;
        double result = xLeft;
        return result;
      }

      // pixel coordinate of right edge of symbol if rendered at given x
      // Note: this can actually be the left edge if the symbol
      // width is negative, as can occur with baseline-based bars
      protected double getEdgeRight(PlotPanel pp, Symbol symbol,
                                    double x, boolean onY2) {
        double xMin = pp.getXMin();
        double xMax = pp.getXMax();
        double xMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
        double xMinPx = pp.xToPixel(xMin);
        double xMaxPx = pp.xToPixel(xMax);
        double xMidPx = pp.xToPixel(xMid);
        double xPx = pp.xToPixel(x);
        double prevXPx = Double.NaN;
        double nextXPx = Double.NaN;
        double width = symbol.getWidth(pp);
       
        double symWidth = getAdjustedWidth(width, xPx,
                                          prevXPx, nextXPx,
                                          xMinPx, xMaxPx, xMidPx);
        if ((symWidth!=symWidth)) return Double.NaN;
       
        double xLeft = getUpperLeftX(width, xPx,
                                     prevXPx, nextXPx,
                                     xMinPx, xMaxPx, xMidPx,
                                     pp.getXMousePlotArea());
        if ((xLeft!=xLeft)) return Double.NaN;
       
        double result = xLeft + symWidth;

        return result;

      }


      // pixel coordinate of top edge of symbol if rendered at given y
      // Note: this can actually be the bottom edge if the symbol
      // width is negative, as can occur with baseline-based bars
      protected double getEdgeTop(PlotPanel pp,
                                  Symbol symbol,
                                  double y,
                                  boolean onY2) {

        // the cartesian data and pixel Y coordinates are
        // flipped, hence the (counter-intuitive) min/max
        // interchange below:
        double yMin =  onY2?pp.getY2Max():pp.getYMax();
        double yMax =  onY2?pp.getY2Min():pp.getYMin();
        double yMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
        double yMinPx = pp.yToPixel(yMin,onY2);
        double yMaxPx = pp.yToPixel(yMax,onY2);
        double yMidPx = pp.yToPixel(yMid,onY2);
        double yPx = pp.yToPixel(y, onY2);
        double prevYPx = Double.NaN;
        double nextYPx = Double.NaN;
        double height = symbol.getHeight(pp, onY2);

        double symHeight = getAdjustedHeight(height, yPx,
                               prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
        if ((symHeight!=symHeight)) return Double.NaN;
       
        double yTop = getUpperLeftY(height, yPx,
                                    prevYPx, nextYPx,
                                    yMinPx, yMaxPx, yMidPx,
                                    pp.getYMousePlotArea());
        if ((yTop!=yTop)) return Double.NaN;
       
        double result = yTop;

        return result;

      }


      // pixel coordinate of bottom edge of symbol if rendered at given y
      // Note: this can actually be the top edge if the symbol
      // width is negative, as can occur with baseline-based bars
      protected double getEdgeBottom(PlotPanel pp,
                                     Symbol symbol,
                                     double y,
                                     boolean onY2) {

        // the cartesian data and pixel Y coordinates are
        // flipped, hence the (counter-intuitive) min/max
        // interchange below:
        double yMin =  onY2?pp.getY2Max():pp.getYMax();
        double yMax =  onY2?pp.getY2Min():pp.getYMin();
        double yMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
        double yMinPx = pp.yToPixel(yMin,onY2);
        double yMaxPx = pp.yToPixel(yMax,onY2);
        double yMidPx = pp.yToPixel(yMid,onY2);
        double yPx = pp.yToPixel(y, onY2);
        double prevYPx = Double.NaN;
        double nextYPx = Double.NaN;
        double height = symbol.getHeight(pp, onY2);

        double symHeight = getAdjustedHeight(height, yPx,
                               prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
        if ((symHeight!=symHeight)) return Double.NaN;
       
        double yTop = getUpperLeftY(height, yPx,
                                    prevYPx, nextYPx,
                                    yMinPx, yMaxPx, yMidPx,
                                    pp.getYMousePlotArea());
        if ((yTop!=yTop)) return Double.NaN;
       
        double result = yTop+symHeight;

        return result;

      }

      // gets edge that is furthest away from the point, horizontally
      // Note: for bar charts, this is the edge of the symbol
      // along the y-axis, y2-axis, or vertical baseline.
      protected double getEdgeOppositeHorizontally(
                                    PlotPanel pp, Symbol symbol,
                                    double x, boolean onY2) {
        double xMin = pp.getXMin();
        double xMax = pp.getXMax();
        double xMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
        double xMinPx = pp.xToPixel(xMin);
        double xMaxPx = pp.xToPixel(xMax);
        double xMidPx = pp.xToPixel(xMid);
        double xPx = pp.xToPixel(x);
        double prevXPx = Double.NaN;
        double nextXPx = Double.NaN;
        double width = symbol.getWidth(pp);
       
        double symWidth = getAdjustedWidth(width, xPx,
                                          prevXPx, nextXPx,
                                          xMinPx, xMaxPx, xMidPx);
        if ((symWidth!=symWidth)) return Double.NaN;
       
        double xLeft = getUpperLeftX(width, xPx,
                                     prevXPx, nextXPx,
                                     xMinPx, xMaxPx, xMidPx,
                                     pp.getXMousePlotArea());
        if ((xLeft!=xLeft)) return Double.NaN;

        double result = xLeft + symWidth;
        if (Math.abs(xLeft - xPx) > Math.abs(result - xPx))
           result = xLeft;

        return result;

      }

      // gets edge that is furthest away from the point, vertically
      // Note: for bar charts, this is the edge of the symbol
      // along the x-axis, x2-axis, or horizontal baseline.
      protected double getEdgeOppositeVertically(PlotPanel pp,
                                     Symbol symbol,
                                     double y,
                                     boolean onY2) {

        // the cartesian data and pixel Y coordinates are
        // flipped, hence the (counter-intuitive) min/max
        // interchange below:
        double yMin =  onY2?pp.getY2Max():pp.getYMax();
        double yMax =  onY2?pp.getY2Min():pp.getYMin();
        double yMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
        double yMinPx = pp.yToPixel(yMin,onY2);
        double yMaxPx = pp.yToPixel(yMax,onY2);
        double yMidPx = pp.yToPixel(yMid,onY2);
        double yPx = pp.yToPixel(y, onY2);
        double prevYPx = Double.NaN;
        double nextYPx = Double.NaN;
        double height = symbol.getHeight(pp, onY2);

        double symHeight = getAdjustedHeight(height, yPx,
                               prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
        if ((symHeight!=symHeight)) return Double.NaN;
       
        double yTop = getUpperLeftY(height, yPx,
                                    prevYPx, nextYPx,
                                    yMinPx, yMaxPx, yMidPx,
                                    pp.getYMousePlotArea());
        if ((yTop!=yTop)) return Double.NaN;
       
        double result = yTop+symHeight;
        if (Math.abs(yTop - yPx) > Math.abs(result - yPx))
           result = yTop;

        return result;

      }
     
      /*
       * Determines if a symbol, rendered at the specified
       * position (and with the given positions of the previous
       * and subsequent points, and the y-axis on which it is
       * rendered) intersects with a given rectangle.
       *
       */
      private boolean isIntersecting(
                     PlotPanel pp, Symbol symbol,
                     double prevX, double x, double nextX,
                     double prevY, double y, double nextY, boolean onY2,
                     double top, double right,
                     double bottom, double left) {

        double xMin = pp.getXMin();
        double xMax = pp.getXMax();
        double xMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
        double xMinPx = pp.xToPixel(xMin);
        double xMaxPx = pp.xToPixel(xMax);
        double xMidPx = pp.xToPixel(xMid);
        double xPx = pp.xToPixel(x);
        double prevXPx = pp.xToPixel(prevX);
        double nextXPx = pp.xToPixel(nextX);
        double width = symbol.getWidth(pp);
       
        double symWidth = getAdjustedWidth(width, xPx,
                                          prevXPx, nextXPx,
                                          xMinPx, xMaxPx, xMidPx);
        if ((symWidth!=symWidth)) return false;
       
        double xLeft = getUpperLeftX(width, xPx,
                                     prevXPx, nextXPx,
                                     xMinPx, xMaxPx, xMidPx,
                                     pp.getXMousePlotArea());
        if ((xLeft!=xLeft)) return false;

        // note: symWidth can be negative.
        if (Math.max(xLeft, xLeft + symWidth) < left)
           return false// symbol is entirely to left of rectangle
        else if (Math.min(xLeft, xLeft + symWidth) > right)
           return false; // symbol is entirely to right of rectangle
        // else brush and symbol have overlapping x-intervals
       
        // the cartesian data and pixel Y coordinates are flipped,
        // hence the (counter-intuitive) min/max interchange below:
        double yMin =  onY2?pp.getY2Max():pp.getYMax();
        double yMax =  onY2?pp.getY2Min():pp.getYMin();
        double yMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
        double yMinPx = pp.yToPixel(yMin,onY2);
        double yMaxPx = pp.yToPixel(yMax,onY2);
        double yMidPx = pp.yToPixel(yMid,onY2);
        double yPx = pp.yToPixel(y, onY2);
        double prevYPx = pp.yToPixel(prevY, onY2);
        double nextYPx = pp.yToPixel(nextY, onY2);
        double height = symbol.getHeight(pp, onY2);

        double symHeight = getAdjustedHeight(height, yPx,
                               prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
        if ((symHeight!=symHeight)) return false;
       
        double yTop = getUpperLeftY(height, yPx,
                                    prevYPx, nextYPx,
                                    yMinPx, yMaxPx, yMidPx,
                                    pp.getYMousePlotArea());
        if ((yTop!=yTop)) return false;
       
        // note: symHeight can be negative.
        if (Math.max(yTop, yTop + symHeight) < top)
           return false; // symbol is entirely above rectangle
        else if (Math.min(yTop, yTop + symHeight) > bottom)
           return false; // symbol is entirely below the rectangle
        // else rectangle and symbol have overlapping y-intervals

        // overlapping x and y intervals ==> rectangle intersects symbol
        return true;

      }
     
      /*
       * Determines if a symbol, when rendered at a given point,
       * intersects with a "rectangular brush".
       * <p>
       *
       * This brush is typically centered at the current mouse
       * position, and allows the user to select the point on a
       * curve, the pie slice, etc. for which hover feedback will
       * be displayed.
       * <p>
       *
       * This method gets overridden for pie slices (due to
       * their non-rectangular shape).
       *
       */
      protected boolean isIntersecting(PlotPanel pp,
                                     Symbol symbol,
                                     int iPoint,
                                     boolean onY2,
                                     int xBrush,
                                     int yBrush,
                                     int brushWidth,
                                     int brushHeight) {

         Curve c = symbol.getParent();
         Curve.Point p = c.getPoint(iPoint);
         double prevX = Double.NaN;
         double x = p.getX();
         double nextX = Double.NaN;
         double prevY = Double.NaN;
         double y = p.getY();
         double nextY = Double.NaN;
         if (iPoint > 0) {
            prevX = c.getPoint(iPoint-1).getX();
            prevY = c.getPoint(iPoint-1).getY();
         }
         if (iPoint+1 < c.getNPoints()) {
            nextX = c.getPoint(iPoint+1).getX();
            nextY = c.getPoint(iPoint+1).getY();
         }

         // Treat mouse cursor as if it were a 0x0 pixel symbol
         // centered at xBrush, yBrush to which an annotation of
         // the width, height of the brush is attached.
         int top = symbol.getBrushLocation().getUpperLeftY(
            yBrush, brushHeight, 0);
         int bottom = top + brushHeight;
         int left = symbol.getBrushLocation().getUpperLeftX(
            xBrush, brushWidth, 0);
         int right = left + brushWidth;
        
         boolean result = isIntersecting(pp, symbol,
                                    prevX, x, nextX,   
                                    prevY, y, nextY, onY2,
                                    top, right, bottom, left);

         return result;
      }

     
      // width of border of symbol displayed in legend key
      int getIconBorderWidth(int legendFontSize,
                             double symBorderFraction) {
         int result = 0;
         if (symBorderFraction > 0) {
            result = (int) Math.max(1.0, Math.floor(
               symBorderFraction * Math.min(
                 getIconWidth(legendFontSize),
                 getIconHeight(legendFontSize))));
         }
        return result;
     }
      int getIconHeight(int legendFontSize) {
         return (int) Math.round(0.75*legendFontSize);
      }

      int getIconWidth(int legendFontSize) {
         return (int) Math.round(0.75*legendFontSize);
      }  
     
     
      double getUpperLeftX(double width, double x,
                           double xPrev, double xNext,
                           double xMin, double xMax, double xMid,
                           int xMouse) {
         double adjWidth = getAdjustedWidth(width, x,
                                      xPrev, xNext, xMin, xMax, xMid);
         double result =
           x + (0.5*(widthMultiplier - 1)) * adjWidth;
         return result;
      }  

      double getUpperLeftY(double height, double y,
                           double yPrev, double yNext,
                           double yMin, double yMax, double yMid,
                           int yMouse)  {
         double adjHeight = getAdjustedHeight(height, y,
                                          yPrev, yNext, yMin, yMax, yMid);
         double result =
           y + (0.5*(heightMultiplier - 1)) * adjHeight;
         return result;

      }

      protected AnnotationLocation defaultAnnotationLocation() {
         //         return AnnotationLocation.SOUTH;
         AnnotationLocation result = defaultHoverLocation();
         return result;
      }
     // fillSpacing to use when a symbol's fillSpacing is Double.NaN
      protected double defaultFillSpacing() {
        return DEFAULT_SYMBOL_FILL_SPACING;
      }
     // fillThickness to use when a symbol's fillThickness is
     // GChart.NAI
      protected int defaultFillThickness() {
        return DEFAULT_SYMBOL_FILL_THICKNESS;
      }
      // symbol-type-specific default hovertextTemplate 
      protected String defaultHovertextTemplate() {
          return DEFAULT_HOVERTEXT_TEMPLATE;
      }
      // symbol-type-specific default location of hover feedback 
      protected AnnotationLocation defaultHoverLocation() {
          return DEFAULT_HOVER_LOCATION;
      }
  /*
   * Unmanaged images. Supports older code that simply
   * zaps/recreates each image, relying on browser's garbage
   * collector to deal with the reuse issue (that's slower).
   *
   */
   private Image createImage(Symbol symbol,
                             double width, double height,
                             int borderWidth,
                             String url) {

     Image result = new Image(url);
     // if smaller of width, height is at least twice
     // the border width, border width is used as is, otherwise,
     // it's replaced with half the smaller of width, height:
     int cappedBW = (int)
       ((2*borderWidth <= ((width < height) ? width : height)) ?
        borderWidth : (((width < height) ? width : height)/2));                       

     String borderColor = symbol.getBorderColorCSS();
      // If border was too big to fit inside rectangle, since GChart
      // borders are uniform around the rectangle, odd-sized
      // dimensions can leave a single "leftover" 1px inside the
      // border. Set background to the border's color so that the
     // border, in effect, takes up the entire rectangle.
     String backgroundColor = (cappedBW == borderWidth) ?
                            symbol.getBackgroundColorCSS() :
                            borderColor;
     // In principle, x,y position should also change with transparency
     // emulation in some cases. But these images are only used in
     // tables on the legend key, where they are always centered, so
     // that doesn't matter.

      if (TRANSPARENT_BORDER_COLOR == borderColor) {//transparency emulation
         if (cappedBW > 0) {
            // to emulate an internal transparent border using a 0 width
            // border, we need to shrink the size by twice the amount
            // of the border.
            height -= 2*cappedBW; // shrink size
            width -= 2*cappedBW;
         }
         // else, external border is just eliminated, no adjustment needed
         cappedBW = 0;
         borderColor = "transparent";
         if (TRANSPARENT_BORDER_COLOR == backgroundColor)
            backgroundColor = "transparent";
      }
      else if (cappedBW > 0) {
        height -= 2*cappedBW; // shrink size to incorporate
        width -= 2*cappedBW; // impact of internal border.
      }
     GChart.setBackgroundColor(result, backgroundColor);
     GChart.setBorderColor(result, borderColor);
     GChart.setBorderStyle(result, symbol.getBorderStyle());
     GChart.setBorderWidth(result, Math.abs(cappedBW));
     result.setPixelSize((int) Math.round(width),
                         (int) Math.round(height));
     return result;
   }
     // creates small image of symbol (used in the chart legend).
   Image createIconImage(Symbol symbol,
                         int legendFontSize, double symBorderFraction) {
      Image result = createImage(symbol,
                         getIconWidth(legendFontSize),
                         getIconHeight(legendFontSize),
                         getIconBorderWidth(legendFontSize,
                                            symBorderFraction),
                         symbol.getImageURL());
        return result;
   }

   // are two one-dimensional ranges (x1...x2 and y1...y2) disjoint?
   static private boolean areDisjointRanges(double x1, double x2,
                                            double y1, double y2) {
     boolean result = false;
     if ((x1 < y1 && x2 < y1 && x1 < y2 && x2 < y2) ||
         (y1 < x1 && y2 < x1 && y1 < x2 && y2 < x2))
       result = true;
     return result;
   }

   // do two rectangular regions intersect (left/right and/or
   // top/bottom can be interchanged and it still works)
   static boolean intersects(
     double left1, double top1, double right1, double bottom1,
     double left2, double top2, double right2, double bottom2) {
     boolean result = true;
     if (areDisjointRanges(left1, right1, left2, right2) ||
         areDisjointRanges(top1, bottom1, top2, bottom2))
       result = false;
     return result;
   }

  
   /* renders a single image that is part of a (possibly
    * multi-image) symbol, along with that image's annotation */
   protected void realizeOneImageOfSymbol(PlotPanel pp,
                                          GraphicsRenderingPanel grp, 
                                          AnnotationRenderingPanel arp, 
                                          Symbol symbol,
                                          Annotation annotation,
                                          boolean onY2,
                                          boolean clipPlotArea,
                                          boolean clipDecoratedChart,
                                          double xPx, double yPx,
                                          double prevXPx, double prevYPx,
                                          double nextXPx, double nextYPx,
                                          double width, double height) {


        double xMin = pp.getXMin();
        double xMax = pp.getXMax();
        double xMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
        double xMinPx = pp.xToPixel(xMin);
        double xMaxPx = pp.xToPixel(xMax);
        double xMidPx = pp.xToPixel(xMid);
      
        double symWidth = getAdjustedWidth(width, xPx,
                                          prevXPx, nextXPx,
                                          xMinPx, xMaxPx, xMidPx);
        if ((symWidth!=symWidth)) return; // x!=x is a faster isNaN
       
        double xLeft = getUpperLeftX(width, xPx,
                                     prevXPx, nextXPx,
                                     xMinPx, xMaxPx, xMidPx,
                                     pp.getXMousePlotArea());
        if ((xLeft!=xLeft)) return; // x!=x is a faster isNaN
       
        double xCenter = xLeft + symWidth/2.;
        // the data and pixel Y coordinates are flipped, hence
        // the (counter-intuitive) min/max interchange below:
        double yMin =  onY2?pp.getY2Max():pp.getYMax();
        double yMax =  onY2?pp.getY2Min():pp.getYMin();
        double yMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
        double yMinPx = pp.yToPixel(yMin,onY2);
        double yMaxPx = pp.yToPixel(yMax,onY2);
        double yMidPx = pp.yToPixel(yMid,onY2);
       
        double symHeight = getAdjustedHeight(
                                  height, yPx,
                                  prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
        if ((symHeight!=symHeight)) return; // x!=x is a faster isNaN
       
        double yTop = getUpperLeftY(height, yPx,
                                    prevYPx, nextYPx,
                                    yMinPx, yMaxPx, yMidPx,
                                    pp.getYMousePlotArea());
        if ((yTop!=yTop)) return; // x!=x is a faster isNaN
       
        double yCenter =  yTop + symHeight/2.;

        if (clipPlotArea &&
            !intersects(xMinPx, yMinPx, xMaxPx, yMaxPx,
                        xLeft, yTop, xLeft+symWidth, yTop+symHeight))
          return; // image is completely off plot area, so skip it.
        else if (clipDecoratedChart) {
           int yAxisWidth = pp.getYAxisEnsembleWidth();
           int titleThickness = pp.chartTitleThickness();
           if (!intersects(xMinPx - yAxisWidth,
                           yMinPx - titleThickness,
                           pp.getXChartSizeDecoratedQuickly()-yAxisWidth,
                           pp.getYChartSizeDecoratedQuickly()-titleThickness,
                           xLeft, yTop, xLeft+symWidth, yTop+symHeight))
              return; // image completely off decorated chart, so skip
        }
        // translate negative width, height to equivalent
        // positive values that image tags can handle
        int signWidth = 1;
        if (symWidth < 0) {
           xLeft = xLeft + symWidth;
           symWidth *= -1;
           signWidth = -1;
        }
        int signHeight = 1;
        if (symHeight < 0) {
           yTop = yTop + symHeight;
           symHeight *= -1;
           signHeight = -1;
        }

        // Positive pixel padding pushes the specified edge
        // outward from the center by the given amount, without
        // changing the location of the center the symbol.
        // Similarly, negative padding, pushes the edge inward.

        if (symWidth != 0) {
          xLeft -= pixelPadLeft;
          symWidth += pixelPadLeft + pixelPadRight;
        }
        // else, zero width, keep it that way (no padding added)
       
        if (symHeight != 0) {
          yTop -= pixelPadTop;
          symHeight += pixelPadTop + pixelPadBottom;
        }
        // else, zero height, keep it that way (no padding added)

        int borderWidth = symbol.getBorderWidth();
        // borderWidth < 0 ==> external border
        if ((symWidth > 0 && symHeight > 0) || borderWidth < 0) {
           grp.renderBorderedImage(symbol.getBackgroundColorCSS(),
                                        symbol.getBorderColorCSS(),
                                        symbol.getBorderStyle(),
                                        borderWidth,
                                        symWidth,
                                        symHeight,
                                        xLeft, yTop, symbol.getImageURL());
        }
        // if the image has an attached label, realize that
        if (annotation!=null &&
            (annotation.getText() != null ||
             annotation.getWidget() != null) &&
            annotation.getVisible()) {
           AnnotationLocation loc = annotation.getLocation();
           if (null == loc) loc = defaultAnnotationLocation();
           loc = AnnotationLocation.transform(loc, signWidth, signHeight);
          // Note: yShift follows orientation of cartesian y
          // Axis, which is 180 degrees different from pixel y
          // coordinates, hence the extra "-" below.
          //
          // signWidth, signHeight multipliers assure that shifts are
          // appropriately symetrical for bars above and below or to the
          // left or right of their baselines (only baseline bars use
          // negative symbol widths) For example, a yShift of
          // 10px would shift up for bars above the baseline, and down
          // for bars below the baseline, which is usually what you
          // want (e.g. placing labels above or below the bars).
          arp.renderAnnotation(annotation, loc,
                            xCenter+signWidth*annotation.getXShift(),
                            yCenter-signHeight*annotation.getYShift(),
                            symWidth, symHeight,
                            symbol);
        }
    
   }

     // Distance from the point (x1, y1) to the point (x2, y2)
     protected double distance(
         double x1, double y1, double x2, double y2) {
        double result = Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
        return result;
     }
     /*
      * Renders the symbol at the specified position within the plot
      * panel, by creating appropriately positioned Image and Label
      * objects within the given rendering panel.
      * <p>
      *
      * Most of the Image widgets will be replaced with drawing on the
      * rendering panel's dedicated canas Widget if an external canvas
      * capability has been bolted onto GChart, and continuous fill
      * (fillSpacing == 0) has been requested for the curve.
      *
      * <p>
      *
      * So-rendered symbols are used to represent: each point on a curve
      * (including any "filled" elements linearly interpolated between
      * successive points, such as, point-to-point connecting lines and
      * "areas under the curve") and (via special hidden system curves)
      * axes, gridlines, ticks, tick-labels, titles, footnotes, and
      * the legend key.
      * <p>
      *
      * This method is overridden for pie slice symbols and
      * the LINE symbol type.
      *
      */
     // retains coord of an area chart's "filled to" axis/baseline
     static double oppositeEdge = Double.NaN;
     void realizeSymbol(PlotPanel pp,
                        GraphicsRenderingPanel grp,
                        AnnotationRenderingPanel arp,
                        Symbol symbol,
                        Annotation annotation,
                        boolean onY2,
                        boolean clipPlotArea,
                        boolean clipDecoratedChart,
                        boolean drawMainSymbol,
                        double x, double y,
                        double prevX, double prevY,
                        double nextX, double nextY) {

       if ((x!=x) || (y!=y)) // this point undefined (isNaN)
          return;

       double xPx = pp.xToPixel(x);
       double yPx = pp.yToPixel(y, onY2);
       double prevXPx = pp.xToPixel(prevX);
       double prevYPx = pp.yToPixel(prevY, onY2);
       double nextXPx = pp.xToPixel(nextX);
       double nextYPx = pp.yToPixel(nextY, onY2);
       double spacing = symbol.getFillSpacing();
       int thickness = symbol.getFillThickness();
       GChartCanvasLite canvas = grp.getCanvas();
      
       if (0 == spacing && null != canvas && thickness > 0) { // if canvas rendered
         if (null == isHorizontallyBanded) {
           /*
            * Continuous fill, canvas available, and not explicitly
            * horizontally or vertically banded. For example, BOX_*
            * symbol types are not explicitly oriented, but VBAR_*
            * (vertically) and HBAR_* (horizontally) are: use canvas
            * to draw a straight line between points.  <p>
            *
            * Code in this branch also gets executed by the LINE
            * symbol type.
            *
            */
             int borderWidth = symbol.getBorderWidth();
             // negative (external) border widens line by 2*|borderWidth| 
             int externalLineWidth = (borderWidth >= 0) ?
                                      thickness :
                                     (thickness + 2*Math.abs(borderWidth));
             int internalLineWidth = (borderWidth >= 0) ?
                   Math.max(thickness-2*borderWidth,0) : thickness;
             String borderColor = symbol.getBorderColor();
             String backgroundColor = symbol.getBackgroundColor();
             if (externalLineWidth > 0 &&
                 ((TRANSPARENT_BORDER_COLOR != borderColor &&
                  "transparent" != borderColor) ||
                  (TRANSPARENT_BORDER_COLOR != backgroundColor &&
                  "transparent" != backgroundColor))) {
                if (prevX != prevX || prevY != prevY) {
                   // first defined point after an undefined point ==> new path
                   // (need to draw zero-thickness lines for possible line
                   // endings user may have defined by overriding beginPath)
                   canvas.beginPath();
                   canvas.moveTo(xPx - grp.x0, yPx - grp.y0);
                }
                if (nextX!=nextX || nextY!=nextY) {
                   // last defined point before undefined point ==> draw accumulated path
                  if (TRANSPARENT_BORDER_COLOR != borderColor &&
                      "transparent" != borderColor &&
                      externalLineWidth > 0) {
                      canvas.setStrokeStyle(borderColor);
                      canvas.setLineWidth(externalLineWidth);
                      canvas.stroke();
                  }
                  if (TRANSPARENT_BORDER_COLOR != backgroundColor &&
                      "transparent" != backgroundColor &&
                      internalLineWidth > 0) {
                     canvas.setLineWidth(internalLineWidth);
                     canvas.setStrokeStyle(backgroundColor);
                     canvas.stroke();
                  }
                }
                else // not at end of chain ==> add one more segment to the path
                   // (need to draw doubled points for possibly "line join"
                   // user may have defined via overriding beginPath)
                   canvas.lineTo(nextXPx - grp.x0, nextYPx - grp.y0);
             }
          // else lines are 0-width or transparent, so not rendered
          }
          else {
          /*
           * Explicitly oriented bandedness occurs only for vert or
           * horizontal bars. x,y coordinates are connected into
           * a path (as in a line chart), and then that path
           * is extended into a closed polygon by adding a
           * closing segment formed from an appropriate
           * section of an axis or baseline.
           *
           */

          /*
           * Draw area interpolated between successive bars.
           *
           * Note that the "opposite" edge could be a point on an
           * x or y axis, or on the curve's baseline, depending
           * on the kind of bar chart involved: it's the edge
           * of the bar that is furthest from the x,y point.
           *
           */
             boolean closeStrokeAndFill = false;
             if (Boolean.FALSE == isHorizontallyBanded) {
                if (prevX != prevX || prevY != prevY) {
                   // 1st point, or 1st point after a break in the line
                   oppositeEdge = getEdgeOppositeVertically(
                      pp, symbol, y, onY2);
                   canvas.beginPath();
                   canvas.moveTo(xPx - grp.x0, oppositeEdge - grp.y0);
                   canvas.lineTo(xPx - grp.x0, yPx - grp.y0);
                }
                if (nextX!=nextX || nextY!=nextY) {
                   // last point, or last point before a break in the line
                   canvas.lineTo(xPx - grp.x0, oppositeEdge - grp.y0);
                   closeStrokeAndFill = true;
                }
                else {
                   canvas.lineTo(nextXPx - grp.x0, nextYPx - grp.y0);
                }
             }
             else {

                if (prevX != prevX || prevY != prevY) {
                   // 1st point, or 1st point after a break in the line
                   oppositeEdge = getEdgeOppositeHorizontally(
                      pp, symbol, x, onY2);
                   canvas.beginPath();
                   canvas.moveTo(oppositeEdge - grp.x0, yPx - grp.y0);
                   canvas.lineTo(xPx - grp.x0, yPx - grp.y0);
                }
                if (nextX!=nextX || nextY!=nextY) {
                   // last point, or last point before a break in the line
                   canvas.lineTo(oppositeEdge - grp.x0, yPx - grp.y0);
                   closeStrokeAndFill = true;
                }
                else {
                   canvas.lineTo(nextXPx - grp.x0, nextYPx - grp.y0);
                }

             }

             if (closeStrokeAndFill) {

                 canvas.closePath();
                 int borderWidth = symbol.getBorderWidth();
             // negative (external) border requires double-wide stroke
                 int lineWidth = (borderWidth >= 0) ?
                                  borderWidth: (2*Math.abs(borderWidth));
                 String borderColor = symbol.getBorderColor();
                 String backgroundColor = symbol.getBackgroundColor();
                
   /* XXX: Simply dropping the rendering as we do below does not
    * exactly simulate the effect of transparent border/fill,
    * specifically:
    *
    * <ol>
    *   <li> Transparent internal border ==> the background
    *          fill shines through the inner half of that border
    *   <li> Transparent external border ==> Works OK
    *   <li> Transparent fill w external border ==>
    *           border extended internally to double width
    *   <li> Transparent fill w internal border ==> works OK
    * </ol> 
    *
    * GWTCanvas does not (?) provide a mechanism to "stroke
    * transparent pixels", which is what I really needed. And
    * emulating this, though possible via properly positioned
    * inner/outter regions, etc. would have required a lot of effort
    * to assure that sharply peaked angles, say, get rendered right.
    *
    */

                 // non-negative borders fill before stroking (thus
                 // stroke overwrites internal half of border)
                 if (borderWidth >= 0 && thickness > 0 &&
                     TRANSPARENT_BORDER_COLOR != backgroundColor &&
                     "transparent" != backgroundColor) {
                    canvas.setFillStyle(backgroundColor);
                    canvas.fill();
                 }

                 // stroke whenever a border is present
                 if (borderWidth != 0 &&
                    TRANSPARENT_BORDER_COLOR != borderColor &&
                     "transparent" != borderColor) {
                    canvas.setStrokeStyle(borderColor);
                    canvas.setLineWidth(lineWidth);
                    canvas.stroke();
                 }

                 // negative borders fill AFTER stroking (thus zapping
                 // the internal half of the stroked border).
                 if (borderWidth < 0 && thickness > 0 &&
                     TRANSPARENT_BORDER_COLOR != backgroundColor &&
                     "transparent" != backgroundColor) {
                     canvas.setFillStyle(backgroundColor);
                     canvas.fill();
                 }
             }
          }
       } // if (0 == spacing && null != canvas && thickness > 0) 
       else if (nextX==nextX && nextY==nextY &&  // next point defined
                thickness > 0 &&   // not a zero thickness connection
                (x!=nextX || y!=nextY) ) { // this/next point not overlayed
         if (0 == spacing) // 1px is as close as HTML-element
             spacing = 1// based filling can get to continuous
         double d = distance(xPx,yPx,nextXPx,nextYPx);
         int nChunks = (int) Math.round(d/spacing);
         if (nChunks > 1) {
            double deltaX = nextXPx - xPx;
            double deltaY = nextYPx - yPx;
            boolean dXIsLonger = deltaX*deltaX > deltaY*deltaY;
            if (dXIsLonger) {
               deltaY /= deltaX; // from now on, dy is really dy/dx
               deltaX /= nChunks;// from now on, dx is for 1 chunk
            }
            else {
               deltaX /= deltaY;  // from now on, dx is really dx/dy
               deltaY /= nChunks; // from now on, dy is for 1 chunk
            }
          // i==0 corresponds to the (to-be-drawn-last) symbol on (x,y).
            for (int i = 1; i < nChunks; i++) {
              // linearly interpolate forwards towards the next
              // point; forward interpolation (usually) lets us
              // place the "main" symbol for the original point on
              // top of these interpolated symbols, in one pass.
              double xi;
              double yi;

              // Rounding to the longer dimension first, then
              // using that pixelated position to determine other
              // dimension tends to keep points closer to being
              // on the mathematically ideal line (at the cost of
              // being less evenly spaced along that line). It's
              // not too hard to see the improved alignment on
              // GChartExample03, for example.
              if (dXIsLonger) {
                 xi = Math.round(xPx + deltaX * i);
                 yi = Math.round(yPx + deltaY*(xi - xPx));
              }
              else { // delta y is longer
                 yi = Math.round(yPx + deltaY * i);
                 xi = Math.round(xPx + deltaX*(yi - yPx));
             }             


              // interpolated symbols set width & height to
              // thickness, but are otherwise the same as main
              // symbol at (x,y)
              realizeOneImageOfSymbol(pp, grp, arp, symbol, null,
                                      onY2,
                                      clipPlotArea,
                                      clipDecoratedChart,
                                      xi, yi,
                                      prevXPx, prevYPx,
                                      nextXPx, nextYPx,
                                      thickness,
                                      thickness);
            }
         }
         // else points too close to require any "filler" elements
       }
       // the "main" symbol (the one on the (x,y) point itself) is
       // rendered last to put it on top of interpolated images; this
       // is also where any annotation on the point gets rendered.
       if (drawMainSymbol) {
          realizeOneImageOfSymbol(pp, grp, arp, symbol, annotation,
                                  onY2,
                                  clipPlotArea,
                                  clipDecoratedChart,
                                  xPx, yPx,
                                  prevXPx, prevYPx,
                                  nextXPx, nextYPx,
                                  symbol.getWidth(pp),
                                  symbol.getHeight(pp,onY2));
       }
     }



    
   } // end of class SymbolType

   /**
    ** Defines keywords <tt>INSIDE</tt>, <tt>OUTSIDE</tt>, and
    ** <tt>CENTERED</tt> that specify the location of ticks
    ** relative to their axis.
    ** <p>
    **
    ** @see Axis#setTickLocation setTickLocation
    **
    **/
   public static final class TickLocation {
      /*
       * An integer form of the tick location (-1 - OUTSIDE,
       * 0 - CENTERED, +1 - INSIDE) that facilitates
       * generating appropriate symbol types for rendering ticks
       *
       */
      int locationIndex;
      private TickLocation(int locationIndex) {
         this.locationIndex = locationIndex;
      }
     /**
      ** Indicates that ticks are located outside of the axis.
      **
      ** @see Axis#setTickLocation setTickLocation
      **/
     public static final TickLocation OUTSIDE = new TickLocation(-1);
     /**
      ** Indicates that ticks are centered on the axis.
      **
      ** @see Axis#setTickLocation setTickLocation
      **/
     public static final TickLocation CENTERED = new TickLocation(0);
     /**
      ** Indicates that ticks are located inside of the axis.
      **
      ** @see Axis#setTickLocation setTickLocation
      **/
     public static final TickLocation INSIDE = new TickLocation(1);

     // symbol type representing ticks on x axes at given position
     // axisPosition of  -1 is x-axis, +1 is x2-axis.
     //
     // (symbols representing ticks depend on the axis they are on)
     SymbolType getXAxisSymbolType(int axisPosition) {
        final SymbolType[] symbolMap =
          {SymbolType.BOX_NORTH, SymbolType.BOX_CENTER, SymbolType.BOX_SOUTH};
        SymbolType result =
           symbolMap[axisPosition*locationIndex+1];
        return result;               
      }
     // symbol type representing ticks on y axes at given position
     // axisPosition of  -1 is y-axis, +1 is y2-axis
     //
     // (symbols representing ticks depend on the axis they are on)
     SymbolType getYAxisSymbolType(int axisPosition) {
        final SymbolType[] symbolMap =
          {SymbolType.BOX_EAST, SymbolType.BOX_CENTER, SymbolType.BOX_WEST};
        SymbolType result =
           symbolMap[axisPosition*locationIndex+1];
        return result;               
     }
    
   } // class TickLocation
                   
  /**
   * Defines how the <tt>update</tt> method updates the touched
   * point, that is, the point the user is considered to be
   * hovered over.
   *
   * @see #update(TouchedPointUpdateOption) update
   *
   */
           
  public static final class TouchedPointUpdateOption {
     private TouchedPointUpdateOption() {super();}

     /**
      * When this option is passed to the update method, any
      * touched point is cleared as a consequence of the update.
      * <p>
      *
      * This option can be used when you want to "start fresh"
      * with regards to hover feedback after an update, and want
      * to assure that only explicit user-generated mouse move
      * actions (rather than objects moving <i>underneath</i> a
      * fixed-position mouse cursor) can trigger hover feedback.
      *
      * @see #update update
      * @see #TOUCHED_POINT_LOCKED TOUCHED_POINT_LOCKED
      * @see #TOUCHED_POINT_UPDATED TOUCHED_POINT_UPDATED
      *
      */
     public static final TouchedPointUpdateOption TOUCHED_POINT_CLEARED =
        new TouchedPointUpdateOption();
    
     /**
      * When this option is passed to the update method, any
      * previously touched point is locked in (remains unchanged).
      * <p>
      *
      * For example, if the mouse is over a certain point before
      * the update, and that point moves away from the mouse
      * (without the mouse moving otherwise) as a consequence of
      * the update, the hover feedback remains "locked in" to the
      * original point, even though the mouse is no longer on top
      * of that point.
      * <p>
      *
      * This option is useful for hover widgets that modify the
      * position, size, symbol of points/curves, and do not want the
      * selected point/curve (and popup hover widget) to change as
      * a consequence of such changes.
      * <p>
      *
      * <i>Note:</i> If the currently touched point or the curve
      * containing it is deleted, GChart sets the touched point
      * reference to <tt>null</tt>. In that case, this option and
      * <tt>TOUCHED_POINT_CLEARED</tt> behave the same way.
      *
      *
      * @see #update update
      * @see #TOUCHED_POINT_CLEARED TOUCHED_POINT_CLEARED
      * @see #TOUCHED_POINT_UPDATED TOUCHED_POINT_UPDATED
      *
      */
     public static final TouchedPointUpdateOption TOUCHED_POINT_LOCKED =
       new TouchedPointUpdateOption();
     /**
      * When this option is passed to the update method, the
      * touched point is updated so that it reflects whatever point
      * is underneath the mouse cursor after the update
      * completes.
      * <p>
      *
      * For example, if the mouse is not hovering over any point
      * before the update, but the update repositions one of the
      * points so that it is now underneath the mouse cursor,
      * the hover feedback for that point will be displayed.
      * Similarly, if the update moves a point away from the
      * mouse cursor, previously displayed hover feedback will
      * be eliminated.
      * <p>
      *
      * @see #update update
      * @see #TOUCHED_POINT_CLEARED TOUCHED_POINT_CLEARED
      * @see #TOUCHED_POINT_LOCKED TOUCHED_POINT_LOCKED
      *
      */
     public static final TouchedPointUpdateOption TOUCHED_POINT_UPDATED =
       new TouchedPointUpdateOption();
  }

           
  /** The x-axis of a GChart.
   *
   * @see GChart#getXAxis getXAxis
   */
 
  public class XAxis extends Axis {
     XAxis() {
        super();
        isHorizontalAxis = true;
        ticksId = XTICKS_ID;
        gridlinesId = XGRIDLINES_ID;
        axisId = XAXIS_ID;
        axisPosition = -1;
        setTickLocation(DEFAULT_TICK_LOCATION);
        setTickThickness(DEFAULT_TICK_THICKNESS);
        setTickLength(DEFAULT_TICK_LENGTH);
     }
    
     public double clientToModel(int clientCoordinate) {
        int xPixel =
                    Window.getScrollLeft() + clientCoordinate -
                    plotPanel.getAbsoluteLeft();
        double result = plotPanel.xChartPixelToX(xPixel);
        return result;
     }
     public int getAxisLabelThickness() {
        final int EXTRA_CHARHEIGHT = 2; // 1-char space above & below
        final int DEF_CHARHEIGHT = 1;
        int result = 0;
        if (null == getAxisLabel())
           result = 0;
        else if (GChart.NAI != axisLabelThickness)
           result = axisLabelThickness;
        else if (getAxisLabel() instanceof HasHTML) {
           int charHeight = htmlHeight(
              ((HasHTML) (getAxisLabel())).getHTML());
           result = (int) Math.round((EXTRA_CHARHEIGHT+charHeight) *
                      getTickLabelFontSize() *
                     TICK_CHARHEIGHT_TO_FONTSIZE_LOWERBOUND);
        }
        else
           result = (int) Math.round(
                     (EXTRA_CHARHEIGHT + DEF_CHARHEIGHT) *
                     getTickLabelFontSize() *
                     TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
        return result;
     }
     public double getDataMax() {
       double result = -Double.MAX_VALUE;
       int nCurves = getNCurves();
       for (int i = 0; i < nCurves; i++) {
          Curve c = getSystemCurve(i);
          if (!c.isVisible()) continue;
          int nPoints = c.getNPoints();
          for (int j = 0; j < nPoints; j++) {
             result = maxIgnoreNaNAndMaxValue(result,
                                              c.getPoint(j).getX());
          }
       }
       return result == -Double.MAX_VALUE ? Double.NaN : result;
     }
     public double getDataMin() {
       double result = Double.MAX_VALUE;
       int nCurves = getNCurves();
       for (int i = 0; i < nCurves; i++) {
          Curve c = getSystemCurve(i);
          if (!c.isVisible()) continue;
          int nPoints = c.getNPoints();
          for (int j = 0; j < nPoints; j++) {
             result = minIgnoreNaNAndMaxValue(result,
                                              c.getPoint(j).getX());
          }
       }
       return result == Double.MAX_VALUE ? Double.NaN : result;
     }

     public double getMouseCoordinate() {
       double result = plotPanel.xChartPixelToX(plotPanel.getXMouse());
       return result;       
     }
    
     public int getTickLabelThickness(boolean needsPopulation) { // overrides base class
       int result;
       if (tickLabelThickness != GChart.NAI)
         result = tickLabelThickness;
       else if (getTickCount() == 0)
          result = 0;
       else {
       // XXX: single line labels assumed; these have height
       // almost equal to the fontSize in pixels. Not really
       // right, since multi-line HTML can now be used, but user
       // can explicitly change tick label thickness with
       // multi-line, HTML based, ticks, so OK for now.  
          result = (int) Math.round(
                   TICK_CHARHEIGHT_TO_FONTSIZE_LOWERBOUND *
                   tickLabelFontSize);
       }
       return result;
     }
    
     public double modelToClient(double modelCoordinate) {
        double xPixel = plotPanel.xToChartPixel(modelCoordinate);
        double result = plotPanel.getAbsoluteLeft()
                        - Window.getScrollLeft() + xPixel;
        return result;
     }
     public double modelToPixel(double modelCoordinate) {
        double result = plotPanel.xToChartPixel(modelCoordinate);
        return result;
     }
     public double modelToPlotAreaPixel(double modelCoordinate) {
        double result = plotPanel.xToPixel(modelCoordinate);
        return result;
     }
     public double pixelToModel(int pixelCoordinate) {
        double result = plotPanel.xChartPixelToX(pixelCoordinate);
        return result;
     }
     public double plotAreaPixelToModel(int pixelCoordinate) {
        double result = plotPanel.xPixelToX(pixelCoordinate);
        return result;
     }
     public void setTickLength(int tickLength) {
       chartDecorationsChanged = true;
       this.tickLength = tickLength;
       getSystemCurve(ticksId).getSymbol().setHeight
          getActualTickLength());
     }
     public void setTickThickness(int tickThickness) {
       this.tickThickness = tickThickness;
       getSystemCurve(ticksId).getSymbol().setWidth(tickThickness);
     }

          
  } // end of class XAxis  
  /** The right, or "y2", axis of a GChart.
   *
   * @see GChart#getY2Axis getY2Axis
   */

  public class Y2Axis extends Axis {
     Y2Axis() {
        super();
        isHorizontalAxis = false;
        ticksId = Y2TICKS_ID;
        gridlinesId = Y2GRIDLINES_ID;
        axisId = Y2AXIS_ID;
        axisPosition = 1;
        setTickLocation(DEFAULT_TICK_LOCATION);
        setTickThickness(DEFAULT_TICK_THICKNESS);
        setTickLength(DEFAULT_TICK_LENGTH);
     }
     public double clientToModel(int clientCoordinate) {
        int yPixel =
                    Window.getScrollTop() + clientCoordinate -
                    plotPanel.getAbsoluteTop();
        double result = plotPanel.yChartPixelToY2(yPixel);
        return result;
     }
     public double getDataMax() {
       double result = -Double.MAX_VALUE;
       int nCurves = getNCurves();
       for (int i = 0; i < nCurves; i++) {
          Curve c = getSystemCurve(i);
          if (!c.isVisible()) continue;
          if (c.getYAxis() == Y2_AXIS) {
            int nPoints = c.getNPoints();
            for (int j = 0; j < nPoints; j++) {
               result = maxIgnoreNaNAndMaxValue(result,
                                                c.getPoint(j).getY());
            }
          }
       }
       return result == -Double.MAX_VALUE ? Double.NaN : result;
     }
     public double getDataMin() {
       double result = Double.MAX_VALUE;
       int nCurves = getNCurves();
       for (int i = 0; i < nCurves; i++) {
          Curve c = getSystemCurve(i);
          if (!c.isVisible()) continue;
          if (c.getYAxis() == Y2_AXIS) {
            int nPoints = c.getNPoints();
             for (int j = 0; j < nPoints; j++) {
                result = minIgnoreNaNAndMaxValue(result,
                                                 c.getPoint(j).getY());
            }
          }
       }
       return result == Double.MAX_VALUE ? Double.NaN : result;
     }

     public double getMouseCoordinate() {
       double result = plotPanel.yChartPixelToY2(plotPanel.getYMouse());
       return result;       
     }


     public double modelToClient(double modelCoordinate) {
        double yPixel = plotPanel.yToChartPixel(modelCoordinate, true);
        double result = plotPanel.getAbsoluteTop()
                        - Window.getScrollTop() + yPixel;
        return result;
     }
     public double modelToPixel(double modelCoordinate) {
        double result = plotPanel.yToChartPixel(modelCoordinate, true);
        return result;
     }
     public double modelToPlotAreaPixel(double modelCoordinate) {
        double result = plotPanel.yToPixel(modelCoordinate, true);
        return result;
     }
     public double pixelToModel(int pixelCoordinate) {
        double result = plotPanel.yChartPixelToY2(pixelCoordinate);
        return result;
     }
     public double plotAreaPixelToModel(int pixelCoordinate) {
        double result = plotPanel.yPixelToY2(pixelCoordinate);
        return result;
     }
     public void setTickLength(int tickLength) {
       chartDecorationsChanged = true;
       this.tickLength = tickLength;
       getSystemCurve(ticksId).getSymbol().setWidth(
          getActualTickLength());
     }
     public void setTickThickness(int tickThickness) {
       this.tickThickness = tickThickness;
       getSystemCurve(ticksId).getSymbol().setHeight(tickThickness);
     }
    
  } // end of class Y2Axis
  /** The left y-axis of a GChart.
   *
   * @see GChart#getYAxis getYAxis
   *
   */

  public class YAxis extends Axis {
     YAxis() {
        super();
        isHorizontalAxis = false;
        ticksId = YTICKS_ID;
        gridlinesId = YGRIDLINES_ID;
        axisId = YAXIS_ID;
        axisPosition = -1;
        setTickLocation(DEFAULT_TICK_LOCATION);
        setTickThickness(DEFAULT_TICK_THICKNESS);
        setTickLength(DEFAULT_TICK_LENGTH);
     }
     public double clientToModel(int clientCoordinate) {
        int yPixel =
                    Window.getScrollTop() + clientCoordinate -
                    plotPanel.getAbsoluteTop();
        double result = plotPanel.yChartPixelToY(yPixel);
        return result;
     }
     public double getDataMax() {
       double result = -Double.MAX_VALUE;
       int nCurves = getNCurves();
       for (int i = 0; i < nCurves; i++) {
          Curve c = getSystemCurve(i);
          if (!c.isVisible()) continue;
          if (c.getYAxis() == Y_AXIS) {
            int nPoints = c.getNPoints();
            for (int j = 0; j < nPoints; j++) {
               result = maxIgnoreNaNAndMaxValue(result,
                                                c.getPoint(j).getY());
            }
          }
       }
       return result == -Double.MAX_VALUE ? Double.NaN : result;
     }
     public double getDataMin() {
       double result = Double.MAX_VALUE;
       int nCurves = getNCurves();
       for (int i = 0; i < nCurves; i++) {
          Curve c = getSystemCurve(i);
          if (!c.isVisible()) continue;
          if (c.getYAxis() == Y_AXIS) {
            int nPoints = c.getNPoints();
            for (int j = 0; j < nPoints; j++) {
               result = minIgnoreNaNAndMaxValue(result,
                                                c.getPoint(j).getY());
            }
          }
       }
       return result == Double.MAX_VALUE ? Double.NaN : result;
     }

     public double getMouseCoordinate() {
       double result = plotPanel.yChartPixelToY(plotPanel.getYMouse());
       return result;       
     }


     public double modelToClient(double modelCoordinate) {
        double yPixel = plotPanel.yToChartPixel(modelCoordinate, false);
        double result = plotPanel.getAbsoluteTop()
                        - Window.getScrollTop() + yPixel;
        return result;
     }
     public double modelToPixel(double modelCoordinate) {
        double result = plotPanel.yToChartPixel(modelCoordinate, false);
        return result;
     }
     public double modelToPlotAreaPixel(double modelCoordinate) {
        double result = plotPanel.yToPixel(modelCoordinate, false);
        return result;
     }
     public double pixelToModel(int pixelCoordinate) {
        double result = plotPanel.yChartPixelToY(pixelCoordinate);
        return result;
     }
     public double plotAreaPixelToModel(int pixelCoordinate) {
        double result = plotPanel.yPixelToY(pixelCoordinate);
        return result;
     }
     public void setTickLength(int tickLength) {
       chartDecorationsChanged = true;
       this.tickLength = tickLength;
       getSystemCurve(ticksId).getSymbol().setWidth(
          getActualTickLength());
     }
     public void setTickThickness(int tickThickness) {
       this.tickThickness = tickThickness;
       getSystemCurve(ticksId).getSymbol().setHeight(tickThickness);
     }

  } // end of class YAxis

  /*
   * Allows precise alignment of a text label before the
   * exact size of that label is known, by enclosing it
   * within a 1x1 grid.
   * <p>
   *
   * The external grid must be larger than the label or requested grid
   * alignment won't be realized. But larger that required containing
   * grids occlude mouse events from nearby elements. The
   * NonoccludingReusableAlignedLabel subclasses this class to solve
   * this problem.
   *
   *
   */
 
  private static class AlignedLabel extends Grid {
    AlignedLabel() {
      super(1,1);
      getCellFormatter().setWordWrap(0,0,false);
      setCellPadding(0);
      setCellSpacing(0);
      setBorderWidth(0);
    }   
  }

   
  /*
   * This class' sole purpose is to work around a FF 2
   * performance limitation: chart update times increase as
   * O(N^2) after the number of direct ancestor child widgets in
   * a single AbsolutePanel exceeds around 500-1000, AND the
   * chart is updated in more than one browser-displayed stage
   * (e.g, via a series of incremental updates that successively
   * add more curve data, for purposes of user feedback). By
   * contrast, IE7 times grow as O(N) even if 3,000 child widgets
   * are added to a previously displayed chart (e.g. by adding a
   * bar curve with 3,000 bars on it).
   *
   * <p>
   *
   * For solid-fill line chart support (LINE SymbolType
   * introduced in 2.2), thousands of widgets are often needed
   * and so these O(N^2) FF 2 times were just too slow.
   *
   * <p>
   *
   * Some kind of fixed hash table inside of FF 2 divs could
   * explain this switch from O(N) to O(N^2) performance.
   * <p>
   *
   * Approach is to split up the large AbsolutePanel into a
   * series of child panels, each of which contains a number of
   * elements within the range where FF 2 updates are O(N).
   * <p>
   *
   * Tried to keep it light-weight so IE7 isn't penalized too
   * much for having to workaround this FF 2 limitation.
   *
   */

  static class PartitionedAbsolutePanel extends Composite {
/* Max number of widgets in each panel; chose a value as large as
   possible while remaining within the empirically
   determined FF 2 O(N) range. Here's the data (from a 3,000 element
   test based on the sin curve of the 2.1 live demo
   called GChartExample15c.java) upon which this choice was based:
<p>

<pre>

Size    FF2    IE7
        (sec)  (sec)
  1     ~14     16
  2      11     13
32      9      11  
64      9      12
128      9      11
256      9      11
512      10     11
1024     15     11
2048     27     11
4096*    61     12

</pre>
<p>

The two largest sizes are good approximations of FF2 times we got
informally before the switch to partitioned AbsolutePanels with
charts with the corresponding number of elements (2000 or 3000).
The overhead of the partitioning itself is very low, as shown by
the modest time increase even when each element is placed into
its own sub-panel. Tests with a < 256 element chart suggested at
most a couple of ms of time increases due to the introduction of
partitioning.

<p>

I kind of expected to see >61 second times with a sub-panel size
of 1, since the parent panel still has >3,000 elements in this
case. Whatever the cause of the performance logjam (presumably in
the FF2 heap somewhere?)  simply the fact that you introduce a
parent AbsolutePanel that holds various child AbslolutePanels
that hold the actual image widgets appears to work around most of
the problem. If such a useless change makes FF2 materially
faster, that seems like a performance bug in FF2 to me.<p>

The root FF cause is apparently NOT image cache related, though,
since turning off the image cache via about:config didn't change
times for the last row of the table above.

*/
    
/* Strange, reorganized DOM layout in GChart v2.5 and now cannot
* reproduce these results in FF2 (partitioning gave just a modest 1
* second boost in a 15 second, 4,000 point test). So, I was about to
* drop back to a simple AbsolutePanel. BUT, in FF3, now, the 4,000
* point test crashes unless I use PartitionedAbsolutePanel! I guess
* Firefox 3 has problems with divs that have 4,000 elements, or at
* least 4,000 image elements, on them, which the partioning fixes.
*
*/
    
    final int WIDGETS_PER_PANEL = 256;
    private AbsolutePanel root = new AbsolutePanel();
    private AbsolutePanel subPanel = null; // "selected" subPanel
    private int iSubPanel = -1;    // index of "selected" subPanel
    private int nWidgets = 0;     // total # over all subPanels
  
    PartitionedAbsolutePanel() {
       super();
       initWidget(root);
    }

    /* resets the partitioned panel to it's initial state */
    public void clear() {
       root.clear();
       subPanel = null;
       iSubPanel = -1;
       nWidgets = 0;
    }
   
    public int getWidgetCount() {
       return nWidgets;
    }

    // makes the subpanel containing the widget the selected one.
    private void selectSubPanel(int iWidget) {
       if (iSubPanel != iWidget/WIDGETS_PER_PANEL) {
          iSubPanel = iWidget/WIDGETS_PER_PANEL;
          subPanel = (AbsolutePanel) root.getWidget(iSubPanel);
       }
    }
      
    // adds a widget to end of this partioned absolute panel
    public void add(Widget w) {
       if (nWidgets % WIDGETS_PER_PANEL == 0) {
          // last panel is full, time to add a new one
          subPanel = new AbsolutePanel();
          // Panel sits in upper left corner. Does nothing, can't
          // be seen. It's just a holder for other widgets.
          GChart.setOverflow(subPanel, "visible");
          subPanel.setPixelSize(0,0);
          root.add(subPanel, 0, 0);
       }
       selectSubPanel(nWidgets);
       subPanel.add(w);
       nWidgets++;
    }

    // returns widget at given index
    public Widget getWidget(int iWidget) {
       if (iWidget < 0 || iWidget >= nWidgets)
             throw new IllegalArgumentException(
"Invalid widget index: " + iWidget +
". Valid range is: 0..." + (nWidgets-1));

       selectSubPanel(iWidget);
       Widget result = subPanel.getWidget(
                          iWidget % WIDGETS_PER_PANEL);
       return result;
    }
 
    // Remove very last widget from panel.
    public boolean remove(int iWidget) {
       if (iWidget != nWidgets-1)
          throw new IllegalArgumentException(
"iWidgets arg = " + iWidget + " nWidgets-1 (" + (nWidgets-1)+") is required.");

       selectSubPanel(iWidget);
       boolean result = subPanel.remove(iWidget % WIDGETS_PER_PANEL);
       if (iWidget % WIDGETS_PER_PANEL == 0) {
       // if deleted widget is last widget overall, and first on
       // the selected panel, selected panel will now be empty.
         root.remove(subPanel);
         iSubPanel = -1;    // next selectSubPanel will reset these
         subPanel = null;
       }
       nWidgets--;
       return result;
    }

   
   
     // To assure that w is on the selected subPanel, this method
     // must only be passed a widget that is in the currently
     // selected subPanel. This can be assured by passing in a
     // widget that was just added via add(), or just retrieved
     // via getWidget (otherwise, an exception will be thrown).
     
    public void setWidgetPosition(Widget w, int left, int top) {
       subPanel.setWidgetPosition(w, left, top);
    }
   
  } // end of class PartitionedAbsolutePanel


  static class Rectangle { // a (pixel graphics coords) rectangle
     double x;  // x, y at upper left corner of rectangle
     double y;
     double width;  // distance from x to right edge
     double height; // distance from y to bottom edge
  }
/*
  * AbsolutePanel that allows annotations it contains to be easily
  * reused, for increased efficiency.
  *
  */     
  class AnnotationRenderingPanel extends PartitionedAbsolutePanel {
     int labelIndex = 0;                // to-be-added-next label index
     private int lastVisibleLabel = -1; // just before 1st valid index
     /*
      * Returns the inner grid of the first reusuable, non-occluding
      * aligned label in this rendering panel.
      *
      */
     AlignedLabel getFirstInnerAlignedLabel() {
        AlignedLabel result = null;
        if (labelIndex > 0) {
           NonoccludingReusuableAlignedLabel parent =
              (NonoccludingReusuableAlignedLabel) getWidget(0);
           result = parent.getInnerGrid();
        }
        return result;
     }

     /**
      * Provides support for reusing certain property specifications
      * that are likely to be the same, given how aligned labels in a
      * GChart get reused, and given certain assumptions about which
      * properties of the labels are most likely to remain unchanged
      * between updates.
      * <p>
      *
      * Also applies a hidden outter grid technique to allow proper
      * alignment with labels of unknown size without occluding
      * mouse events of nearby elements.
      * <p>
      *
      * Very similar in intent to the ReusableImage, see that
      * class' header comment for more info.
      *
      */
     class NonoccludingReusuableAlignedLabel extends AlignedLabel {
        int fontSize = GChart.NAI;
        String fontStyle = USE_CSS;
        String fontWeight = USE_CSS;
        String fontColor = USE_CSS;
        HasHorizontalAlignment.HorizontalAlignmentConstant hAlign;
        HasVerticalAlignment.VerticalAlignmentConstant vAlign;
        String labelText = null;
        boolean isHTML = false;
        Widget labelWidget = null;
        final AlignedLabel innerGrid = new AlignedLabel();

        AlignedLabel getInnerGrid() {
           return innerGrid;
        }

        NonoccludingReusuableAlignedLabel() {
           super();
           setWidget(0, 0, innerGrid);
           /*
            * The basic technique being used in the lines below is
            * illustrated by this excerpt from p 317 of "CSS, The
            * Definitive Guide" by Eric A. Meyer: <p>
            *
            * <pre>
            *   p.clear {visibility: hidden;}
            *   p.clear em {visibility: visible;}
            * </pre>
            * <p>
            *
            * In the above example, emphasized (italic) text is
            * positioned exactly as it would have been in the
            * paragraph had the normal text been visible, except that
            * the normal text <i>isn't</i> visible. And, unlike
            * transparent text, the invisible text also won't capture
            * mouse events (essential for our aligned labels)
            *
            * <p>
            *
            * With GChart's aligned label, we want the outter Grid (HTML
            * table) to be "not there" as far as visibility and
            * mouseovers, but still impact centering and
            * other alignment of the stuff in the visible, inner Grid.
            *
            * <p>
            *
            * Note that we cannot just make the Grid color transparent
            * (tried that first) because in that case the oversized
            * outter Grid required for alignment will still grab the
            * mouseover events inappropriately (wrong hovertext
            * problem).
            *
            * <p>
            *
            * Not certain but, apparently, IE6 requires that, if
            * you want to apply this trick when the outer element
            * is a table you must use another table as the
            * inner element. At least, the div inner element approach
            * I used at first (that basically worked in Firefox), made
            * both parent and child invisible in IE6.
            * <p>
            *
            * In summary:
            *
            * The upside: alignment without inappropriate event occlusion
            * The downside: the extra Grid element saps performance
            *
            */

           DOM.setStyleAttribute(getElement(),
                                 "visibility","hidden");
           DOM.setStyleAttribute(innerGrid.getElement(),
                                 "visibility", "visible");
        }

        /*
         * Sets properties only if they have changed; to replace
         * expensive DOM calls with cheap inequality tests.
         *
         * TODO: Investigate moving properties that are guaranteed to be
         * the same across all elements in the image panel (backgroundColor,
         * borderColor, borderWidth, borderStyle, image url, possibly more)
         * into a single style for the image panel as a whole, and just add
         * that styleName to each image, and set the properties once, in
         * the style. If this worked, it would save both time and space
         * (style would be internal and not intended for direct developer
         * access, since in some charts canvas, not styles, would be
         * responsible for these curve properties), and the same approach
         * could be applied to the labelPanel.
         *
         */

        void setReusableProperties(
                                   int fontSize,
                                   String fontStyle,
                                   String fontWeight,
                                   String fontColor,
                                   HasHorizontalAlignment.HorizontalAlignmentConstant hAlign,
                                   HasVerticalAlignment.VerticalAlignmentConstant vAlign,
                                   String labelText,
                                   boolean isHTML,
                                   Widget labelWidget) {

           if (this.fontSize != fontSize) {
              DOM.setIntStyleAttribute(innerGrid.getElement(), "fontSize", fontSize);
              this.fontSize = fontSize;
           }
           if (this.fontStyle != fontStyle) {
              DOM.setStyleAttribute(innerGrid.getElement(), "fontStyle", fontStyle);
              this.fontStyle = fontStyle;
           }
           if (this.fontWeight != fontWeight) {
              DOM.setStyleAttribute(innerGrid.getElement(), "fontWeight", fontWeight);
              this.fontWeight = fontWeight;
           }
           if (this.fontColor != fontColor) {
              DOM.setStyleAttribute(innerGrid.getElement(),"color", fontColor);
              this.fontColor = fontColor;
           }
           if (this.hAlign != hAlign) {
              getCellFormatter().setHorizontalAlignment(0,0,hAlign);
              // without this, only IE6-quirks doesn't quite align right:
              innerGrid.getCellFormatter().setHorizontalAlignment(0,0,hAlign);
              this.hAlign = hAlign;
           }
           if (this.vAlign != vAlign) {
              getCellFormatter().setVerticalAlignment(0,0,vAlign);
              // without this, only IE6-quirks doesn't quite align right:
              innerGrid.getCellFormatter().setVerticalAlignment(0,0,vAlign);
              this.vAlign = vAlign;
           }

           if (null != labelWidget) {
              if (this.labelWidget != labelWidget) {
                 innerGrid.setWidget(0,0,labelWidget);
                 this.labelWidget = labelWidget;
                 this.labelText = null;
              }
           }
           else if (this.labelText != labelText || this.isHTML != isHTML) {
              if (null == labelText || "" == labelText)
                 innerGrid.setText(0,0,"");
              else if (!isHTML) {
                 innerGrid.setText(0,0,labelText);
              }
              else {
                 innerGrid.setHTML(0, 0, labelText);
              }
              this.isHTML = isHTML;
              this.labelText = labelText;
              this.labelWidget = null;
           }
        }
     } // end of class NonoccludingReusuableAlignedLabel

     AnnotationRenderingPanel() {
        super();
/*
* Because of event-occlusion that can occur on all browsers but IE,
* annotation panels MUST by 0-sized/overflow:visible otherwise they
* will short-circuit event processing needed for widget annotations
* added by developer. Graphics rendering panels don't have this
* constraint and thus can be clipped to the plot area.
*
*/
        GChart.setOverflow(this, "visible");
        this.setPixelSize(0,0);
     }

     void setLabelPosition(NonoccludingReusuableAlignedLabel lbl, int x, int y) {
// workaround problem with special meaning of (-1,-1) to
// setWidgetPosition (makes position off by one pixel).
        if (x == -1 && y == -1) x = 0;
        setWidgetPosition(lbl, x, y);
     }

     void beginRendering() {      
        labelIndex = 0;
    

     void endRendering() {
        // hide or remove labels no longer being used
        for (int iLabel = optimizeForMemory ?
                          (getWidgetCount()-1) :
                          lastVisibleLabel;
             iLabel >= labelIndex;
             iLabel--) {
           Widget w = getWidget(iLabel);
           if (optimizeForMemory)
              remove(iLabel);
           else
              w.setVisible(false);
        }
        lastVisibleLabel = labelIndex-1;
     }

     /*
      * Creates (or reveals), and configures, an aligned label. Works
      * very similarly to addOrRevealImage.
      *
      */
     NonoccludingReusuableAlignedLabel getNextOrNewAlignedLabel(
        int fontSize,
        String fontStyle,
        String fontWeight,
        String fontColor,
        HasHorizontalAlignment.HorizontalAlignmentConstant hAlign,
        HasVerticalAlignment.VerticalAlignmentConstant vAlign,
        String labelText,
        boolean isHTML,
        Widget labelWidget) {
        NonoccludingReusuableAlignedLabel result;
        if (labelIndex < getWidgetCount()) {      
           result = (NonoccludingReusuableAlignedLabel)
                    getWidget(labelIndex);
           if (null != result.labelWidget &&
               labelWidget == result.labelWidget) {
/*
* DOM element actually stored in the label's Grid-cell, and what
* the label "thinks" is stored there, could be inconsistent if,
* for example, the same label widget reference was used to
* define two different points' annotations. In that case, we
* need to clear the widget reference that the label stores
* thus making it consistent with what is really in the DOM.
* <p>
*
* This code was added to fix the bug reproduced by
* TestGChart53.java. See that test for more info.
*
*
*/
              Element e = labelWidget.getElement();
              if (null == e ||
                  (e.getParentElement() !=
                  result.innerGrid.getCellFormatter().getElement(0,0)))
// the widget' DOM parent isn't label's grid-cell (it was moved)
                 result.labelWidget = null;
           }
              
           if (labelIndex > lastVisibleLabel)
              result.setVisible(true);
        }
        else {
           result = new NonoccludingReusuableAlignedLabel();
           add(result);
        }
        result.setReusableProperties(fontSize, fontStyle, fontWeight,
                                     fontColor, hAlign, vAlign, labelText, isHTML, labelWidget);

        if (lastVisibleLabel < labelIndex)
           lastVisibleLabel = labelIndex;
        labelIndex++;
        return result;
     }

     protected void renderAnnotation(Annotation annotation,
                                     AnnotationLocation loc,
                                     double xCenter,
                                     double yCenter,
                                     double symWidth,
                                     double symHeight,
                                     Symbol symbol) {

        int widthUpperBound = annotation.getWidthUpperBound();
        int upLeftX = loc.getUpperLeftX(xCenter,
                         widthUpperBound, Math.abs(symWidth));
        int heightUpperBound = annotation.getHeightUpperBound();
        int upLeftY = loc.getUpperLeftY(yCenter,
                            heightUpperBound, Math.abs(symHeight));


        NonoccludingReusuableAlignedLabel alignedLabel =
           getNextOrNewAlignedLabel(
                                    annotation.getFontSize(),
                                    annotation.getFontStyle(),
                                    annotation.getFontWeight(),
                                    annotation.getFontColor(),
                                    loc.getHorizontalAlignment(),
                                    loc.getVerticalAlignment(),
                                    annotation.getText(), annotation.isHTML(),
                                    annotation.getWidget());
        // If positioning by top or left edges, explicit sizing isn't needed
        // (makes the bounding box tighter, which, for reasons unknown, makes
        // rendering around 10% faster on some browsers and usage scenarios). 
        if (loc.getHorizontalAlignment() !=
            HasHorizontalAlignment.ALIGN_LEFT)
           alignedLabel.setWidth(widthUpperBound + "px");
        else
           alignedLabel.setWidth("");

        if (loc.getVerticalAlignment() !=
            HasVerticalAlignment.ALIGN_TOP)
           alignedLabel.setHeight(heightUpperBound + "px");
        else
           alignedLabel.setHeight("");

        setLabelPosition(alignedLabel, upLeftX, upLeftY);
     }


  } // end of class AnnotationRenderingPanel
 
   /*
    * A rendering panel contains subpanels
    * for Image-element based graphics rendering, canvas-based graphics
    * rendering, and Grid-based compass-aligned label rendering.  <p>
    *
    * Each rendering panel is joined-at-the-hip with, and provides the
    * in-the-browser-realization of, a single corresponding GChart curve
    * The one exception to this rule are the system curves used
    * internally for rendering chart decorations, which, for reasons of
    * efficiency, all share the same rendering panel.  <p>
    *
    * Rather than clearing the widgets contained in the rendering panel
    * and recreating and adding them back as needed (which is expensive)
    * the rendering panel can make widgets it employs for these purposes
    * invisible when not in use and visible again when they are needed
    * (which is usually at least twice as fast).  <p>
    *
    * <p> In principle, this is less memory efficient, but in
    * practice, due to the fact that there is less likelyhood of
    * fragmentation with reuse than with relying of the garbage
    * collector, it could even be more memory efficient.
    *
    * <p>
    *
    * When a canvas factory is specified by the developer, the panel
    * will include a single canvas widget, where most of the graphical
    * elements associated with the curve that uses this rendering panel
    * will be drawn.  GChart's canvas support is not yet up to the task
    * of rendering everything--it renders only those aspects of the
    * chart where canvas rendering provides the biggest quality/speed
    * advantages.  So, even if a canvas factory has been provided, many
    * aspects of a curve (e.g. the rectangles in bar charts) will still
    * be rendered with Image elements.
    *
    */
  class GraphicsRenderingPanel extends AbsolutePanel {
     private GChartCanvasLite canvas = null;
     int x0 = 0// origin, in pixel coords, of upper left..
     int y0 = 0//   corner of rendering canvas widget
     int canvasWidth = 0; // width of last used rendering canvas
     int canvasHeight = 0; // height of last used rendering canvas
     private AbsolutePanel canvasPanel = new AbsolutePanel();
     private PartitionedAbsolutePanel imagePanel = new PartitionedAbsolutePanel();
     int imageIndex = 0;
     // helps minimize calls to setVisible (which can be expensive)
     private int lastVisibleImage = -1;
     // Add a canvas, if needed
     void maybeAddCanvas() {
        if (null != canvasFactory && null == canvas) {
           canvas = canvasFactory.create();
           if (null != canvas) {
              if (canvas instanceof Widget) {
/*
* The next line is only needed for IE; it is needed to work-around a
* GWTCanvas bug that improperly shifts the x-placement of rendered
* graphics when a GChart is placed into a non-left-aligned Grid cell
* (GChart uses Grid Widgets to implement its annotations feature, so a
* GChart placed as an annotation on another GChart, as would occur with
* an inset or popup chart, will end up within an aligned Grid).
* <p>
*
* See also TestGChart46.java, which reproduces the GWTCanvas bug.
*
*/
                 DOM.setElementAttribute(((Widget) canvas).getElement(),
                                         "align", "left");
                 canvasPanel.add((Widget) canvas, 0, 0);
              }
              else  
                throw new IllegalStateException(
                  "Your canvas factory's create method did not return " +
                  "either null or a GWT Widget, as required. See the " +
                  "GChart.setCanvasFactory method javadocs for details.");
           }
        }
     }

    
/**
  * Provides support for reusing certain property specifications
  * that are likely to be the same, given how images in a GChart
  * get reused, and given certain assumptions about which
  * properties of the image are most likely to remain unchanged
  * between updates.  For the most common scenarios, chart
  * updates are significantly faster due to replacing (relatively
  * expensive) DOM style attribute setting with (relatively
  * cheap) String reference or integer equality tests.
  * <p>
  *
  * For hovertext, the class also lets us defer actual generation
  * of the hovertext until they actually mouse over the image,
  * saving further time (it's surprisingly expensive just to
  * format the numbers and such used in hovertexts).
  * <p>
  *
  * TODO: Since we no longer use events on Image widgets,
  * see if we can switch to just using simpler HTML elements,
  * if that reduces the overhead associated with a Widget?
  *
  *
  */
   class ReusableImage extends Image {
     private String backgroundColor = USE_CSS;
     private String borderColor = USE_CSS;
     private String borderStyle= USE_CSS;
     // the capped border width, times two (to allow half-pixel widths)
     private int cappedBorderWidthX2 = GChart.NAI;
     private int width = GChart.NAI;
     private int height = GChart.NAI;
     int x = GChart.NAI;
     int y = GChart.NAI;
     String url = null;
    
     ReusableImage() {
        super();
        this.url = null
     }

          
     void setReusableProperties(String backgroundColor,
                                String borderColor,
                                String borderStyle,
                                int borderWidth,
                                double dWidth,
                                double dHeight,
                                double xD,
                                double yD,
                                String url) {

      // Round two edges, and define width to be their difference. 
      // (rounding this way assures bars align with gridlines, etc.)
      int newX = (int) Math.round(xD);
      int newW = (int) Math.round(xD + dWidth) - newX;
      int newY =  (int) Math.round(yD);
      int newH = (int) Math.round(yD + dHeight) - newY;
      int thickness = (newW < newH) ? newW : newH;
      // Don't allow borders that would exceed specified width or
      // height. So, if smaller of width, height is at least twice the
      // border width, border width is used as is, otherwise,
      // it's replaced with half the smaller of width, height:
      int newCappedBorderWidthX2 =   
         (2*borderWidth < thickness) ? 2*borderWidth : thickness;                       


/*
* Note: on a GWT absolute panel, the x,y position of the widget is the
* upper left corner of the widget's border, so x, y need no adjustment
* to account for an internal (positive) border. Negative (external)
* borders expand rectangle equally in all directions, so x,y need to
* shift back to the new upper left corner.  Transparent border
* emulation sets border width to 0, and adjusts element size and
* position to mimic border transparency (this rather odd feature is
* required to workaround the IE6 "transparent border" bug)
*
*/
      if (TRANSPARENT_BORDER_COLOR == borderColor) {//transparency emulation
         if (newCappedBorderWidthX2 > 0) {
            // to emulate an internal transparent border using a 0 width
            // border, we need to shift the upper left corner by the
            // amount of border, and shrink the size by twice the amount
            // of the border.
            newX += newCappedBorderWidthX2/2; // shift upper left corner
            newY += newCappedBorderWidthX2/2;
            newH -= newCappedBorderWidthX2; // shrink size
            newW -= newCappedBorderWidthX2;
         }
         // else, external border is just eliminated, no adjustment needed
         newCappedBorderWidthX2 = 0;
         borderColor = "transparent"; // because DOM won't accept null
         if (backgroundColor == TRANSPARENT_BORDER_COLOR)
            backgroundColor = "transparent";
      }
      else if (newCappedBorderWidthX2 < 0) {
        newX += newCappedBorderWidthX2/2; // shift upper left corner back
        newY += newCappedBorderWidthX2/2; // to incorporate external border.
      }
      else {
        newH -= newCappedBorderWidthX2; // shrink size to incorporate
        newW -= newCappedBorderWidthX2; // impact of internal border.
      }
     
      if (cappedBorderWidthX2 != newCappedBorderWidthX2) {
         if (1 == (newCappedBorderWidthX2 % 2)) {
// odd pixel 2 x borderWidth needs asymetical borders to fill rect
// (only positive (internal) borders can have half-pixel widths)
            int floorBW = newCappedBorderWidthX2/2;
            int ceilBW = floorBW+1;
            // (top, right, bottom, left) == (floor, floor, ceil, ceil)
            // assures symbol is odd-pixel border-filled in all cases
            DOM.setStyleAttribute(getElement(),
                               "borderWidth",
                               floorBW+"px "+floorBW+"px "+
                               ceilBW+"px " + ceilBW+"px ");
         }
         else {
           DOM.setStyleAttribute(getElement(),
               "borderWidth", Math.abs(newCappedBorderWidthX2/2)+"px");
         }
         cappedBorderWidthX2 = newCappedBorderWidthX2;
      }
     
      if (GChart.NAI == this.x) {
         // At first, use AbsolutePanel's official API
         // (to insulate us from any future AbsolutePanel
         // changes)
         setImagePosition(this, newX, newY);
         this.x = newX;
         this.y = newY;
      }
      else { // for speed, just set the edge positions that changed
             // (works, but bypasses AbsolutePanel's official API)
         if (this.x != newX) {
            DOM.setStyleAttribute(getElement(),"left", newX+"px");
            this.x = newX;
         }
         if (this.y != newY) {
            DOM.setStyleAttribute(getElement(),"top", newY+"px");
            this.y = newY;
         }
      }

      if (this.width != newW) {
        setWidth(newW + "px");
        this.width = newW;
      }
      if (this.height != newH) {
        setHeight(newH + "px");
        this.height = newH;
      }
 
      if (this.backgroundColor != backgroundColor) {
         DOM.setStyleAttribute(getElement(), "backgroundColor",
                               backgroundColor);
         this.backgroundColor =backgroundColor;
      }
      if (this.borderColor != borderColor) {
         DOM.setStyleAttribute(getElement(), "borderColor", borderColor);
         this.borderColor = borderColor;
      }
      if (this.borderStyle != borderStyle) {
         DOM.setStyleAttribute(getElement(), "borderStyle",
                               borderStyle);
         this.borderStyle = borderStyle;
      }

      if (this.url != url) {
/*
* WARNING: Redundant setUrls cause leaks in FF 2.0.0.16. So, be
* particularly careful not to accidentally "double set" a URL to
* the exact same URL (I did this with a slightly less efficient
* initialization of my images, and this caused a huge memory
* leak that I hope to memorialize, and lay to rest forever, here.)
*
* Symptoms, in FF 2 only, are those that would occur AS IF the
* extra setUrl increased the reference count on the (browser
* cached) image file so that Firefox can't release either it, or
* any of the img elements that reference it. A very big leak for
* GChart, since just about everything in a GChart references the
* exact same blank gif URL.
*
* Such symptoms did not occur in IE7, or in FF 2 if the cache
* has been disabled via "about:config".
*
* Search for "massively leak" and below that comment you will
* find two lines that, if uncommented, make the leak reappear.
*/
         setUrl(url);
         this.url = url;
      }     
    }
  } // end of class ReusableImage



 
   GraphicsRenderingPanel() {
       super();
// Overflow of this panel is controlled when it is added
//       GChart.setOverflow(this, "visible");
       GChart.setOverflow(canvasPanel, "visible");
       GChart.setOverflow(imagePanel, "visible");
       // these sub-panels have no size themselves, they are merely
       // there to segregate background, images, and labels.
       canvasPanel.setPixelSize(0,0);
       imagePanel.setPixelSize(0,0);
       this.add(canvasPanel, 0, 0);         
       this.add(imagePanel, 0, 0);
     }

     GChartCanvasLite getCanvas() {
       return canvas;
     }

     void setImagePosition(ReusableImage img, int x, int y) {
// workaround problem of special meaning of (-1,-1) to
// setWidgetPosition (makes position off by one pixel, though).
        if (x == -1 && y == -1) x = 0;
        imagePanel.setWidgetPosition(img, x, y);
     }

     // Tells panel you are ready to start drawing the curve on it
     void beginRendering(Rectangle canvasRegion) {
        if (null != canvas) {
           if (null == canvasRegion) {
              // hold onto empty canvas for simplicity
                 canvas.resize(0, 0);
                 canvasWidth = canvasHeight = 0;
           }
           else {
              int width = (int) Math.round(canvasRegion.width);
              int height = (int) Math.round(canvasRegion.height);
// if exactly same size, just clear...seems to save a little time
              if (width == canvasWidth && height == canvasHeight)
                 canvas.clear(); // reuse same canvas
              else { // size changed
                canvas.resize(width, height);
                canvasWidth = width;
                canvasHeight = height;
              }
              x0 = (int) Math.round(canvasRegion.x);
              y0 = (int) Math.round(canvasRegion.y);
// workaround problem with special meaning of (-1,-1) to
// setWidgetPosition (makes position off by one pixel).
              if (x0 == -1 && y0 == -1) x0 = 0;
              canvasPanel.setWidgetPosition((Widget) canvas, x0, y0);
           }
        }
        imageIndex = 0;
     }
     // Tells panel you are done drawing on it, and
     // it's OK to do any final cleanup/bookkeeping needed.
     void endRendering() {
       // hide or remove images no longer being used
        for (int iImage = optimizeForMemory ?
                          (imagePanel.getWidgetCount()-1) :
                          lastVisibleImage;
            iImage >= imageIndex;
            iImage--) {
          Widget w = imagePanel.getWidget(iImage);
          if (optimizeForMemory)
             imagePanel.remove(iImage);
          else
            DOM.setStyleAttribute(w.getElement(), "visibility", "hidden");
// setVisible unreliable w Images in IE as shown in TestGChart41a.java
//             w.setVisible(false);
       }
       lastVisibleImage = imageIndex - 1;           
     }

     /* Speedier, reusable, rendering-panel-managed images. In effect,
        turns image panel into a specialized memory manager. */
     void addOrRevealImage(String backgroundColor,
                           String borderColor,
                           String borderStyle,
                           int borderWidth,
                           double width,
                           double height,
                           double x, double y,
                           String url) {
       ReusableImage img;
       if (imageIndex <
           imagePanel.getWidgetCount()) { // reuse an old image      
         img = (ReusableImage) imagePanel.getWidget(imageIndex);
         if (imageIndex > lastVisibleImage)
// "" visibility means "visible whenever the parent is visible"
            DOM.setStyleAttribute(img.getElement(), "visibility", "");
// setVisible unreliable for Images in IE as shown in TestGChart41a.java
//           img.setVisible(true);
       }
       else {                             // add a new image 
         img = new ReusableImage();
         imagePanel.add(img);
       }

       img.setReusableProperties(backgroundColor,
                                    borderColor,
                                    borderStyle,
                                    borderWidth,
                                    width,
                                    height,
                                    x, y, url);
                                  
       if (lastVisibleImage < imageIndex)
         lastVisibleImage = imageIndex;
       imageIndex++;

     }
// Decided, for simplicitly, to just use HTML for rectanglular symbols.
// May change my mind again, so leaving code below in comments
     /* Fills a rectangle.
      * <p>
      *
      * Needed because GChartCanvasLite does not include fillRect
      * <p>
      *
      * TODO: Check if this slows us much compared to using canvas'
      * fillRect. We may also later need to use other canvas features
      * not in GChartCanvasLite (gradients? drawImage?) so should bundle
      * all such changes together, in one, final, GChart canvas
      * interface-related change to minimize interface-changing
      * aggravations for developers.
      *
      */
//     private void fillRect(GChartCanvasLite canvas, double x, double y, double width, double height) {
//       canvas.beginPath();
//       canvas.moveTo(x - rp.x0,y  - rp.y0); 
//       canvas.lineTo(x+width - rp.x0, y - rp.y0);
//       canvas.lineTo(x+width - rp.x0,
//                     y+height  - rp.y0);
//       canvas.lineTo(x  - rp.x0,
//                     y+height - rp.y0);
//       canvas.closePath();
//       canvas.fill();
//     }
     /*
      * Uses canvas to emulate a single Image-based rectangle,
      * assuming that the image URL points to a transparent GIF
      *
      */
//     void drawBorderedImage(String backgroundColor,
//                            String borderColor,
//                            String borderStyle,
//                            int borderWidth,
//                            double width,
//                            double height,
//                            double x, double y) {
//        double xOut = x + ((borderWidth < 0) ? borderWidth : 0);
//        double yOut = y + ((borderWidth < 0) ? borderWidth : 0);
//        double xIn = x + ((borderWidth > 0) ? borderWidth : 0);
//        double yIn = y + ((borderWidth > 0) ? borderWidth : 0);
//        double wOut  = width + ((borderWidth < 0) ? (-2*borderWidth) : 0);
//        double hOut  = height + ((borderWidth < 0) ? (-2*borderWidth) : 0);
//        double wIn  = width + ((borderWidth > 0) ? (-2*borderWidth) : 0);
//        double hIn  = height + ((borderWidth > 0) ? (-2*borderWidth) : 0);
//        if (TRANSPARENT_BORDER_COLOR != borderColor &&
//            "transparent" != borderColor) {
//          double absBW = Math.abs(borderWidth);
//          canvas.setFillStyle(borderColor);
//          // draw the four rectangles forming the outer perimeter
//          fillRect(canvas, xOut, yOut, absBW, hOut);
//          fillRect(canvas, xOut, yOut, wOut, absBW);
//          fillRect(canvas, xOut, yIn+hIn, wOut, absBW);
//          fillRect(canvas, xIn+wIn, yOut, absBW, hOut);
//        }
//        if (TRANSPARENT_BORDER_COLOR != backgroundColor &&
//            "transparent" != backgroundColor) {
//          // draw the inside-the-border rectangle
//          canvas.setFillStyle(backgroundColor);
//          fillRect(canvas, xIn, yIn, wIn, hIn);
//        }
//     }
    
  public void renderBorderedImage(String backgroundColor,
                                  String borderColor,
                                  String borderStyle,
                                  int borderWidth,
                                  double width,
                                  double height,
                                  double x, double y,
                                  String url) {
//     if (null != canvas && url == getBlankImageURL() &&
//         (borderStyle == USE_CSS || borderStyle.equals("solid")))
           /*
            * Use canvas to emulate a transparent, bordered image
            * (GChart can only render solid borders and blank image URLS
            * with canvas at this point)
            *
            */
//           drawBorderedImage(backgroundColor,
//                            borderColor,
//                            borderStyle,
//                            borderWidth,
//                            width,
//                            height,
//                            x, y);          
//        else // use an actual image HTML element
           addOrRevealImage(backgroundColor,
                             borderColor,
                             borderStyle,
                             borderWidth,
                             width,
                             height,
                             x, y, url);

     }

  
   } // end of class GraphicsRenderingPanel

  // An AbsolutePanel that just turns the protected insert method public
  class InsertableAbsolutePanel extends AbsolutePanel {
     public void insert(Widget child,
                      com.google.gwt.user.client.Element container,
                      int beforeIndex,
                        boolean domInsert) {
        super.insert(child, container, beforeIndex, domInsert);
     }
  }
 
   class PlotPanel extends AbsolutePanel {
     private int topMargin;
     private int xAxisEnsembleHeight;
     private int xChartSize;
     private double xMax = Double.NaN;
     private double xMin = Double.NaN;
     private int y2AxisEnsembleWidth;
     private double y2Max = Double.NaN;
     private double y2Min = Double.NaN;
     private int yAxisEnsembleWidth;
     private int chartLegendThickness;
     private int chartFootnotesThickness;
     private int yChartSize;
     private double yMax = Double.NaN;
     private double yMin = Double.NaN;
// Retains the last moved-to (Event.ONMOUSEMOVE) client mouse position, or NAI if
// mouse moved away from chart entirely.    
     private int clientX = GChart.NAI;
     private int clientY = GChart.NAI;
     // Pixel coords of above mouse position, relative to top-left
     // corner of the GChart (mouse position in GChart's pixel coords)
     private int xMouse = GChart.NAI;
     private int yMouse = GChart.NAI;
     // first rendering panel is reserved for chart decorations,
     // and its overflow outside of the plot panel is never hidden
     final static int DECORATIVE_RENDERING_PANEL_INDEX = 0;
     private InsertableAbsolutePanel graphicsPanel =
        new InsertableAbsolutePanel();
     private InsertableAbsolutePanel annotationPanel =
        new InsertableAbsolutePanel();
    
     /*
      * Adds a sub-panel of this plot panel that contains the widgets
      * used to render the graphical parts of the given curve
      * <p>
      *
      * This method must be called just after a new curve is added to
      * the chart, to add it's associated graphics rendering panel;
      * GChart assumes each curve (except internal decoration rendering
      * curves, which share a single rendering panel for efficiency)
      * already has an corresponding, unique, rendering panel available
      * and ready to go during updates.
      *
      */

     void addGraphicsRenderingPanel(int rpIndex) {
        final boolean domInsert = true;
        GraphicsRenderingPanel w = new GraphicsRenderingPanel();
        if (DECORATIVE_RENDERING_PANEL_INDEX == rpIndex ||
            isHoverFeedbackRenderingPanel(rpIndex) ||
            !getClipToPlotArea()) {
         // chart decorations and hover feedback are never clipped
          w.setPixelSize(0, 0);
          GChart.setOverflow(w, "visible");
        }
        else {
          w.setPixelSize(getXChartSize(), getYChartSize());
          GChart.setOverflow(w, "hidden");
        }
        graphicsPanel.insert(w, graphicsPanel.getElement(), rpIndex, domInsert);
        graphicsPanel.setWidgetPosition(w, 0, 0);
     }
    
     /*
      * Adds a sub-panel of this plot panel that contains the widgets
      * used to render the annnotations of the given curve
      * <p>
      *
      * This method must be called just after a new curve is
      * added to the chart, to add it's associated annotation
      * rendering panel; GChart assumes each curve has a
      * correspondingly indexed rendering panel during updates.
      *
      */

     void addAnnotationRenderingPanel(int rpIndex) {
        final boolean domInsert = true;
        AnnotationRenderingPanel w = new AnnotationRenderingPanel();
        annotationPanel.insert(w, annotationPanel.getElement(), rpIndex, domInsert);
        annotationPanel.setWidgetPosition(w, 0, 0);
     }

     /*
      * Removes the rendering panel of the curve with the given
      * internal index on the curves list.
      * <p>
      *
      * This method must be called just before a curve is removed
      * from the chart, to remove the widgets used to render
      * that curve in the browser.
      *
      */
     void removeGraphicsRenderingPanel(int rpIndex) {
        graphicsPanel.remove(rpIndex);
     }
     void removeAnnotationRenderingPanel(int rpIndex) {
        annotationPanel.remove(rpIndex);
     }

     /*
      * Returns panel used to render the graphical and textual element of
      * the curve with the given internal index within the browser.
      *
      */
     GraphicsRenderingPanel getGraphicsRenderingPanel(int rpIndex) {
        if (0 == graphicsPanel.getWidgetCount()) // for lazy addition
          // smaller,faster if all background curves put on single panel
           for (int i = N_PRE_SYSTEM_CURVES-1; i < curves.size(); i++) {
             int rpInd = getRenderingPanelIndex(i);
             addGraphicsRenderingPanel(rpInd);
           }
        GraphicsRenderingPanel result = (GraphicsRenderingPanel)
                                (graphicsPanel.getWidget(rpIndex));
        return result;
     }
     AnnotationRenderingPanel getAnnotationRenderingPanel(int rpIndex) {
        if (0 == annotationPanel.getWidgetCount()) // for lazy addition
          // smaller,faster if all background curves put on single panel
          for (int i = N_PRE_SYSTEM_CURVES-1; i < curves.size(); i++) {
             int rpInd = getRenderingPanelIndex(i);
             addAnnotationRenderingPanel(rpInd);
          }
        AnnotationRenderingPanel result = (AnnotationRenderingPanel)
                                (annotationPanel.getWidget(rpIndex));
        return result;
     }
    
     int getClientX() {return clientX;}
     void setClientX(int clientX, boolean isClick) {
/*
* Due to presumed bugs in FF2 and Chrome, space-bar clicking on
* TestGChart25's "rotate" button produces bogus 0 and/or (in Chrome)
* seemingly random negative return values from <tt>event.getClient[XY]
* with the ONCLICK event. IE7 produces correct mouse coordinates for
* Event.ONCLICK in this case. The bogus coordinates, if not corrected,
* generate bogus "mouse moved off chart"-like actions (in TestGChart25,
* Chrome produces inappropriate deselection of the hovered over point
* after a space-bar invoked update)<p>
*
* Workaround is to just ignore any 0 or negative coordinates--thus
* using the last valid coordinates seen by the chart's mouse tracking
* code in lieu of the bogus ones.
*
* <p>
*
* The resulting 1px "partly-dead" band at the top and left edges
* of the client area due to this workaround (0 is a valid client
* coordinate) is unlikely to be a significant problem, since
* clicked-on stuff is rarely clicked on right along the edges
* of the client area.
* <p>
*
*/
        if (clientX <= 0 && isClick)
           return;
        else if (clientX < 0)
   // some browsers (e.g. FF2) use -1 to indicate undefined mouse coords.
           clientX = GChart.NAI;
  
        this.clientX = clientX; 
        // computing this on-the-fly is VERY expensive, so we retain it
        // (the buffering can be wrong in unusual scrolling scenarios)
        xMouse = (GChart.NAI == clientX) ? GChart.NAI :
                    (Window.getScrollLeft() + clientX - getAbsoluteLeft());
     }
     int getClientY() {return clientY;}
     // See comments on analogous lines in setClientX above
     void setClientY(int clientY, boolean isClick) {
        if (clientY <= 0 && isClick)
           return;
        else if (clientY < 0)
           clientY = GChart.NAI;
       
        this.clientY = clientY;
        yMouse = (GChart.NAI == clientY) ? GChart.NAI :
                 (Window.getScrollTop() + clientY -
                  getAbsoluteTop());
     }

     /*
      * In IE drop-down list boxes, when you enter the dropdown
      * part of the list, client coordinates suddenly become -1, -1
      * (presumably IE's way of saying it won't tell you what they are;
      * apparently the dropdown part of the list isn't in the DOM)
      * These repair methods replace such impossible client coordinates
      * with the last valid coordinates.
      * <p>
      *
      * Without this patch, the geometric "within the hover widget"
      * test on GChartExample20a fails in IE7: as soon as user mouses
      * into the dropdown list, the entire hover widget is closed.  <p>
      *
      */
     private int repairBadClientX(int x) {
        if (x <= 0// 0 isn't strictly bad, but its one of the
                     // bad values that can pop up in some browsers.
           return clientX;
        else
           return x;
     }
    
     private int repairBadClientY(int y) {
        if (y <= 0)
           return clientY;
        else
           return y;
     }
    
     int getXMouse() {
         return xMouse;       
     }
     int getYMouse() {
       return yMouse;       
     }

     // Mouse x position relative to plot area upper left corner.
     int getXMousePlotArea() {
        int result = xMouse-yAxisEnsembleWidth;
         return result;
     }
     // Mouse y position relative to plot area upper left corner.
     int getYMousePlotArea() {
       int result = yMouse-topMargin;
       return result;       
     }
    
     PlotPanel() {
        super();
// allows labels, symbols, that extend a tad off the
// chart proper to still appear on the chart; AbsolutePanel
// default is to truncate these.
        GChart.setOverflow(this, "visible");
        GChart.setOverflow(graphicsPanel, "visible");
        GChart.setOverflow(annotationPanel, "visible");
       // these sub-panels have no size themselves, they are merely
       // there to segregate the graphical and annotation part of chart
        graphicsPanel.setPixelSize(0,0);
        annotationPanel.setPixelSize(0,0);
// this order assures all the annotations are on top of all the graphics
        this.add(graphicsPanel, 0, 0);         
        this.add(annotationPanel, 0, 0);       
// events for hover selection feedback, click event handling
        sinkEvents(Event.ONMOUSEMOVE | Event.ONMOUSEOUT |
                   Event.ONCLICK | Event.ONMOUSEOVER);
     }
    
     int getXAxisEnsembleHeight() {
        return xAxisEnsembleHeight;
     }
     double getXMax() {
        return xMax;
     }
     double getXMin() {
        return xMin;
     }
     int getY2AxisEnsembleWidth() {
        return y2AxisEnsembleWidth;
     }
     double getY2Max() {
        return y2Max;
     }
     double getY2Min() {
        return y2Min;
     }
     int getYAxisEnsembleWidth() {
        return yAxisEnsembleWidth;
     }

     int legendThickness() { return chartLegendThickness; }
     int chartFootnotesThickness() {return chartFootnotesThickness;}
     int chartTitleThickness() {return topMargin; }
    
     double getYMax() {
        return yMax;
     }
     double getYMin() {
        return yMin;
     }

     void reset(int xChartSize, int yChartSize,
                       boolean hasYAxis, boolean hasY2Axis,
                       Axis xAxis, Axis yAxis, Axis y2Axis) {

    // these must come first (getTickLabelThickness(false) needs them)
        getXAxis().maybePopulateTicks();
        getYAxis().maybePopulateTicks();
        getY2Axis().maybePopulateTicks();

        this.xChartSize = xChartSize;
        this.yChartSize = yChartSize;

        Axis.AxisLimits axisLimits = xAxis.getAxisLimits();
        xMin = axisLimits.min;
        xMax = axisLimits.max;
        axisLimits = yAxis.getAxisLimits();
        yMin = axisLimits.min;
        yMax = axisLimits.max;
        axisLimits = y2Axis.getAxisLimits();
        y2Min = axisLimits.min;
        y2Max = axisLimits.max;

        topMargin = getChartTitleThickness();
       
        xAxisEnsembleHeight = xAxis.getAxisLabelThickness() +
                              xAxis.getTickLabelThickness(false) +
                              xAxis.getTickSpace() +
                              xAxis.getTickLabelPadding();
        yAxisEnsembleWidth = yAxis.getAxisLabelThickness() +
                             yAxis.getTickLabelThickness(false) +
                             yAxis.getTickSpace() +
                             yAxis.getTickLabelPadding();
        y2AxisEnsembleWidth = y2Axis.getAxisLabelThickness() +
                              y2Axis.getTickLabelThickness(false) +
                              y2Axis.getTickSpace() +      
                              y2Axis.getTickLabelPadding();
       
        chartLegendThickness = getLegendThickness();
        chartFootnotesThickness = getChartFootnotesThickness();
       
        setPixelSize(getXChartSizeDecoratedQuickly(),
                     getYChartSizeDecoratedQuickly());

        setWidgetPosition(graphicsPanel, yAxisEnsembleWidth, topMargin);
        setWidgetPosition(annotationPanel, yAxisEnsembleWidth, topMargin);

        // if there are any existing graphical rendering panels, bring
        // their clipping specs into agreement with the chartspecs
        for (int i = 0; i < getRenderingPanelCount(); i++) {
           GraphicsRenderingPanel grp = (GraphicsRenderingPanel)
             graphicsPanel.getWidget(i);
           if (DECORATIVE_RENDERING_PANEL_INDEX == i ||
               isHoverFeedbackRenderingPanel(i) ||
               !getClipToPlotArea()) {
              grp.setPixelSize(0, 0);
              GChart.setOverflow(grp, "visible");
           }
           else {
              grp.setPixelSize(getXChartSize(), getYChartSize());
              GChart.setOverflow(grp, "hidden");
           }
         }
     }

     double xToChartPixel(double x) {
        double result = Double.NaN;
        if (-Double.MAX_VALUE == x)
           result = yAxisEnsembleWidth;
        else if (Double.MAX_VALUE == x)
           result = yAxisEnsembleWidth+xChartSize-1.0;
        else if (!(x!=x)) { // x!=x is a faster isNaN
          result =            
          (yAxisEnsembleWidth * (xMax - x) +
          (yAxisEnsembleWidth+xChartSize-1.0) * (x - xMin))/
                  (xMax - xMin);
        }
       
        return result;
     }

     double xToPixel(double x) {
        double result = Double.NaN;
        if (-Double.MAX_VALUE == x)
           result = 0;
        else if (Double.MAX_VALUE == x)
           result = xChartSize-1.0;
        else if (!(x!=x)) { // x!=x is a faster isNaN
          result = (xChartSize-1.0) * (x - xMin)/(xMax - xMin);
        }
        return result;
     }

    
     double xChartPixelToX(int xPx) {
        double result = Double.NaN;
        if (GChart.NAI != xPx && xChartSize > 1) {
           result = xMin + (xMax - xMin) *
                   (xPx - yAxisEnsembleWidth)/(xChartSize-1.);
        }
        return result;
     }

     double xPixelToX(int xPx) {
        double result = Double.NaN;
        if (GChart.NAI != xPx && xChartSize > 1) {
           result = xMin + (xMax - xMin) * xPx/(xChartSize-1.);
        }
        return result;
     }

    
     double dxToPixel(double dx) {
// xMax and xMin are at centers of their pixels, hence the -1
        double result = (dx * (xChartSize-1))/(xMax-xMin);
        return result;
     }
    
     double yToChartPixel(double y, boolean isY2) {
        double minY = isY2 ? y2Min : yMin;
        double maxY = isY2 ? y2Max : yMax;
        double result = Double.NaN;
        if (-Double.MAX_VALUE == y)
           result = yChartSize + topMargin - 1.0;
        else if (Double.MAX_VALUE == y)
           result = topMargin;
        else if (!(y!=y)) // x!=x is a faster isNaN
           result = (topMargin * (y - minY) +
                    ((yChartSize + topMargin - 1.0) *
                     (maxY - y)))/(maxY - minY);
        return result;
     }

     double yToPixel(double y, boolean isY2) {
        double minY = isY2 ? y2Min : yMin;
        double maxY = isY2 ? y2Max : yMax;
        double result = Double.NaN;
        if (-Double.MAX_VALUE == y)
           result = yChartSize - 1.0;
        else if (Double.MAX_VALUE == y)
           result = 0;
        else if (!(y!=y)) // x!=x is a faster isNaN
           result = (yChartSize - 1.0) * (maxY - y)/(maxY - minY);
        return result;
     }
    
     double yChartPixelToY(int yPx) {
        double result = Double.NaN;
        if (GChart.NAI != yPx && yChartSize > 1) {
          result = yMax + (yMin - yMax) *
                  (yPx - topMargin)/(yChartSize-1.);
        }
        return result;
     }
     double yPixelToY(int yPx) {
        double result = Double.NaN;
        if (GChart.NAI != yPx && yChartSize > 1) {
          result = yMax + (yMin - yMax) * yPx/(yChartSize-1.);
        }
        return result;
     }
     double yChartPixelToY2(int yPx) {
        double result = Double.NaN;
        if (GChart.NAI != yPx && yChartSize > 1) {
           result = y2Max + (y2Min - y2Max) *
                    (yPx - topMargin)/(yChartSize-1.);
        }
        return result;
     }
     double yPixelToY2(int yPx) {
        double result = Double.NaN;
        if (GChart.NAI != yPx && yChartSize > 1) {
           result = y2Max + (y2Min - y2Max) * yPx/(yChartSize-1.);
        }
        return result;
     }
    
     double dyToPixel(double dy, boolean isY2) {
        double minY = isY2 ? y2Min : yMin;
        double maxY = isY2 ? y2Max : yMax;
// maxY and minY are at centers of their pixels, hence the -1
        double result = (dy * (yChartSize-1))/(maxY-minY);
        return result;
     }


     // keep track of last touched point & hover widget
     Curve.Point touchedPoint = null;
     HoverUpdateable touchedHoverWidget = null;
     // returns the inner aligned label of the opened hover annotation
     // (this is the one that directly holds the popup hover annotation)
     AlignedLabel getOpenedHoverContainer() {
        AlignedLabel result = null;
        Curve c = getSystemCurve(HOVER_ANNOTATION_ID);
        if (touchedPoint != null && c.isVisible()) {
           int internalIndex = getInternalCurveIndex(c);
           int rpIndex = getRenderingPanelIndex(internalIndex);
           AnnotationRenderingPanel arp =
              getAnnotationRenderingPanel(rpIndex);
           result = arp.getFirstInnerAlignedLabel();
        }
        return result;
     }

     // the element associated with any opened hover container, else null
     Element getOpenedHoverElement() {
        AlignedLabel hoverContainer = getOpenedHoverContainer();
        Element result = (null == hoverContainer) ? null :
                         hoverContainer.getElement();
        return result;
     }

     // so if user calls update inside hoverUpdate, it won't recurse
     private boolean insideHoverUpdate = false;
    // so if user calls update inside hoverCleanup, it won't recurse
     private boolean insideHoverCleanup = false;
     /*
      * Configures GChart's special selection cursor and
      * hover annotation "system curves" so that they
      * provide appropriate feedback associated with "touching" the
      * given point with the mouse. When rendered, these curves
      * will:
      * <p>
      *
      * <ol>
      * <li> Highlight the selected point in accord with specified hover
      * selection options
      * <p>
      * <li> Display a point-specific hover annotation, in accord with
      * various hover annotation related options.
      *
      * </ol>
      *
      * <p>
      *
      * Also executes <tt>hoverCleanup</tt> on any hover widget
      * associated with the previously touched point, and
      * <tt>hoverUpdate</tt> on any hover widget associated with the
      * newly touched point, and maintains up-to-date references to the
      * last touched point and last touched hover widget.
      *
      */
     private void touch(Curve.Point p) {
        // Note: getTouchedPoint always returns NEW touched point
          Curve.Point prevTouchedPoint = touchedPoint;
          touchedPoint = p;
          Curve cAnnotation = getSystemCurve(HOVER_ANNOTATION_ID);
          Curve cCursor = getSystemCurve(HOVER_CURSOR_ID);
          Curve cTouched = (null == p) ? null : p.getParent();
         
          if (null != touchedHoverWidget) {
              // free up resources allocated to previous hover widget
              if (!insideHoverCleanup) {
                 try {
                    insideHoverCleanup = true;                   
                    touchedHoverWidget.hoverCleanup(prevTouchedPoint);
                 }
                 finally {insideHoverCleanup = false;}
              }
             
          }

          // with hoverCleanup out of the way, switch to new hover widget
          touchedHoverWidget = (null == cTouched) ? null :
                               cTouched.getSymbol().getHoverWidget();

          if (null == touchedHoverWidget) {
             if (null != p) {
                // no hover-widget, just use expanded hover-template
                String hovertext = p.getHovertext();

                cAnnotation.getPoint(0).setAnnotationText(
                   hovertext,
                   cTouched.getSymbol().getHoverAnnotation().widthUpperBound,
                   cTouched.getSymbol().getHoverAnnotation().heightUpperBound);
             }
          }
          else {
             // touched curve has custom hover widget; update it, etc.
             if (!insideHoverUpdate) {
                try {
                   insideHoverUpdate = true;                   
                   touchedHoverWidget.hoverUpdate(p);
                }
                finally {insideHoverUpdate = false;}
             }
             cAnnotation.getPoint(0).setAnnotationWidget(
                (Widget) touchedHoverWidget,
                cTouched.getSymbol().getHoverAnnotation().widthUpperBound,
                cTouched.getSymbol().getHoverAnnotation().heightUpperBound);
          }
         
          if (null == p) {    // no longer touching anything
             cAnnotation.setVisible(false);
             cCursor.setVisible(false);
          }
          else { // touching something, show that

             if (!cTouched.getSymbol().getHoverAnnotationEnabled()) {
                cAnnotation.setVisible(false);
             }
             else {
               cAnnotation.setVisible(true);
               cAnnotation.setYAxis(cTouched.getYAxis());
               cAnnotation.getPoint(0).setX(p.getX());
               cAnnotation.getPoint(0).setY(p.getY());
               cAnnotation.getSymbol().copy(cTouched.getSymbol());
               // the symbol isn't needed, so make it transparent
               // and zap any images (can't make it 0-sized since
               // annotation placement is size-dependent)
               cAnnotation.getSymbol().setImageURL(
                  GChart.DEFAULT_BLANK_IMAGE_URL_FULLPATH);
               cAnnotation.getSymbol().setBackgroundColor("transparent");
               cAnnotation.getSymbol().setBorderColor(TRANSPARENT_BORDER_COLOR);
               if (null !=
                   cTouched.getSymbol().getHoverAnnotationSymbolType())
                  cAnnotation.getSymbol().setSymbolType(
                    cTouched.getSymbol().getHoverAnnotationSymbolType());
               // else just stick with the touched symbol's type
              
               // copy the hover annotations specs (including
               // hover widget ref or HTML defined above)
               // to the annotation curve's point
               cAnnotation.getPoint(0).setAnnotationFontColor(
                  cTouched.getSymbol().getHoverFontColor());
               cAnnotation.getPoint(0).setAnnotationFontSize(
                  cTouched.getSymbol().getHoverFontSize());
               cAnnotation.getPoint(0).setAnnotationFontStyle(
                  cTouched.getSymbol().getHoverFontStyle());
               cAnnotation.getPoint(0).setAnnotationFontWeight(
                  cTouched.getSymbol().getHoverFontWeight());
               cAnnotation.getPoint(0).setAnnotationLocation(
                  cTouched.getSymbol().getHoverLocation());
               cAnnotation.getPoint(0).setAnnotationXShift(
                  cTouched.getSymbol().getHoverXShift());
               cAnnotation.getPoint(0).setAnnotationYShift(
                  cTouched.getSymbol().getHoverYShift());
             }
            
             if (!cTouched.getSymbol().getHoverSelectionEnabled()) {
                cCursor.setVisible(false);
             }
             else {
               cCursor.setVisible(true);
               cCursor.setYAxis(cTouched.getYAxis());
             // place cursor curve's point where touched point is:
               cCursor.getPoint(0).setX(p.getX());
               cCursor.getPoint(0).setY(p.getY());
             // cursor gets (mostly) same props as touched symbol  
               cCursor.getSymbol().copy(cTouched.getSymbol());
               if (null != cTouched.getSymbol().getHoverSelectionSymbolType())
                 cCursor.getSymbol().setSymbolType(
                    cTouched.getSymbol().getHoverSelectionSymbolType());
               double fillSpacing =
                  cTouched.getSymbol().getHoverSelectionFillSpacing();   
               if (!(fillSpacing != fillSpacing))
                 cCursor.getSymbol().setFillSpacing(fillSpacing);
               int fillThickness =
                 cTouched.getSymbol().getHoverSelectionFillThickness();
               if (GChart.NAI != fillThickness)
                 cCursor.getSymbol().setFillThickness(fillThickness);
               if (GChart.NAI != cTouched.getSymbol().getHoverSelectionHeight())
                 cCursor.getSymbol().setHeight(
                    cTouched.getSymbol().getHoverSelectionHeight());
               if (GChart.NAI != cTouched.getSymbol().getHoverSelectionWidth())
                 cCursor.getSymbol().setWidth(
                    cTouched.getSymbol().getHoverSelectionWidth());
               cCursor.getSymbol().setImageURL(cTouched.getSymbol().getHoverSelectionImageURL());
               cCursor.getSymbol().setBackgroundColor(
                  cTouched.getSymbol().getHoverSelectionBackgroundColor());
               cCursor.getSymbol().setBorderColor(
                  cTouched.getSymbol().getHoverSelectionBorderColor());
               cCursor.getSymbol().setBorderStyle(
                  cTouched.getSymbol().getHoverSelectionBorderStyle());
               int borderWidth =
                     cTouched.getSymbol().getHoverSelectionBorderWidth();
               cCursor.getSymbol().setBorderWidth(borderWidth);
             }
           }
       }

       /*
        * Is the given target element contained within the given container?
        * (tried isOrHasChild but was getting exceptions in FF2 I could
        *  not track to their source, so I just stuck with this)
        */ 
     private boolean isContainedIn(Element container, EventTarget et) {
        Element part =
          (null == et || !Element.is(et)) ? null : Element.as(et);
        /*
         * In Chrome and FF2, the next line makes dropdown lists inside
         * hover widgets work more correctly when they click on the
         * dropdown part of the list. Otherwise, hover widget can close
         * inappropriately. As tested in GChartExample20a.java
         *
         */
         if (null == part) return true;
         try {
           for (Element ancestor = part;
                ancestor != null && container != null;
                ancestor = ancestor.getParentElement())
              if (container == ancestor) return true;
         }
         catch (Exception e) {
/*
* In FF2, we get the error "Error: uncaught exception: Permission
* denied to get property HTMLDivElement.parentNode" if a TextBox is
* placed into the chart x axis label and you mouse over that textbox
* (as reported by secnoc in issue #24, which has additional useful info
* about the likely cause of this problem; TestGChart44.java reproduces
* this behavior if this try/catch is removed).
*
* Returning true, which will act as if the element is contained in the
* GChart, has "cruft possibilities" as in some cases a hoverwidget may
* not get closed when user mouses completely out of the GChart, onto a
* TextBox, but should otherwise be a lesser evil than false or an
* uncaught exception.
*
*/
           return true;

         }
         return false;
       }

         
     /*
      * Is given mouse client point geometrically "inside" container?
      * <p>
      *
      * Note: Certain widgets, like drop-down lists, SuggestBox, etc.
      * implicitly create popups that are not children of the chart, and
      * thus when the user moves into these popups, a mouseout that
      * looks like they are leaving the hover widget or chart, and
      * thus inappropriately hides it can often make hover widgets
      * involving such elements unusable.
      *
      * <p>
      *
      * But, by adding a geometric condition to define "being out of
      * the chart or hover widget", hover widgets that use such popups
      * can avoid such inappropriate hiding as long as the popups remain
      * geometrically within either the chart as a whole, or the hover
      * widget itself.  For example a form with drop-down lists is OK
      * as long as the drop down lists don't extrude off the form
      * when they drop down.<p>
      *
      * Unfortunately, this creates another problem, in that hover
      * widgets that extrude off the chart can remain open when the user
      * mouses off the chart in such a way that the mouseout occurs at a
      * point geometrically within the hover widget. We consider this
      * small amount of "hover cruft" a lesser evil than not being able
      * to use dropdown lists and such within hover widgets at all.
      * <p>
      *
      * Often a more natural solution, rather than wrestling with such
      * "geometrical popup containment", is a click-invoked modal
      * dialog. But that has a distinctly different feel than the more
      * nearly modeless hover widget, so I thought there was room/need
      * for both approaches.
      *
      */
       private boolean isGeometricallyContainedIn(Element container,
                                                  int clientX, int clientY) {
         
          if (null == container)
             throw new IllegalArgumentException(
                 "Container cannot be null");
          boolean result = false;


          /*
           * Equations below shrink container 1px around its perimeter,
           * to account for apparent roundoff errors in FF2 associated
           * with window scrolling. FF2 mouseout events get dropped
           * (about half the time, with random scroll position choices
           * hence my roundoff suspicions--problem did not occur in
           * Chrome or IE7) without this 1px shrinkage. End users can't
           * discriminate a 1px mouse shift anyway, so there is no
           * real downside (except that you had to read this comment)
           * to using this workaround.
           *
           */
          int y = Window.getScrollTop() + repairBadClientY(clientY);
          int absTop = container.getAbsoluteTop();
          if (absTop < y &&
              y+1 < absTop + container.getOffsetHeight()) {
             int x = Window.getScrollLeft() + repairBadClientX(clientX);
             int absLeft = container.getAbsoluteLeft();
             if (absLeft < x &&
                 x+1 < absLeft + container.getOffsetWidth())
                result = true;
          }
         
          return result;
       }

       // Touches the underlying object at the last event's mouse
       // position if it is different from the currently touched point,
       // or if retouch is true. Returns true if a touch occured.
       private boolean touchObjectAtMousePosition(boolean retouch) {
          boolean result = false;
          Curve.Point pointAtPosition = getClosestBrushTouchingPointNoCheck(
             getXMousePlotArea(), getYMousePlotArea());
          if ((pointAtPosition != touchedPoint) || retouch) {
             touch(pointAtPosition);
             result = true;
          }
          return result;
       }
       // touch object at mouse, but only if it is a different one
       boolean touchObjectAtMousePosition() {
          boolean result = touchObjectAtMousePosition(false);
          return result;
       }
       // touch object at mouse, even if it is the same one as last time
       void retouchObjectAtMousePosition() {
          touchObjectAtMousePosition(true);
       }

       /*
        * Does the event occur over the currently opened hover
        * annotation?
        *
        * This method helps us to ignore mouse clicks and moves over a
        * "sticky-open" hover annotation, or one of its children. Idea
        * is to prevent the hover feedback from jumping to another
        * point while the user interacts with an opened hover widget.
        * <p>
        *
        * This is not just important for hover annotations that contain
        * buttons and such, but even for hover annotations based on
        * static text, since the user might want to select/copy that
        * text (involving mouse moves) for example.
        *
        *
        */
       private boolean isOverOpenedHoverAnnotation(Event event) {
          boolean result = false;
          Element hoverElement = getOpenedHoverElement();
          if (null != hoverElement) {
             if (isContainedIn(hoverElement, event.getEventTarget()))
                 result = true;
             else if (isGeometricallyContainedIn(hoverElement,
                                                event.getClientX(),
                                                event.getClientY()))
                result = true;
          }
          return result;
       }

       /*
        * Does event (assumed a MOUSEOUT) take the mouse completely
        * outside of the current chart?
        * <p>
        *
        * To be completely outside the chart, the event must be both not
        * associated with any child element of the chart (as represented
        * in the DOM) and geometrically outside of the chart's "box" and
        * the "box" of any currently opened hover annotation.  <p>
        *
        * Because a GChart is rendered with many DOM elements, moving
        * the mouse across it generates many MOUSEOUT events. This
        * method lets us focus on only those that take us completely off
        * the chart, and thus require the hover feedback to be turned
        * off.
        * <p>
        *
        *
        *
        */
       private boolean takesUsCompletelyOutsideChart(Event event) {
          boolean result = true;
                               
          if (isContainedIn(getElement(),
                            event.getRelatedEventTarget()))
/* hoverElement is always a descendant of the main chart element due to
* how GChart generates it, so if this branch isn't reached, toElement
* is not contained in either chart or the opened hover annotation */
             result = false;
          else if (isGeometricallyContainedIn(getElement(),
                                           event.getClientX(),
                                           event.getClientY()))
             result = false;
          else {
             Element hoverElement = getOpenedHoverElement();
             if (null != hoverElement) {
                if (isGeometricallyContainedIn(hoverElement,
                                               event.getClientX(),
                                               event.getClientY()))
                   result = false;
             }
          }

          return result;

       }

      
       /**
        * Fired whenever a browser event is recieved.
        * <p>
        *
        * GChart keeps track of browser mouse-moves, mouse-outs,
        * mouse-overs, and mouse clicks and will automatically provide
        * appropriate hover feedback whenever the mouse "touches"
        * rendered symbols on the chart. It also maintains a reference
        * to the "currently touched" point which you can retrieve via
        * the <tt>getTouchedPoint</tt> method.  <p>
        *
        * GChart never "eats" mouse events (it just watches them go by,
        * and keeps track of what the mouse-anchored brush is touching)
        * so containing Widgets can track and respond to the same mouse
        * event stream after GChart does, if they want to.
        *
        * <p>
        *
        * Each curve's symbol can configure how hover feedback is
        * displayed via the <tt>setHover*</tt> family of methods and the
        * related HoverUpdateable and HoverParameterInterpreter
        * interfaces. In addition, GChart implements the standard
        * GWT <tt>SourcesClickEvents</tt> interface, so you
        * can easily implement <tt>ClickListener.onClick</tt>
        * to be notified of user clicks on a GChart, using
        * <tt>getTouchedPoint</tt> to grab the clicked-on point.
        * <p>
        *
        * This method can only properly track events when
        * <tt>isUpdateNeeded</tt> returns false (implies DOM/GChart
        * specs are in synch) so if you want this tracking system to
        * work as intended, you need to be sure to always call
        * <tt>update</tt> after making a series of chart specification
        * changes, just before you give control back to the browser.
        *
        *
        * @param event the browser event that GChart will monitor
        * so as to maintain a reference to the "touched" point and
        * provide appropriate hover feedback.
        * 
        * @see #touch touch
        * @see #getTouchedPoint getTouchedPoint
        * @see #setHoverWidget setHoverWidget
        * @see #setHovertextTemplate setHovertextTemplate
        * @see #setHoverAnnotationEnabled setHoverAnnotationEnabled
        * @see #setHoverSelectionEnabled setHoverSelectionEnabled
        * @see HoverUpdateable HoverUpdateable
        * @see #setHoverParameterInterpreter setHoverParameterInterpreter
        * @see HoverParameterInterpreter HoverParameterInterpreter
        * @see #isUpdateNeeded isUpdateNeeded
        * @see #update update
        *
        */
       public void onBrowserEvent(Event event) {
          // GWT docs say without this, 1.6+ event handlers won't work
          super.onBrowserEvent(event);
         
          /*
           * The tracking of the mouse position depends on if there are
           * opened hover annotations or not (mouse moves over such
           * annotations don't get tracked, and thus don't change the
           * "touched" point). However, all of that dependency can be
           * determined by the current DOM rendering of the chart--there
           * is no need to look at actual chart specs.  <p>
           *
           * So, when the DOM/chart specs are inconsistent (chart "needs
           * update") we continue to perform mouse tracking based on the
           * <i>last completed DOM rendering</i> of the chart (that is,
           * the last <tt>assembleChart</tt> call).  However, actual
           * changes to the chart are "frozen" (as assured by the
           * <tt>!isUpdateNeeded</tt> test below) so no DOM changes
           * occur automatically in response to mouse moves over things
           * (e.g. no changes to hover feedback occur--that's frozen,
           * too). In short, we track, but do not act.
           *
           * <p>
           *
           * We can think of it this way: mouse tracking remains
           * consistent with the <i>last DOM rendered</i> specification
           * and then it is as if all of the accumulated specification
           * changes are applied to the DOM at that point in time when
           * the next (developer invoked) update occurs. That means
           * there is exactly one point in time of "unpredictable
           * change" (points previously hovered over can disappear from
           * under the mouse since they have been deleted or moved,
           * etc.). But that discontinuity can be adequately managed by
           * the developer via the <tt>TouchedPointUpdateOption</tt>
           * argument to update.
           * <p>
           *
           * Well, that's the theory. But GChart's mouse tracking has
           * only been tested for the case where <tt>update</tt> is
           * always called just before the developer ceeds control back
           * to the browser after making a series of chart spec changes.
           * So, the public docs warn developers to be sure that they do
           * that, too. But the hope is that specialized applications
           * where they don't call update until the user explicitly asks
           * for that (say, for a very busy chart with an editing
           * capability and a "refresh" button) will also work OK.
           *
           * <p>
           *
           * Another important consequence of this "track but don't act"
           * approach is that it assures that only cheap/quick
           * operations can be triggered automatically by direct user
           * mousing. Potentially expensive "full chart" updates always
           * require a direct developer update invocation. So, if the
           * system "locks up while it's doing a lengthly update" there
           * will always be an actual developer line of code responsible
           * for that, not some mysteriously event-triggered call.
           *
           *
           */
         
          int eventId = DOM.eventGetType(event);
          /* Note that a click that closes a modal DialogBox can
           * generate a mouse location change without an ONMOUSEMOVE,
           * and a point that moves under the mouse due to an update
           * can generate a mouseover without a MOUSEMOVE */
          boolean isClick = (Event.ONCLICK == eventId);
          if ((Event.ONMOUSEMOVE == eventId ||
               Event.ONMOUSEOVER == eventId || isClick) &&
              !isOverOpenedHoverAnnotation(event)) {
             // remember last "tracked" mouse location
//             if (Event.ONCLICK == eventId)
//                Window.alert("CLICK: event.getClientX()=" + event.getClientX() +
//                             " event.getClientY()=" + event.getClientY() +
//                             " event.getTarget()==this.getElement() is " +
//                             (event.getTarget() == this.getElement()) +
//                             " event.getCurrentTarget()="+event.getCurrentTarget() +
//                             " event.getTarget()=" + event.getTarget());
//             else if (Event.ONMOUSEOVER == eventId)
//                Window.alert("MOUSEOVER: event.getClientX()=" + event.getClientX() +
//                             " event.getClientY()=" + event.getClientY() +
//                             " event.getCurrentTarget()="+event.getCurrentTarget() +
//                             " event.getTarget()=" + event.getTarget());
             if (getHoverTouchingEnabled() || isClick) {
                setClientX(event.getClientX(), isClick);
                setClientY(event.getClientY(), isClick);
                if (!isUpdateNeeded() &&
                    touchObjectAtMousePosition(isClick))
                   assembleChart();
             }
          }
          else if (Event.ONMOUSEOUT == eventId &&
                   getHoverTouchingEnabled() &&
                   takesUsCompletelyOutsideChart(event)) {
//                Window.alert("MOUSEOUT: event.getClientX()=" + event.getClientX() +
//                             " event.getClientY()=" + event.getClientY() +
//                             " event.getCurrentTarget()="+event.getCurrentTarget() +
//                             " event.getTarget()=" + event.getTarget());
             setClientX(GChart.NAI, false); // mouse not over chart,
             setClientY(GChart.NAI, false); // so position is undefined
             if (!isUpdateNeeded()  && touchObjectAtMousePosition())
                assembleChart();
          }
         
       }

      
    
     // Is chart's DOM rendering consistent with its specs?
     boolean isValidated() {
        boolean result = true;
        for (int i = 0; result && i < curves.size(); i++)
           result = curves.get(i).isValidated();
        return result;
     }

     /*
      * Returns number of "rendering panels" that there actually
      * are right now.
      *
      * GChart's implementation assures that this number is exactly the
      * same for the graphics and annotation rendering panels.
      *
      */ 
     int getRenderingPanelCount() {
        int result = graphicsPanel.getWidgetCount();
        return result;
     }
    int getXChartSize() {return xChartSize;}
    int getYChartSize() {return yChartSize;}
    // quickly returns decorated xChartsize as of the last plotPanel.reset
    int getXChartSizeDecoratedQuickly() {
       int result = xChartSize +
                    yAxisEnsembleWidth +
                    y2AxisEnsembleWidth +
                    chartLegendThickness;
       return result;
    }
   
    // quickly returns decorated yChartsize as of the last plotPanel.reset
    int getYChartSizeDecoratedQuickly() {
       int result = yChartSize +
                    xAxisEnsembleHeight +
                    topMargin +
                    chartFootnotesThickness;
       return result;
    }


   } // end of class PlotPanel
  
  
   // axis types (used to define which y-axis each curve is on)
   private static class YAxisId {}
 
   // case-independent index of next "break" tag in string (case of HTML
   // returned from HasHTML.getHTML can change with browser)
   private static int indexOfBr(String s, int iStart) {
     final String BR1 = "<br>";
     final String BR2 = "<BR>";
     final String BR3 = "<li>"// recognize <li> as a break.
     final String BR4 = "<LI>";
     final String BR5 = "<tr>"// recognize <tr> as a break.
     final String BR6 = "<TR>";
     int iBr1 = s.indexOf(BR1, iStart);
     int iBr2 = s.indexOf(BR2, iStart);
     int iBr3 = s.indexOf(BR3, iStart);
     int iBr4 = s.indexOf(BR4, iStart);
     int iBr5 = s.indexOf(BR5, iStart);
     int iBr6 = s.indexOf(BR6, iStart);
     int result1 = 0;
     int result2 = 0;
     int result3 = 0;
     int result = 0;

     if (-1 == iBr1)
        result1 = iBr2;
     else if (-1 == iBr2)
        result1 = iBr1;
     else
        result1 = Math.min(iBr1, iBr2);

     if (-1 == iBr3)
        result2 = iBr4;
     else if (-1 == iBr4)
        result2 = iBr3;
     else
        result2 = Math.min(iBr3, iBr4);

     if (-1 == iBr5)
        result3 = iBr6;
     else if (-1 == iBr6)
        result3 = iBr5;
     else
        result3 = Math.min(iBr5, iBr6);


    
     if (-1 == result1)
        result = result2;
     else if (-1 == result2)
        result = result1;
     else
        result = Math.min(result1, result2);


     if (-1 == result)
        result = result3;
     else if (-1 != result3)
        result = Math.min(result, result3);

    
     return result;
    
   }
   private static int indexOfBr(String s) {
      return indexOfBr(s, 0);
   }
  
// Provides a character-based width estimate when simple tags
// such as <b> and <i> are present in a multi-line,
// "break"-delimited, string. Very approximate, but a useful
// default.
private static int htmlWidth(String sIn) {
   int iBr = indexOfBr(sIn);
   String s = (-1 == iBr) ? sIn : sIn.substring(0, iBr);
   final String LITERAL_PAT = "[&][#a-zA-Z]+[;]";
   s = s.replaceAll(LITERAL_PAT, "X"); // literals count as 1 char
   final String TAG_PAT = "[<][^>]+[>]";
   s = s.replaceAll(TAG_PAT, "");   // tags don't count at all
   return s.length();
}

// number of <br> delimited lines in an HTML string
private static int htmlHeight(String s) {
   final int BR_LEN = "<br>".length();
   int iBr = 0;
   int result = 1;
   if (null != s) {
     for (iBr = indexOfBr(s);
          iBr != -1;
          iBr = indexOfBr(s, iBr+BR_LEN))
        result++;
   }
   return result;

}

  /*
   * Annotates (labels) a chart symbol. Users access this class via
   * wrapper methods of the Point class, and via various tick-label
   * related methods of the Axis class.
   *
   */
 
   static class Annotation {
     String fontColor = DEFAULT_FONT_COLOR;
     int fontSize = DEFAULT_ANNOTATION_FONTSIZE;
     String fontStyle = "normal";
     String fontWeight = "normal";
     AnnotationLocation location = null;
     String text = null;
     Widget widget = null;    // may be used in lieu of text or HTML
     boolean isVisible = true;
     int xShift = 0;
     int yShift = 0;
     boolean isHTML = false; // no break tags ==> plain text
     // Estimated number of lines, width in chars, of annotation
     // text (not used by Widgets)
     int numberOfLinesHigh = 0;
     int numberOfCharsWide = 0;
     int widthUpperBound = GChart.NAI;
     int heightUpperBound = GChart.NAI;
     static final int HTML_LEN = "<html>".length();
     static final int BR_LEN = "<br>".length();

     /*
      * Computes parameters used to estimate the width and height
      * of the (invisible) enclosing 1x1 Grid of an annotation
      * (used to align, center, etc. the annotation) <p>
      *
      */
     private String analyzeHTML(String s) {
       String result = null;   
       if (null == s) {
          isHTML = false;
          numberOfLinesHigh = 0;
          numberOfCharsWide = 0;
       }
       else if (!s.startsWith("<html>")) { // no html==>plain text
          isHTML = false;
          numberOfLinesHigh = 1;
          numberOfCharsWide = s.length();
          result = s;
       }
       else // HTML
          isHTML = true;
          // <html> is just a flag, not a tag, so strip it out.
          result = s.substring(HTML_LEN);
          if (widthUpperBound == GChart.NAI)
             numberOfCharsWide = htmlWidth(result);
                       
          if (heightUpperBound == GChart.NAI)
             numberOfLinesHigh = htmlHeight(result);
            
       }
       return result;
      
     }

     // Returns number of chars in first <br>-delimited line of
     // given string. A very crude way to estimate (especially
     // HTML) width in characters, but user can give explicit
     // widths when the width estimates based on this char width
     // heuristic fail them.
     static int getNumberOfCharsWide(String s) {
        int result = 0;
        if (!s.startsWith("<html>")) {
           result = s.length();
        }
        else {
            result = htmlWidth(s);
        }
        return result;
     }    
     public String getFontColor() {
       return fontColor;
     }
     public int getFontSize() {
        return fontSize;
     }
    
     public AnnotationLocation getLocation() {
        return location;
     }
    
     boolean isHTML() {return isHTML;}

     public String getText() {
        return (isHTML? ("<html>" + text) : text);
     }

     public boolean getVisible() {
       return isVisible;
     }
     public int getXShift() {
       return xShift;
     }
     public int getYShift() {
       return yShift;
     }
    
     public void setFontColor(String cssColor) {
        this.fontColor = cssColor;
     }
     public void setFontSize(int fontSize) {
        this.fontSize = fontSize;
     }
     public void setFontWeight(String cssWeight) {
        this.fontWeight = cssWeight;
     }
     public void setFontStyle(String cssStyle) {
        this.fontStyle = cssStyle;
     }

     String getFontWeight() { return fontWeight; }
     String getFontStyle() { return fontStyle; }

     public void setLocation(AnnotationLocation location) {
        this.location = location;
     }

     public void setText(String text, int widthUpperBound,
                         int heightUpperBound) {
        this.widthUpperBound = widthUpperBound;
        this.heightUpperBound = heightUpperBound;
        this.text = analyzeHTML(text);
        this.widget = null;
     }
     public void setText(String text) {
        setText(text, GChart.NAI, GChart.NAI);
     }

     public void setVisible(boolean isVisible) {
        this.isVisible = isVisible;
     }

     public void setWidget(Widget widget, int widthUpperBound,
                         int heightUpperBound) {
        this.widthUpperBound = widthUpperBound;
        this.heightUpperBound = heightUpperBound;
        this.text = null;
        this.widget = widget;
     }
     public void setWidget(Widget widget) {
       setWidget(widget, DEFAULT_WIDGET_WIDTH_UPPERBOUND,
                 DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
   }
     public Widget getWidget() {return widget;}
    
     public void setXShift(int xShift) {
        this.xShift = xShift;
     }
     public void setYShift(int yShift) {
        this.yShift = yShift;
     }
    
     int getHeightUpperBound() {
        int result = 0;
        if (heightUpperBound != GChart.NAI)
           result = heightUpperBound;
        else {
           result = (int) Math.ceil(fontSize *
                                    numberOfLinesHigh *
                                    CHARHEIGHT_TO_FONTSIZE_UPPERBOUND);
        }
        return result;
     }

     int getWidthUpperBound() {
        int result = 0;
        if (widthUpperBound != GChart.NAI)
           result = widthUpperBound;
        else {
           result = (int) Math.ceil(fontSize *
                    numberOfCharsWide * CHARWIDTH_TO_FONTSIZE_UPPERBOUND);
        }
        return result;
     }
    
  } // end of class Annotation

   /** Default size, in pixels, of text used to annotate individual
    ** plotted points on a curve.
    **
    ** @see Curve.Point#setFontSize Point.setFontSize
     */
   public static final int DEFAULT_ANNOTATION_FONTSIZE = 12;

   /**
    * Default pixel height of rectangular "brush" that defines
    * how close the mouse cursor must be to a rendered symbol for
    * it to be "touched" (which pops up its hover feedback).
    *
    * @see Symbol#setBrushHeight setBrushHeight
    * @see Symbol#setBrushWidth setBrushWidth
    * @see #DEFAULT_BRUSH_WIDTH DEFAULT_BRUSH_WIDTH
    */
   public static final int DEFAULT_BRUSH_HEIGHT = 1;
   /**
    *
    * Default pixel width of rectangular "brush" that defines how
    * close the mouse cursor must be to a rendered symbol for it
    * to be "touched" (which pops up its hover feedback).
    *
    * @see Symbol#setBrushHeight setBrushHeight
    * @see Symbol#setBrushWidth setBrushWidth
    * @see #DEFAULT_BRUSH_HEIGHT DEFAULT_BRUSH_HEIGHT
    */
   public static final int DEFAULT_BRUSH_WIDTH = 1;
  
   /** Default color of border around the chart legend
    **
    ** @see #setLegendBorderColor setLegendBorderColor
    **
    **/
   public static final String DEFAULT_LEGEND_BORDER_COLOR = "black";
   /** Default width of border around the chart legend
    **
    ** @see #setLegendBorderWidth setLegendBorderWidth
    **
    **/
   public static final int DEFAULT_LEGEND_BORDER_WIDTH = 1;
   /** Default style of border around the chart legend
    **
    ** @see #setLegendBorderStyle setLegendBorderStyle
    **
    **/
   public static final String DEFAULT_LEGEND_BORDER_STYLE = "solid";

   /** Default color of background of the chart legend
    **
    ** @see #setLegendBackgroundColor setLegendBackgroundColor
    **
     */
   public static final String DEFAULT_LEGEND_BACKGROUND_COLOR = "transparent";
  /**
   ** The default color of any text appearing in a chart's
   ** legend, annotations, or tick labels.
   **
   ** @see #setLegendFontColor setLegendFontColor
   ** @see Axis#setTickLabelFontColor setTickLabelFontColor
   ** @see Curve.Point#setAnnotationFontColor setAnnotationFontColor
   **
   **/
   public final static String DEFAULT_FONT_COLOR ="black";
   /**
    ** Default style of axis label and legend fonts.
    **
    ** @see #setLegendFontStyle setLegendFontStyle
    ** @see Axis#setTickLabelFontStyle setTickLabelFontStyle
    ** @see Curve.Point#setAnnotationFontStyle
    ** setAnnotationFontStyle
    **
    **/
   public static final String DEFAULT_FONT_STYLE = "normal";
   /** Default weight of axis label and legend fonts.
    **
    ** @see #setLegendFontWeight setLegendFontWeight
    ** @see Axis#setTickLabelFontWeight setTickLabelFontWeight
    ** @see Curve.Point#setAnnotationFontWeight
    ** setAnnotationFontWeight
    **
    **/
  
   public static final String DEFAULT_FONT_WEIGHT = "normal";

/**
    ** The default template string used to generate the hovertext
    ** displayed when the user hovers their mouse above a point
    ** on a curve (pie slices have a different default).
    **
    ** @see Symbol#setHovertextTemplate setHovertextTemplate
    ** @see #DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
    **    DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
    **   
    */
   public static final String DEFAULT_HOVERTEXT_TEMPLATE =
      GChart.formatAsHovertext("(${x}, ${y})");
   /**
    ** The default hover feedback location used to position the
    ** hover feedback when the user hovers their mouse above a point
    ** on a curve (pie slices, and bar symbols have different
    ** defaults).
    **
    ** @see Symbol#setHoverLocation setHoverLocation
    ** @see #DEFAULT_PIE_SLICE_HOVER_LOCATION DEFAULT_PIE_SLICE_HOVER_LOCATION
    ** @see #DEFAULT_VBAR_BASELINE_HOVER_LOCATION DEFAULT_VBAR_BASELINE_HOVER_LOCATION
    ** @see #DEFAULT_VBARBOTTOM_HOVER_LOCATION DEFAULT_VBARBOTTOM_HOVER_LOCATION
    ** @see #DEFAULT_VBARTOP_HOVER_LOCATION DEFAULT_VBARTOP_HOVER_LOCATION
    ** @see #DEFAULT_HBAR_BASELINE_HOVER_LOCATION DEFAULT_HBAR_BASELINE_HOVER_LOCATION
    ** @see #DEFAULT_HBARLEFT_HOVER_LOCATION DEFAULT_HBARLEFT_HOVER_LOCATION
    ** @see #DEFAULT_HBARRIGHT_HOVER_LOCATION DEFAULT_HBARRIGHT_HOVER_LOCATION
    ** 
    */
   public static final AnnotationLocation DEFAULT_HOVER_LOCATION =
      AnnotationLocation.NORTHWEST;
   /** The default fontsize of text that appears
    ** in the chart's legend (key).
    **
    ** @see Axis#setTickLabelFontSize setTickLabelFontSize
    ** @see #getXAxis getXAxis
    ** @see #getYAxis getYAxis
    ** @see #getY2Axis getY2Axis
    **
    **/
   public final static int DEFAULT_LEGEND_FONTSIZE = 12;


   /**
    ** The default background color used for the chart's plot area
    ** if none is specified.
    **
    ** @see #setPlotAreaBackgroundColor setPlotAreaBackgroundColor
    **
    **/
   public final static String DEFAULT_PLOTAREA_BACKGROUND_COLOR = "transparent";
   /**
    ** The default border color used for the chart's plot area
    ** if none is specified.
    **
    ** @see #setPlotAreaBorderColor setPlotAreaBorderColor
    **
    **/
   public final static String DEFAULT_PLOTAREA_BORDER_COLOR = "black";
   /**
    ** The default style of the border around the chart's plot area
    ** if none is specified.
    **
    ** @see #setPlotAreaBorderStyle setPlotAreaBorderStyle
    **
    **/
   public final static String DEFAULT_PLOTAREA_BORDER_STYLE = "solid";
   /**
    ** The default width of the border around the chart's plot area
    ** if none is specified.
    **
    ** @see #setPlotAreaBorderWidth setPlotAreaBorderWidth
    **
    **/
   public final static int DEFAULT_PLOTAREA_BORDER_WIDTH = 0;
   /**
    ** The default CSS background color used for symbols if none is
    ** specified.
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setBackgroundColor setBackgroundColor
    **/
   public static final String DEFAULT_SYMBOL_BACKGROUND_COLOR =
      "transparent";
   /**
    ** The default CSS border colors used for symbols if none are
    ** specified. These defaults are, in order of the curve's
    ** integer index: red, green, blue, fuchsia, aqua, teal,
    ** maroon, lime, navy, silver, olive, purple.  This sequence
    ** repeats if there are more than 12 curves.
    ** <p>
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setBorderColor setBorderColor
    **
    **/

   public static final String[] DEFAULT_SYMBOL_BORDER_COLORS =
     {"red", "green", "blue",
      "fuchsia", "aqua", "teal",
      "maroon", "lime", "navy",
      "silver", "olive", "purple"};
   private static String[] defaultSymbolBorderColors =
      DEFAULT_SYMBOL_BORDER_COLORS;
  
   /**
    ** The default CSS border style used for symbols if none is
    ** specified; this default is "solid".
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setBorderStyle setBorderStyle
    **
    **/
   public static final String DEFAULT_SYMBOL_BORDER_STYLE = "solid";
   /**
    ** The default CSS border width used for symbols if none is
    ** specified; this default is 1 pixel.
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setBorderWidth setBorderWidth
    **
    **/
   public static final int DEFAULT_SYMBOL_BORDER_WIDTH = 1;
   /**
    ** The default spacing between discrete, rectangular, elements
    ** used to simulate continuous graphical elements. This
    ** default does not apply to bar chart symbol types or
    ** the LINE symbol type, which have their own default
    ** fill spacings.
    ** <p>
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    ** @see #DEFAULT_BAR_FILL_SPACING
    **      DEFAULT_BAR_FILL_SPACING
    ** @see #DEFAULT_LINE_FILL_SPACING
    **      DEFAULT_LINE_FILL_SPACING
    **
    **/
   public static final double DEFAULT_SYMBOL_FILL_SPACING = 4;
   /**
    ** The default "thickness" of the rectangular elements used to
    ** simulate continuous graphical objects, such as connecting
    ** lines in line charts. This default applies to all symbol
    ** types <tt>except</tt> for those representing pie slices,
    ** whose default is
    ** <tt>DEFAULT_PIE_SLICE_FILL_THICKNESS</tt>, and the LINE
    ** symbol type, whose default is DEFAULT_LINE_FILL_THICKNESS.
    **
    ** <p> Since this default thickness is 0 px, this implies
    ** that, except for pie slices and lines, no such continuous fill
    ** elements are generated by default. For example, if you
    ** want to have dotted connecting lines drawn between individual
    ** data points represented using the <tt>BOX_CENTER</tt>
    ** symbol type, you must explicitly specify a positive fill
    ** thickness (for solid connecting lines, the LINE symbol
    ** is usually far more efficient than using a fill thickness
    ** of 1px with the BOX_CENTER symbol).
    **
    ** @see #DEFAULT_PIE_SLICE_FILL_THICKNESS
    **      DEFAULT_PIE_SLICE_FILL_THICKNESS
    ** @see #DEFAULT_LINE_FILL_THICKNESS
    **      DEFAULT_LINE_FILL_THICKNESS
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    **
    **/
   public static final int DEFAULT_SYMBOL_FILL_THICKNESS = 0;


   /**
    ** The default spacing between discrete, rectangular, elements
    ** used to simulate continuous filling of polygonal regions
    ** formed by connecting corresponding ends of successive
    ** bars in a bar chart.
    ** <p>
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    ** @see #DEFAULT_SYMBOL_FILL_SPACING DEFAULT_SYMBOL_FILL_SPACING
    **
    **/
   public static final double DEFAULT_BAR_FILL_SPACING = 0;
  
   /**
    ** The default thickness of connecting lines drawn on
    ** curves whose symbols have the LINE symbol type.
    **
    ** @see #DEFAULT_SYMBOL_FILL_THICKNESS
    **      DEFAULT_SYMBOL_FILL_THICKNESS
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    **
    **/
   public static final int DEFAULT_LINE_FILL_THICKNESS = 1;

  
   /**
    ** The default spacing between discrete, rectangular, elements
    ** used to simulate continuously connected lines between
    ** successive points on a curve that uses the
    ** <tt>LINE</tt> symbol type.
    ** <p>
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    ** @see #DEFAULT_SYMBOL_FILL_SPACING DEFAULT_SYMBOL_FILL_SPACING
    **
    **/
   public static final int DEFAULT_LINE_FILL_SPACING = 0;

   /**
    ** The default "spacing" between corresponding edges of the
    ** rectangular elements used to simulate continuous fill of pie
    ** slices.  <p>
    **
    ** @see #DEFAULT_SYMBOL_FILL_SPACING
    **      DEFAULT_SYMBOL_FILL_SPACING
    ** @see #DEFAULT_PIE_SLICE_FILL_THICKNESS
    **      DEFAULT_PIE_SLICE_FILL_THICKNESS
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    **
    **/
   public static final double DEFAULT_PIE_SLICE_FILL_SPACING = 4;
   /**
    ** The default "thickness" of the rectangular elements
    ** used to simulate continuous fill of pie slices. This
    ** thickness defines the height of horizontal pie slice
    ** shading bars, and the width of vertical shading bars.
    ** <p>
    **
    ** @see #DEFAULT_SYMBOL_FILL_THICKNESS
    **      DEFAULT_SYMBOL_FILL_THICKNESS
    ** @see #DEFAULT_LINE_FILL_THICKNESS
    **      DEFAULT_LINE_FILL_THICKNESS
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    **
    **/
   public static final int DEFAULT_PIE_SLICE_FILL_THICKNESS = 2;

   /**
    ** The default hovertext template used by symbols when they have a
    ** symbol type of of the form PIE_SLICE_*.
    **
    ** @see Symbol#setHovertextTemplate setHovertextTemplate
    ** @see SymbolType#PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
    ** @see #DEFAULT_HOVERTEXT_TEMPLATE DEFAULT_HOVERTEXT_TEMPLATE
    **
    **/
   public static final String DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE =
      GChart.formatAsHovertext("${pieSliceSize}");

   /**
    ** The default hover feedback location used by symbols when they have a
    ** symbol type of of the form PIE_SLICE_*.
    **
    ** @see Symbol#setHoverLocation setHoverLocation
    ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
    **
    **/
   public static final AnnotationLocation DEFAULT_PIE_SLICE_HOVER_LOCATION =
     AnnotationLocation.OUTSIDE_PIE_ARC;
   /**
    ** The default height (including borders) used for
    ** symbols if none is specified; this default is
    ** the same as for <tt>DEFAULT_SYMBOL_WIDTH</tt>
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setHeight setHeight
    ** @see #DEFAULT_SYMBOL_WIDTH DEFAULT_SYMBOL_WIDTH
    **
    **/
   public static final int DEFAULT_SYMBOL_HEIGHT = 7;
  
/**
    ** The default symbol type for curve if none is
    ** specified; this default is BOX_CENTER
    **
    ** @see SymbolType#BOX_CENTER BOX_CENTER
    ** @see Symbol#setSymbolType setSymbolType
    **
    **/
   public static final SymbolType DEFAULT_SYMBOL_TYPE = SymbolType.BOX_CENTER;

  /**
    ** The default width (including borders) used for
    ** symbols if none is specified.
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setWidth setWidth
    ** @see #DEFAULT_SYMBOL_WIDTH DEFAULT_SYMBOL_WIDTH
    **/
   public static final int DEFAULT_SYMBOL_WIDTH =
     DEFAULT_SYMBOL_HEIGHT;
  
   /**
   * The default number of tick marks on each Axis.
   *
   * @see Axis#setTickCount setTickCount
   *
   */
  public static final int DEFAULT_TICK_COUNT = 10;

  /** The default color (a CSS color specification) of tick labels
   **
   ** @see Axis#setTickLabelFontColor setTickLabelFontColor
   ** @see #getXAxis getXAxis
   ** @see #getYAxis getYAxis
   ** @see #getY2Axis getY2Axis
   **/
   public final static String DEFAULT_TICK_LABEL_FONT_COLOR ="black";

  /** The default CSS font-style applied to tick labels
   **
   ** @see Axis#setTickLabelFontStyle setTickLabelFontStyle
   ** @see #getXAxis getXAxis
   ** @see #getYAxis getYAxis
   ** @see #getY2Axis getY2Axis
   **/
   public final static String DEFAULT_TICK_LABEL_FONT_STYLE ="normal";

  /** The default CSS font-weight applied to tick labels
   **
   ** @see Axis#setTickLabelFontWeight setTickLabelFontWeight
   ** @see #getXAxis getXAxis
   ** @see #getYAxis getYAxis
   ** @see #getY2Axis getY2Axis
   **/
   public final static String DEFAULT_TICK_LABEL_FONT_WEIGHT ="normal";

  /** The default fontsize (in pixels) of tick labels
    **
    ** @see Axis#setTickLabelFontSize setTickLabelFontSize
    ** @see #getXAxis getXAxis
    ** @see #getYAxis getYAxis
    ** @see #getY2Axis getY2Axis
    **/
   public final static int DEFAULT_TICK_LABEL_FONTSIZE = 12;
  /**
    ** The default GWT <tt>NumberFormat</tt> format string used to convert
    ** numbers to the text strings displayed as tick labels
    ** on X, Y, and Y2 axes.
    **
    ** @see Axis#setTickLabelFormat setTickLabelFormat
    ** @see #getXAxis getXAxis
    ** @see #getYAxis getYAxis
    ** @see #getY2Axis getY2Axis
    **
    **/
   public final static String DEFAULT_TICK_LABEL_FORMAT = "#.##";

   /**
   * The default length of tick marks, in pixels.
   *
   * @see Axis#setTickLength setTickLength
   */
  public static final int DEFAULT_TICK_LENGTH = 6;


   /**
   * The default tick location.
   *
   * @see Axis#setTickLocation setTickLocation
   */
  public static final TickLocation DEFAULT_TICK_LOCATION = TickLocation.OUTSIDE;

 
   /**
   * The default thickness of tick marks, in pixels.
   *
   * @see Axis#setTickThickness setTickThickness
   */
  public static final int DEFAULT_TICK_THICKNESS = 1; // pixel

   /**
    ** The default location used to position the hover feedback
    ** when the user hovers their mouse above a point on a curve
    ** that uses a VBAR_BASELINE_* symbol type.
    **
    ** @see Symbol#setHoverLocation setHoverLocation
    ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
    **   
    */
   public static final AnnotationLocation DEFAULT_VBAR_BASELINE_HOVER_LOCATION =
      AnnotationLocation.FARTHEST_FROM_HORIZONTAL_BASELINE;
   /**
    ** The default location used to position the hover feedback
    ** when the user hovers their mouse above a point on a curve
    ** that uses a VBAR_SOUTH* symbol type.
    **
    ** @see Symbol#setHoverLocation setHoverLocation
    ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
    **   
    */
   public static final AnnotationLocation DEFAULT_VBARBOTTOM_HOVER_LOCATION =
      AnnotationLocation.NORTH;

   /**
    ** The default location used to position the hover feedback
    ** when the user hovers their mouse above a point on a curve
    ** that uses a VBAR_NORTH* symbol type.
    **
    ** @see Symbol#setHoverLocation setHoverLocation
    ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
    **   
    */
   public static final AnnotationLocation DEFAULT_VBARTOP_HOVER_LOCATION =
      AnnotationLocation.SOUTH;

   /**
    ** The default location used to position the
    ** hover feedback when the user hovers their mouse above a point
    ** on a curve that uses a HBAR_BASELINE_* symbol type.
    **
    ** @see Symbol#setHoverLocation setHoverLocation
    ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
    **   
    */
   public static final AnnotationLocation DEFAULT_HBAR_BASELINE_HOVER_LOCATION =
      AnnotationLocation.FARTHEST_FROM_VERTICAL_BASELINE;

   /**
    ** The default location used to position the
    ** hover feedback when the user hovers their mouse above a point
    ** on a curve that uses an HBAR_*WEST symbol type.
    **
    ** @see Symbol#setHoverLocation setHoverLocation
    ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
    **   
    */
   public static final AnnotationLocation DEFAULT_HBARLEFT_HOVER_LOCATION =
      AnnotationLocation.EAST;

   /**
    ** The default location used to position the
    ** hover feedback when the user hovers their mouse above a point
    ** on a curve that uses an HBAR_*EAST symbol type.
    **
    ** @see Symbol#setHoverLocation setHoverLocation
    ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
    **   
    */
   public static final AnnotationLocation DEFAULT_HBARRIGHT_HOVER_LOCATION =
      AnnotationLocation.WEST;

  /**
   ** The default upper bound on the width of widgets used
   ** in annotations and tick labels that GChart
   ** will assume for centering and similar alignment purposes.
   **
   ** @see Curve.Point#setAnnotationWidget setAnnotationWidget
   ** @see Axis#addTick(double,Widget,int,int) addTick
   **
   **/
  public static final int DEFAULT_WIDGET_WIDTH_UPPERBOUND = 400;
  /**
   ** The default upper bound on the height of widgets used
   ** in annotations and tick labels that GChart
   ** will assume for centering and similar alignment purposes.
   **
   ** @see Curve.Point#setAnnotationWidget setAnnotationWidget
   ** @see Axis#addTick(double,Widget,int,int) addTick
   **
   **/
  public static final int DEFAULT_WIDGET_HEIGHT_UPPERBOUND = 400;
 
  /**
   *  The default width of the area of the chart in
   *  which curves are displayed, in pixels.
   */
  public final static int DEFAULT_X_CHARTSIZE = 300; // pixels
  /**
   * The default height of the area of the chart in
   * which curves are displayed, in pixels.
   */
  public final static int DEFAULT_Y_CHARTSIZE = 300; // pixels

   /**
    ** In analogy to how it uses <tt>Double.NaN</tt> (Not a
    ** Number), GChart uses <tt>GChart.NAI</tt> (Not an Integer) to
    ** represent integers whose values have not been explicitly
    ** specified.
    **
    **/
   public static final int NAI = Integer.MIN_VALUE;

   /**
    * Due to a well-known bug (see, for example, <a
    * href="http://www.hedgerwow.com/360/dhtml/css-ie-transparent-border.html">
    * this explanation on Hedger Wang's blog</a>), though white
    * may not be black in IE6, transparent borders certainly are.
    * Besides this outright bug, different browsers define which
    * element's background color "shines through" a transparent border
    * differently. For example, in FF2, the background of the element
    * containing the border shines through, which makes setting the
    * border color to "transparent" equivalent to setting the border
    * color to equal the background color. In IE7, the color of
    * the chart's background "shines through"--which is more likely
    * what you intended when you set a symbol's border to transparent.
    * <p>
    *
    * To make it easy for you to eliminate such problems, and obtain a
    * consistently behaving transparent-border behavior cross-browser,
    * this special GChart-only "color" (recognized by all GChart border
    * color related methods <i>except</i>
    * <tt>GChart.setBorderColor</tt>) causes GChart to emulate a
    * transparent border by eliminating the border entirely (setting
    * it's width to 0) and changing the size and position of the element
    * so as to make it look like the border is still "taking up space".
    *
    * <p>
    *
    *
    * <blockquote><small> <i>Note:</i>The <tt>GChart.setBorderColor</tt>
    * method, which sets the color of the border around the entire
    * chart, does <i>not</i> support this keyword because GChart's
    * transparent border emulation relies on changing the size of, and
    * shifting the position of, the transparently bordered element. But,
    * the position of the GChart as a whole is determined not by GChart,
    * but by the enclosing page. Well-known CSS tricks, such as
    * described in the "hedgerwow" link above, can be used if you need a
    * truely transparent border around the entire chart. Or, just fake
    * it by setting the border color to equal the background color
    * of the containing page.  </small></blockquote>
    *
    * <p>
    *
    * This differs from setting the border color to "transparent" (which
    * you can still do should you need the "standard non-standard"
    * transparent border color behavior) in subtle ways that can matter
    * in special cases. For example, because the element is smaller than
    * it is with "transparent", if you draw your symbols outside the
    * chart rectangle, GChart will not be able to track the mouse moves
    * inside the transparent region (yes, this is a fine point, but
    * there could be other differences I'm not aware of). In almost
    * every other case I can think of, though, setting the border color
    * to this special keyword instead of "transparent" will be the
    * simplest way to eliminate these inconsistent transparent border
    * problems from your charts.
    * <p>
    *
    * @see Symbol#setBorderColor setBorderColor
    *
    */
  
   public static final String TRANSPARENT_BORDER_COLOR = null;

   /**
    ** A special value used to tell GChart that a property should
    ** be defined via CSS, not via an explicit Java API specification.
    **
    ** <p>
    **
    ** This value is mainly used by GChart's "CSS convenience methods"
    ** which make it possible to use the Java API to specify
    ** certain properties of a GChart that can also be specified
    ** via CSS. When the value of the Java property is set to
    ** <tt>USE_CSS</tt> GChart ignores the API specification
    ** and allows the standard "CSS cascade" to define the
    ** property.
    **
    ** The discussion below elaborates on why GChart
    ** supports CSS convenience methods, and how the <tt>USE_CSS</tt>
    ** keyword fits into that support.
    **
    ** <p>
    **
    ** <blockquote><small>
    ** <b>CSS Convenience Methods and the
    ** <tt>GChart.USE_CSS</tt> keyword</b>
    ** <p>
    **
    ** Like all GWT Widgets, a GChart is both an object in a Java application,
    ** and an HTML element in a web page.
    **
    ** <p>
    ** This duality naturally divides the properties of a GChart into
    ** three categories:
    **
    ** <ol>
    **
    **  <li>Those you can access only via the Java API.
    **  <li>Those you can access only via CSS and the DOM.
    **  <li>Those you can access both ways.
    ** </ol>
    ** <p>
    **
    ** I've used the following criteria to determine the access method
    ** appropriate for a given GChart property:
    ** <p>
    **
    ** <ol>
    **
    ** <li> Those properties that mainly define the chart
    **      itself--independent of its relationship to any containing web
    **      page--are exclusively accessed via the Java API
    **
    ** <p>
    **
    ** For example, the x,y data values of a curve have everything to do
    ** with the chart itself and nothing to do with the enclosing web
    ** page, so all the defining x,y data of a curve can only be
    ** accessed via the Java API.
    ** <p>
    **
    ** <li>Those properties that mainly define how the chart fits into
    ** the enclosing web page and have nothing to do with the chart
    ** itself are accessed exclusively via CSS stylesheets or the
    ** GWT DOM class.
    **
    ** <p> For example, how big of a margin should be placed around a
    ** GChart is only about how the GChart fits into the enclosing web
    ** page, so you must define a GChart's margins using a CSS
    ** stylesheet (or via the GWT DOM class)--there is no
    ** <tt>GChart.setMargin</tt> method.
    **
    ** <li>Finally, those properties that, in some situations are
    ** best viewed as part of the chart itself, and in other
    ** situations as defining how the chart fits into the enclosing
    ** web page can be accessed <i>either</i> via the Java API, or
    ** via CSS/DOM.  <p>
    **
    ** For example, if you are focused on assuring that the chart has
    ** a the same border as every other element on the page, the
    ** border around the chart as a whole can be viewed as relating
    ** to how the chart fits into the enclosing web page. On the
    ** other hand, if you are focused on assuring that, like the
    ** frame around a picture, the border looks good around that
    ** particular chart, it makes more sense to view it as a part of
    ** the chart itself.
    **
    ** </ol>
    **
    ** <p>
    **
    ** The Java API methods for GChart properties in this third
    ** category are known as "CSS convenience methods" because, though
    ** you could do the same thing by exploiting the "GChart as HTML
    ** element" perspective, these methods save you the trouble of
    ** looking up CSS syntax, splitting up your chart's specification
    ** between Java code and a CSS stylesheet, invoking a rather
    ** hard-to-remember method call in the GWT DOM class, etc.
    **
    ** <p> Specifications made via the GChart Java API always take
    ** precedence over those made via stylesheets or the DOM class.
    ** To instruct GChart that you want one of these properties to be
    ** defined via CSS or the DOM, set the associated Java API
    ** property to the special value <tt>GChart.USE_CSS</tt>.
    **
    ** <p> Fortunately, since <tt>USE_CSS</tt> is the default value for
    ** every one of these CSS convenience properties, if you never use the
    ** Java API to set them, you can use CSS to control them
    ** just as you would for a GWT widget that did not support
    ** convenience properties.
    **
    **
    ** Unfortunately, these CSS defaults rarely produce a great
    ** looking chart out of the box; the example CSS snippet
    ** below defines all of these convenience properties and
    ** attaches them to GChart's default CSS selector (aka
    ** stylename) in a way that I think looks better. A comment
    ** to the right of each line contains the corresponding
    ** CSS convenience-method call that has the same effect.
    **
    ** <p>
    ** <pre>
    ** .gchart-GChart {
    **   background-color: #DDF;   /* setBackgroundColor("#DDF"); *&#47;
    **   border-width: 1px;         /* setBorderWidth("1px"); *&#47;
    **   border-color: black;       /* setBorderColor("black"); *&#47;
    **   border-style: solid;       /* setBorderStyle("solid");  *&#47;
    **   font-family: Arial, sans-serif;  /* setFontFamily("Arial, sans-serif"); *&#47;
    ** }
    ** </pre>
    **
    **
    ** Note that certain CSS convenience methods that could in
    ** principle have been added, such as those for defining the
    ** background image of a chart, were omitted because I
    ** thought they would almost never be used. Of course, you
    ** can always access these CSS properties "the old fashioned
    ** way" (via a CSS specification or methods of the GWT DOM class).
    **
    ** <p>
    ** </small></blockquote>
    **
    ** @see #setBorderColor(String) setBorderColor
    ** @see #setBorderStyle(String) setBorderStyle
    ** @see #setBackgroundColor(String) setBackgroundColor
    ** @see #setBorderWidth(String) setBorderWidth
    ** @see #setFontFamily(String) setFontFamily
    **
    **
    **
    **/
    /*
     * Setting a CSS property to "" generally clears the
     * attribute specification, restoring things to their initial
     * defaults (not sure if this always works, but it appears to
     * so far).
     *
     */
    public final static String USE_CSS = "";

  /**
   ** Keyword used to indicate that a curve should be
   ** displayed on the left y-axis.
   **
   ** @see #Y2_AXIS Y2_AXIS
   ** @see GChart.Curve#setYAxis(GChart.YAxisId) setYAxis
   **
   **/
   public static final YAxisId Y_AXIS = new YAxisId();

   /**
   ** Keyword used to indicate that a curve should be
   ** displayed on the right (the so-called y2) y-axis.
   **
   ** @see #Y_AXIS Y_AXIS
   ** @see Curve#setYAxis setYAxis
   **
   **/
   public static final YAxisId Y2_AXIS = new YAxisId();

  /**
   ** The default URL GChart will use to access the blank image
   ** (specifically, a 1 x 1 pixel transparent GIF) it requires
   ** to prevent "missing image" icons from appearing in your
   ** charts.
   ** 
   ** @see #setBlankImageURL setBlankImageURL
   **
   **/
  public final static String DEFAULT_BLANK_IMAGE_URL = "gchart.gif";
  /**
   ** The full path to the default GChart blank image
   ** (specifically, a 1 x 1 pixel transparent GIF) it requires
   ** to prevent "missing image" icons from appearing in your
   ** charts.
   ** <p>
   **
   ** Convenience constant equal to:
   **
   ** <pre>
   ** GWT.getModuleBaseURL()+GChart.DEFAULT_BLANK_IMAGE_URL
   ** </pre>
   ** 
   ** @see #setBlankImageURL setBlankImageURL
   **
   **/
  public final static String DEFAULT_BLANK_IMAGE_URL_FULLPATH =
     GWT.getModuleBaseURL()+GChart.DEFAULT_BLANK_IMAGE_URL;
  private static final int DEFAULT_GRID_HEIGHT =
      DEFAULT_TICK_THICKNESS;  
  private static final int DEFAULT_GRID_WIDTH =
      DEFAULT_TICK_THICKNESS;  
  private static final String GRID_BORDER_STYLE = "solid";
  private static final int GRID_BORDER_WIDTH = 1;

  /** The default color used for all axes, gridlines, and ticks.
   **
   ** @see #setGridColor setGridColor
   **
   */
  public static final String DEFAULT_GRID_COLOR = "black";


  /** The default thickness (height) of the rectangular region at
   ** the bottom of the chart allocated for footnotes, per
   ** <tt>&lt;br&gt;</tt> or <tt>&lt;li&gt;</tt> delimited HTML line. This
   ** default is only used when the footnote thickness is set to
   ** <tt>GChart.NAI</tt> (the default).
   **
   ** @see #setChartFootnotesThickness setChartFootnotesThickness
   **
   */
  public static final int DEFAULT_FOOTNOTES_THICKNESS = 15;

  /**
   **
   ** The default thickness (height) of the rectangular region at
   ** the top of the chart allocated for the chart's title, per
   ** <tt>&lt;br&gt;</tt> or <tt>&lt;li&gt;</tt> delimited HTML line. This default
   ** is only used when the title thickness is set to
   ** <tt>GChart.NAI</tt>.
   **
   **
   ** @see #setChartTitleThickness setChartTitleThickness
   **
   */
  public static final int DEFAULT_TITLE_THICKNESS = 15;

 
 
// for purposes of estimating fixed space "band" around the plot
// panel reserved for the tick labels:
  private static final double
      TICK_CHARHEIGHT_TO_FONTSIZE_LOWERBOUND = 1.0;  
  // a bit larger than the 0.6 rule-of-thumb
  private static final double
      TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND = 0.7;
  // For estimating size of invisible "box" needed for alignment
  // purposes.  Note: when these are bigger, annotations remain
  // properly aligned longer as user zooms up font sizes.  But,
  // bigger bounding boxes can slow updates (not sure why,
  // maybe it's related to hit testing browser has to do)
  private static final double
        CHARHEIGHT_TO_FONTSIZE_UPPERBOUND = 2*1.5;
  private static final double
       CHARWIDTH_TO_FONTSIZE_UPPERBOUND = 2*0.7;

  private static final String TICK_BORDER_STYLE = GRID_BORDER_STYLE;
  private static final int TICK_BORDER_WIDTH = GRID_BORDER_WIDTH;

  private static void setBackgroundColor(
      UIObject uio, String cssColor) {
     DOM.setStyleAttribute(uio.getElement(),
                            "backgroundColor", cssColor);
  }

//  private static void setBackground(
//      UIObject uio, String cssColor) {
//     DOM.setStyleAttribute(uio.getElement(),
//                           "background", cssColor);
//  }
  
  private static void setBorderColor(
       UIObject uio, String cssColor) {
      DOM.setStyleAttribute(uio.getElement(),
                            "borderColor", cssColor);
   }




 
  private static void setBorderStyle(
       UIObject uio, String cssBorderStyle) {
      DOM.setStyleAttribute(uio.getElement(),
                            "borderStyle", cssBorderStyle);
   }
 

  private static void setBorderWidth(
       UIObject uio, String cssBorderWidth) {
      DOM.setStyleAttribute(uio.getElement(),
                            "borderWidth", cssBorderWidth);
   }

  private static void setBorderWidth(
      UIObject uio, int borderWidth) {
      if (borderWidth != GChart.NAI)
         setBorderWidth(uio, borderWidth + "px");
      else
         setBorderWidth(uio, "");
   }

  private static void setFontFamily(
       UIObject uio, String cssFontFamily) {
      DOM.setStyleAttribute(uio.getElement(),
                            "fontFamily", cssFontFamily);
  
 
  private static void setFontSize(
       UIObject uio, int fontSize) {
      DOM.setIntStyleAttribute(
         uio.getElement(), "fontSize", fontSize);
   }

  private static void setFontStyle(
       UIObject uio, String fontStyle) {
      DOM.setStyleAttribute(uio.getElement(),
         "fontStyle", fontStyle);
   }
  private static void setFontWeight(
       UIObject uio, String fontWeight) {
      DOM.setStyleAttribute(uio.getElement(),
         "fontWeight", fontWeight);
   }

  private static void setColor(
       UIObject uio, String cssColor) {
      DOM.setStyleAttribute(uio.getElement(),
         "color", cssColor);
  }

  // valid layout strings are fixed, auto, and inherit
//  private static void setTableLayout(
//       UIObject uio, String layout) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "table-layout", layout);
//  }
 

 
//   private static void setLineHeight(
//       UIObject uio, String cssLineHeight) {
//      DOM.setStyleAttribute(uio.getElement(),
//                            "lineHeight", cssLineHeight);
//   }

  //   private static void setTextAlign(
//       UIObject uio, String cssTextAlign) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "textAlign", cssTextAlign);
//   }
// 
//   private static void setMargin(
//       UIObject uio, String cssMargin) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "margin", cssMargin);
//   }
   private static void setPadding(
       UIObject uio, String cssPadding) {
      DOM.setStyleAttribute(uio.getElement(), "padding", cssPadding);
   }
   // valid choices are block, inline, list-item, or none
//   private static void setDisplay(
//       UIObject uio, String cssDisplay) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "display", cssDisplay);
//   }
   // choices are: visible, hidden, scroll or auto
   private static void setOverflow(
       UIObject uio, String cssOverflow) {
      DOM.setStyleAttribute(
         uio.getElement(), "overflow", cssOverflow);
   }
//   private static void setTextLeading(
//       UIObject uio, String cssTextLeading) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "textTrailing", cssTextLeading);
//   }
//   private static void setVerticalAlign(
//       UIObject uio, String cssVerticalAlign) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "verticalAlign", cssVerticalAlign);
//   }

   // returns the sign of the given number
   static int sign(double x) {
       int result = (x < 0) ? -1 : 1;
       return result;
   }

  // Validates multipliers used to simplify computing the
   // upper left corner location of symbols and labels to
   // properly reflect their alignment relative to the
   // plotted point or labeled symbol.
  static void validateMultipliers(
      int widthMultiplier, int heightMultiplier) {
      if (!(widthMultiplier == 0 || Math.abs(widthMultiplier)==1) &&
          !(heightMultiplier == 0 ||Math.abs(heightMultiplier)==1))
         throw new IllegalArgumentException(
          "widthMultiplier, heightMultiplier args must both be " +
          "either 0, 1, or -1");
  }

// is value within given limits, inclusive?
  static boolean withinRange(double x, double minLim, double maxLim) {
     // x!=x is a faster isNaN; NaN is considered in range
     boolean result = (x!=x) ? true : (x >= minLim && x <= maxLim);
     return result;
  }
 
  private Widget chartFootnotes;
  private boolean chartFootnotesLeftJustified;

// outer container needed so CSS-defined paddings don't interfere with positioning 
  private SimplePanel chartPanel = new SimplePanel();

  private String borderWidth = USE_CSS;
  private String borderStyle = USE_CSS;
  private String borderColor = USE_CSS;
  private String backgroundColor = USE_CSS;
  private static String blankImageURL = null;
  boolean chartDecorationsChanged = true;
  private Widget chartTitle;
  // collection of curves associated with this chart. 
  private ArrayList<Curve> curves = new ArrayList<Curve>();
  private String fontFamily = USE_CSS;
  private int footnotesThickness = GChart.NAI;
  private String legendBackgroundColor =
                             DEFAULT_LEGEND_BACKGROUND_COLOR;
  private String legendBorderColor = DEFAULT_LEGEND_BORDER_COLOR;
  private int legendBorderWidth = DEFAULT_LEGEND_BORDER_WIDTH;
  private String legendBorderStyle = DEFAULT_LEGEND_BORDER_STYLE;
  private int legendThickness = GChart.NAI;
 
  private boolean isLegendVisible = true;
  private String legendFontColor = DEFAULT_FONT_COLOR;
  private int legendFontSize = DEFAULT_LEGEND_FONTSIZE;
  private String legendFontStyle = DEFAULT_FONT_STYLE;
  private String legendFontWeight = DEFAULT_FONT_WEIGHT;

/*
  * Contains the plotting region, as well as axes, ticks, and
  * tick-labels associated with that region. Note that tickText
  * must be centered on the ticks--placing them on the same
  * AbsolutePanel as the ticks/plots facilitates this.
  *
  */
  PlotPanel plotPanel =  new PlotPanel();
  private String padding = USE_CSS;
  private boolean optimizeForMemory = false;
  private boolean clipToPlotArea = false;
  private boolean clipToDecoratedChart = false;
  private int titleThickness = GChart.NAI;
 
  private Axis xAxis;  // must be created in constructor
  private Axis yAxis;  // because they use system curves
  private Axis y2Axis;

  private int xChartSize; //  pixel size of plotting region
  private int yChartSize;

  // # of system curves "underneath" (before in DOM-order) user's curves
  private static int N_PRE_SYSTEM_CURVES = 16;
  // # of system curves "on top of" (after in DOM-order) user's curves
  private static int N_POST_SYSTEM_CURVES = 2;
  private static int N_SYSTEM_CURVES = N_PRE_SYSTEM_CURVES+
                                       N_POST_SYSTEM_CURVES;
  // index of curve that holds correspondingly-named chart part
  // (sys curve indexes are negative & not directly developer-accessible)
  private final static int PLOTAREA_ID = 0-N_SYSTEM_CURVES;
  private final static int TITLE_ID = 1-N_SYSTEM_CURVES;
  private final static int YAXIS_ID = 2-N_SYSTEM_CURVES;
  private final static int YTICKS_ID = 3-N_SYSTEM_CURVES;
  private final static int YGRIDLINES_ID = 4-N_SYSTEM_CURVES;
  private final static int YLABEL_ID = 5-N_SYSTEM_CURVES;
  private final static int Y2AXIS_ID = 6-N_SYSTEM_CURVES;
  private final static int Y2TICKS_ID = 7-N_SYSTEM_CURVES;
  private final static int Y2GRIDLINES_ID = 8-N_SYSTEM_CURVES;
  private final static int Y2LABEL_ID = 9-N_SYSTEM_CURVES;
  private final static int LEGEND_ID = 10-N_SYSTEM_CURVES;
  private final static int XAXIS_ID = 11-N_SYSTEM_CURVES;
  private final static int XTICKS_ID = 12-N_SYSTEM_CURVES;
  private final static int XGRIDLINES_ID = 13-N_SYSTEM_CURVES;
  private final static int XLABEL_ID = 14-N_SYSTEM_CURVES;
  private final static int FOOTNOTES_ID = 15-N_SYSTEM_CURVES;
  private final static int HOVER_CURSOR_ID = 16-N_SYSTEM_CURVES;
  private final static int HOVER_ANNOTATION_ID = 17-N_SYSTEM_CURVES;
 
  // adds system curves GChart uses to render title, ticks, etc.
  private void addSystemCurves() {
     // Must be first: other methods assume sys curves exist
     for (int i = 0; i < N_SYSTEM_CURVES; i++) {
        Curve c = new Curve(i);
        curves.add(c);
// Required rendering panels are added lazily, later on      
     }

     // define static (or default) properties, points on, system curves
     Curve c = getSystemCurve(PLOTAREA_ID);
     c.getSymbol().setSymbolType(SymbolType.BOX_SOUTHEAST);
     c.getSymbol().setBackgroundColor(DEFAULT_PLOTAREA_BACKGROUND_COLOR);
     c.getSymbol().setBorderColor(DEFAULT_PLOTAREA_BORDER_COLOR);
     c.getSymbol().setBorderStyle(DEFAULT_PLOTAREA_BORDER_STYLE);
     c.getSymbol().setBorderWidth(DEFAULT_PLOTAREA_BORDER_WIDTH);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(-Double.MAX_VALUE,Double.MAX_VALUE);

     c = getSystemCurve(TITLE_ID);
     c.getSymbol().setSymbolType(SymbolType.ANCHOR_NORTHWEST);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(0,0);
     c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);

     c = getSystemCurve(YAXIS_ID);
     c.getSymbol().setSymbolType(SymbolType.XGRIDLINE);
     c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
     c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
     c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
     c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(-Double.MAX_VALUE,-Double.MAX_VALUE);

     c = getSystemCurve(YTICKS_ID);
     c.getSymbol().setSymbolType(SymbolType.BOX_WEST);
     c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderStyle(TICK_BORDER_STYLE);
     c.getSymbol().setBorderWidth(TICK_BORDER_WIDTH);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     // points, annotation locations added when ticks are
    
     c = getSystemCurve(YGRIDLINES_ID);
     c.getSymbol().setSymbolType(SymbolType.YGRIDLINE);
     c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
     c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
     c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
     c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
    
     c = getSystemCurve(YLABEL_ID);
     c.getSymbol().setSymbolType(SymbolType.ANCHOR_WEST);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(0,0);
     c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);

     c = getSystemCurve(Y2AXIS_ID);
     c.setYAxis(Y2_AXIS);
     c.getSymbol().setSymbolType(SymbolType.XGRIDLINE);
     c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
     c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
     c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
     c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(Double.MAX_VALUE,-Double.MAX_VALUE);

     c = getSystemCurve(Y2TICKS_ID);
     c.setYAxis(Y2_AXIS);
     c.getSymbol().setSymbolType(SymbolType.BOX_EAST);
     c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderStyle(TICK_BORDER_STYLE);
     c.getSymbol().setBorderWidth(TICK_BORDER_WIDTH);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
    
     c = getSystemCurve(Y2GRIDLINES_ID);
     c.setYAxis(Y2_AXIS);
     c.getSymbol().setSymbolType(SymbolType.YGRIDLINE);
     c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
     c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
     c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
     c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);

     c = getSystemCurve(Y2LABEL_ID);
     c.getSymbol().setSymbolType(SymbolType.ANCHOR_EAST);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(0,0);
     c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);

     c = getSystemCurve(LEGEND_ID);
     c.getSymbol().setSymbolType(SymbolType.ANCHOR_EAST);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(0,0);
     c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);

     c = getSystemCurve(XAXIS_ID);
     c.getSymbol().setSymbolType(SymbolType.YGRIDLINE);
     c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
     c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
     c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
     c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(-Double.MAX_VALUE,-Double.MAX_VALUE);

     // tick thickness and length get set in the axis constructors
     c = getSystemCurve(XTICKS_ID);
     c.getSymbol().setSymbolType(SymbolType.BOX_SOUTH);
     c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderStyle(TICK_BORDER_STYLE);
     c.getSymbol().setBorderWidth(TICK_BORDER_WIDTH);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
    
     c = getSystemCurve(XGRIDLINES_ID);
     c.getSymbol().setSymbolType(SymbolType.XGRIDLINE);
     c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
     c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
     c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
     c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
     c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);

     c = getSystemCurve(XLABEL_ID);
     c.getSymbol().setSymbolType(SymbolType.ANCHOR_SOUTH);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(0,0);
     c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);

     c = getSystemCurve(FOOTNOTES_ID);
     c.getSymbol().setSymbolType(SymbolType.ANCHOR_SOUTHWEST);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(0,0);
     c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);

     c = getSystemCurve(HOVER_ANNOTATION_ID);
     c.setVisible(false); // initially no hover annotation
     c.getSymbol().setSymbolType(SymbolType.NONE);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(Double.NaN,Double.NaN);
     c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);

     c = getSystemCurve(HOVER_CURSOR_ID);
     c.setVisible(false); // initially no hover selection
     c.getSymbol().setSymbolType(SymbolType.NONE);
     c.getSymbol().setHoverAnnotationEnabled(false);
     c.getSymbol().setHoverSelectionEnabled(false);
     c.addPoint(Double.NaN,Double.NaN);
     c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);

     // external "curve count" should now be 0 (system curves don't count)
     if (getNCurves() != 0)
        throw new
        IllegalStateException("getNCurves() != 0. Probably a GChart bug.");

  }

  /*
   * Updates the system curves that represent chart
   * decorations (axis labels, title, ticks, etc.).<p>
   *
   * Note that all x, y shifts are relative to the "anchoring"
   * symbol type locations defined once and for all in the
   * addSystemCurves method above.
   *
   */
  private void updateDecorations(int xChartSizeDecorated) {


    // x-axis label
    getSystemCurve(XLABEL_ID).getPoint(0).setAnnotationWidget(
       getXAxis().getAxisLabel(), getXChartSize(),
       getXAxis().getAxisLabelThickness());
    getSystemCurve(XLABEL_ID).getPoint(0).setAnnotationYShift(
      - getXAxis().getTickLabelThickness(false)
      - getXAxis().getTickSpace()
      - getXAxis().getTickLabelPadding()
      - getXAxis().getAxisLabelThickness()/2);

    // y-axis label
    getSystemCurve(YLABEL_ID).getPoint(0).setAnnotationWidget(
       getYAxis().getAxisLabel(),
       getYAxis().getAxisLabelThickness(), getYChartSize());
    getSystemCurve(YLABEL_ID).getPoint(0).setAnnotationXShift(
      - getYAxis().getTickLabelThickness(false)
      - getYAxis().getTickSpace()
      - getYAxis().getTickLabelPadding()
      - getYAxis().getAxisLabelThickness()/2);

    // y2-axis label
    getSystemCurve(Y2LABEL_ID).getPoint(0).setAnnotationWidget(
       getY2Axis().getAxisLabel(),
       getY2Axis().getAxisLabelThickness(), getYChartSize());
    getSystemCurve(Y2LABEL_ID).getPoint(0).setAnnotationXShift(
      + getY2Axis().getTickLabelThickness(false)
      + getY2Axis().getTickSpace()
      + getY2Axis().getTickLabelPadding()
      + getY2Axis().getAxisLabelThickness()/2);

    // legend
    Grid legend = null;
    if (isLegendVisible() && 0 < getNVisibleCurvesOnLegend())
      legend = createLegend(plotPanel);
    getSystemCurve(LEGEND_ID).getPoint(0).setAnnotationWidget(
       legend, getLegendThickness(), getYChartSize());
    getSystemCurve(LEGEND_ID).getPoint(0).setAnnotationXShift(
      + getY2Axis().getTickLabelThickness(false)
      + getY2Axis().getTickSpace()
      + getY2Axis().getTickLabelPadding()
      + getY2Axis().getAxisLabelThickness()
      + getLegendThickness()/2 );

    // title
    int shiftToLeftEdge =
       - getYAxis().getAxisLabelThickness()
       - getYAxis().getTickLabelThickness(false)
       - getYAxis().getTickSpace()
       - getYAxis().getTickLabelPadding();
    int shiftToHorizontalMidpoint =
         shiftToLeftEdge + xChartSizeDecorated/2;
    getSystemCurve(TITLE_ID).getPoint(0).setAnnotationWidget(
       getChartTitle(), xChartSizeDecorated,
       getChartTitleThickness());
    getSystemCurve(TITLE_ID).getPoint(0).setAnnotationYShift(
       getChartTitleThickness()/2);
    getSystemCurve(TITLE_ID).getPoint(0).setAnnotationXShift(
       shiftToHorizontalMidpoint);

    // footnotes
    getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationWidget(
       getChartFootnotes(), xChartSizeDecorated,
       getChartFootnotesThickness());
    getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationYShift(
      - getXAxis().getTickLabelThickness(false)
      - getXAxis().getTickSpace()
      - getXAxis().getTickLabelPadding()
      - getXAxis().getAxisLabelThickness()
      - getChartFootnotesThickness()/2 );
    if (getChartFootnotesLeftJustified()) {
      getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationXShift(
        shiftToLeftEdge);
      getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationLocation(
           AnnotationLocation.EAST);
    }
    else { // footnotes centered
      getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationXShift(
        shiftToHorizontalMidpoint);
      getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationLocation(
           AnnotationLocation.CENTER);
    }


    // add points to ticks and gridlines curves in accord with chart specs
   
    // x & y axis can be present even if no curves mapped to them
    getSystemCurve(XAXIS_ID).setVisible(getXAxis().getAxisVisible());
    getXAxis().populateGridlines();
    getSystemCurve(YAXIS_ID).setVisible(getYAxis().getAxisVisible());
    getYAxis().populateGridlines();

    // y2 axis is present only if at least 1 curve is on it.
    if (hasY2Axis()) {
      getY2Axis().populateGridlines();
      getSystemCurve(Y2AXIS_ID).setVisible(getY2Axis().getAxisVisible());
      getSystemCurve(Y2TICKS_ID).setVisible(true);
      getSystemCurve(Y2GRIDLINES_ID).setVisible(true);
    }
    else {
      getSystemCurve(Y2AXIS_ID).setVisible(false);
      getSystemCurve(Y2TICKS_ID).setVisible(false);
      getSystemCurve(Y2GRIDLINES_ID).setVisible(false);
    }
   
  }
 
  /**
   * Instantiates a GChart with a curve display region of
   * the specified size.
   *
   *
   * @param xChartSize the width of the curve display region, in pixels.
   * @param yChartSize the height of the curve display region, in pixels.
   *
   * @see #setXChartSize setXChartSize
   * @see #setYChartSize setYChartSize
   * @see #setChartSize setChartSize
   */
  public GChart(int xChartSize, int yChartSize) {
     super();
     addSystemCurves()// must come first: later lines use system curves
     xAxis = new XAxis()
     yAxis = new YAxis()
     y2Axis = new Y2Axis();
     setXChartSize(xChartSize);
     setYChartSize(yChartSize);
     // Note: plotPanel (where real chart resides) won't get
     // added to chartPanel (top-level do-nothing container for
     // padding and such) until AFTER first update; FF2 has some
     // serious performance problems otherwise for common usage
     // scenarios with large widget-count pages.
     initWidget(chartPanel);
     /*
      * See the block comment at top of "class GChart" for a detailed
      * discussion/rational for GChart's (very minimal support) of
      * stylenames. Would like deeper support if I can ever figure out
      * how to do it without hamstringing future versions by locking
      * them into a particular implementation I might need to change
      * later on. In particular, I don't know how to provide such "deep"
      * stylenames that also work consistently with canvas-rendered
      * curves.
      */
     setStyleName("gchart-GChart");
  }
  /**
   * Convenience no-arg constructor equivalent to
   * <tt>GChart(DEFAULT_X_CHARTSIZE,DEFAULT_Y_CHARTSIZE)</tt>.
   *
   * @see #GChart(int,int) GChart(int,int)
   *
   */
  public GChart() {
     this(DEFAULT_X_CHARTSIZE, DEFAULT_Y_CHARTSIZE);
  }


   /**
    *
    * Adds an object to handle click events on this chart, that
    * is, an object whose <tt>ClickHandler.onClick</tt> method will be
    * called whenever the user clicks on this chart.
    *
    * <p>
    *
    * When implementing a class that handles GChart click
    * events, you'll need to know the following facts:
    * <p>
    *
    * <ol>
    *
    *  <li>You can use the <tt>getSource</tt> method of the
    *  <tt>ClickEvent</tt> passed into your <tt>onClick</tt> handler
    *  to retrieve the <tt>GChart</tt> that was
    *  clicked on. For example:
    *  <p>
   
    *  <pre>
    *    // Deletes the clicked-on curve
    *    public void onClick(ClickEvent event) {
    *       GChart theGChart = (GChart) event.getSource();
    *       GChart.Curve c = theGChart.getTouchedCurve();
    *       if (null != c) {
    *          theGChart.removeCurve(c);
    *          // what you see in browser won't change without an update
    *          theGChart.update();
    *       }
    *    }
    *  </pre>
    <p>
   
    *  <li>The <tt>GChart</tt> methods <tt>getTouchedPoint</tt> and
    *  <tt>getTouchedCurve</tt> return either the point and
    *  curve that were clicked on, or <tt>null</tt> if the
    *  click didn't "touch" any points.
    * 
    *  <p>
   
    *</ol>
    * <p>
    *
    * The editable pie chart example on the GChart <a
    * href="http://gchart.googlecode.com/svn/trunk/live-demo/v2_6/com.googlecode.gchart.gchartdemoapp.GChartDemoApp/GChartDemoApp.html">
    * live demo page</a>
    * illustrates how to use this method to launch a popup modal
    * <tt>DialogBox</tt> whenever the user clicks on a point, and how to
    * change the selected point from within that dialog by using
    * GChart's <tt>touch</tt> method.
    * <p>
    *
    * For a much simpler example that lets the user
    * delete points by clicking on them, see the Chart Gallery's 
    * <a href="package-summary.html#GChartExample18a">
    *  GChartExample18a</a>.
    * <p>
    *
    * @param clickHandler the click handler that will handle
    *   click events on this chart.
    *
    * @return the handler's registration object. You need to retain a
    * reference to this registration object only if you may later need
    * to remove the handler (via the registration's
    * <tt>removeHandler</tt> method).  Most applications don't remove
    * handlers (handlers tend to be statically defined) and so can
    * ignore the value returned from this method.
    *
    * @see #getTouchedPoint getTouchedPoint
    * @see #getTouchedCurve getTouchedCurve
    * @see #touch touch
    * @see #isUpdateNeeded isUpdateNeeded
    */

  public HandlerRegistration addClickHandler(ClickHandler clickHandler) {
       HandlerRegistration result =
           addDomHandler(clickHandler, ClickEvent.getType());
       return result;  
   }
  
/**
* Adds a new curve to the chart, at the end of the current
* list of curves.
* <p>
*
* @see #getCurve getCurve
* @see #addCurve(int) addCurve(int)
* @see #removeCurve removeCurve
* @see #clearCurves clearCurves
* @see #getNCurves getNCurves
*/

  public void addCurve() {
     addCurve(getNCurves());
  }
  /*
   * Given external, coded, index returns a curve's ArrayList index
   *
   * Basic order within the curves array is as follows:
   *
   *   o 6 decorative curves that hold title, etc
   *   o "getNCurves()" user-created curves                   
   *   o 1 "Pop-up" hover annotation holding curve
   *   o 1 Selection cursor holding curve         
   *
   * It's very important that the last two system curves come last, both
   * for performance (at the end means GChart's algorithms are able to
   * update only these curves when hover feedback changes) and to
   * ensure these elements are always on top of all other chart
   * elements, as required.
   * <p>
   *
   * The "external" system curve indexes are in a continuous range of
   * negative integers, which are mapped into the ArrayList
   * positions above via this code.
   *
   */
  int internalCurveIndex(int externalIndex) {
     int result;
     if (GChart.NAI == externalIndex)
// -1 is the "no such curve" index used by an ArrayList
        result = -1;
     else if (externalIndex < -N_POST_SYSTEM_CURVES
        // decoration related sys curves (before user's)
        result = externalIndex + N_SYSTEM_CURVES;
     else if (externalIndex < 0)
       // hover feedback related, after user curves (at the end)   
        result = curves.size()+externalIndex;
     else
       // + indexes mapped to ordinary user-created curves 
        result = externalIndex + N_PRE_SYSTEM_CURVES;
     return result;
  }

  /* Given a curves ArrayList index returns external, coded, index
   *
   * Companion/inverse of preceeding method.
   *
   */
  int externalCurveIndex(int internalIndex) {
     int result;
     if (internalIndex < 0)
        result = GChart.NAI;
     else if (internalIndex < N_PRE_SYSTEM_CURVES)
        // one of the sys curves that comes before user's curves
        result = internalIndex - N_SYSTEM_CURVES;
     else if (internalIndex >= curves.size()-N_POST_SYSTEM_CURVES)
       // sys curves, like hover feedback, that come after user's
        result =  internalIndex - curves.size();
     else
       // ordinary user created curve
        result = internalIndex - N_PRE_SYSTEM_CURVES;
     return result;
  }
  // does the external curve index represent a GChart-sys-defined curve?
  private boolean isSystemCurveIndex(int externalIndex) {
     boolean result = externalIndex < 0;
     return result;
  }
  /**
   * Adds a new curve to the chart, at the specified position
   * in the curve sequence. Existing curves at postions at
   * or greater than the specified position have their
   * positional indexes increased by 1.
   * <p>
   *
   * @see #getCurve getCurve
   * @see #addCurve() addCurve()
   * @see #removeCurve removeCurve
   * @see #clearCurves clearCurves
   * @see #getNCurves getNCurves
   */

public void addCurve(int iCurve) {
  if (iCurve > getNCurves())
     throw new IllegalArgumentException(
       "iCurve = " + iCurve +"; iCurve may not exceed getNCurves() (" + getNCurves() + ")");
  else if (iCurve < 0)
    throw new IllegalArgumentException(
       "iCurve = " + iCurve +"; iCurve may not be negative.");
  int internalIndex = internalCurveIndex(iCurve);
  Curve c = new Curve(internalIndex);
  curves.add(internalIndex, c);
  // curves are initially added to the x, y axes.
  getXAxis().incrementCurves();
  getYAxis().incrementCurves();
  // adjust ArrayList indexes to account for newly added element
  for (int i = internalIndex+1; i < curves.size(); i++)
     curves.get(i).incrementIndex();
  if (0 != plotPanel.getRenderingPanelCount()) { // other panels are already there
     int rpIndex = getRenderingPanelIndex(internalIndex);
     plotPanel.addGraphicsRenderingPanel(rpIndex);
     plotPanel.addAnnotationRenderingPanel(rpIndex);
  }
  // otherwise, before 1st update: lazy-add panels when they're 1st used
  c.invalidate();
  if (getNCurves() > 0)
     setDefaultBorderColor(c, getNCurves()-1);

}

  /**
   * Removes every curve this chart contains.
   *
   * @see #getCurve getCurve
   * @see #addCurve() addCurve()
   * @see #addCurve(int) addCurve(int)
   * @see #removeCurve removeCurve
   * @see #getNCurves getNCurves
   */
public void clearCurves() {
   for (int iCurve = getNCurves()-1; iCurve >= 0; iCurve--)
      removeCurve(iCurve);
}

/**
* Convenience method that, given a plain text label, returns an
* HTML snippet appropriate for use as an argument to the
* <tt>setHovertextTemplate</tt> or <tt>setAnnotationText</tt>
* methods, that will display the plain text label with
* formatting appropriate for use with hovertext.
* <p>
*
*
* When the string returned from this method is used as an
* argument to <tt>setHovertextTemplate</tt>, the hovertext that
* is generated is similar in format (light yellow background,
* black 1px border) to what is produced via the
* <tt>setTitle</tt> method (standard browser element hovertext).
* <p>
*
* In detail, this method creates the returned string via the line:
* <p>
*
* <pre>
* result =
     "&lt;html&gt;&lt;div style='background-color:#FFFFF0; border-color:black; border-style:solid; border-width:1px; padding:2px'&gt;"
     + plainTextLabel + "&lt;/div&gt;";
* </pre>
*
* <p>
* This method is provided mainly to simplify transitioning from the
* old hover feedback system to the new one introduced in v2.4.
* <p>
*
* It is expected that many pre v2.4 GChart applications will
* find that simply wrapping a <tt>formatAsHovertext</tt> call
* around existing hovertext templates will provide acceptable
* (though somewhat different) hover feedback for existing
* charts:
*
* <pre>
*   getCurve().getSymbol().setHovertextTemplate(
*     GChart.formatAsHovertext("(x,y) = (${x}, ${y})"));
* </pre>
* <p>
*
* Note that GChart v2.4 automatically includes such a wrapping
* <tt>formatAsHovertext</tt> call on all default hovertext
* templates.
*
* <p>
*
* See the {@link Symbol#setHoverAnnotationSymbolType
* setHoverAnnotationSymbolType} method for a
* code snippet that emulates not just the formatting,
* but also the at-the-mouse positioning behaviour,
* of setTitle-based hovertext.
*
* <p>
*
*
* @see Symbol#setHovertextTemplate setHovertextTemplate
* @see Curve.Point#setAnnotationText setAnnotationText
* @see Symbol#setHoverAnnotationSymbolType setHoverAnnotationSymbolType
*
* @param plainTextLabel the plain text label that is to be
*  HTML-wrapped to make it look like <tt>setTitle</tt>-based
*  hovertext.
*
*
*/

public static String formatAsHovertext(String plainTextLabel) {
   String result =
     "<html><div style='background-color:#FFFFF0; border-color:black; border-style:solid; border-width:1px 1px 1px 1px; padding:2px; text-align:left'>"
     + plainTextLabel + "</div>";
   return result;
}

  /**
   ** Returns the background color of the chart as a whole.
   **
   ** @return the chart's background color, in a standard
   **   CSS color string format.
   **
   ** @see #setBackgroundColor(String) setBackgroundColor
   **
   **/
  public String getBackgroundColor() {
     return(backgroundColor);
  }
  /**
   ** Returns the color of the border around the chart as
   ** a whole.
   **
   ** @return the color of the chart's border, in a standard
   **   CSS color string format.
   **
   ** @see #setBorderColor(String) setBorderColor
   **
   **/
  public String getBorderColor() {
     return borderColor;
  }

  /**
   ** Returns the width of the border around the chart as a whole
   ** 
   ** @return width of the border around the chart as a whole, as
   **   a CSS border width specification string (e.g. "1px").
   **
   ** @see #setBorderWidth(String) setBorderWidth
   **
   **/
  public String getBorderWidth() {
     return borderWidth;
  }

  /**
   ** Returns the style of the border around the chart as a whole
   ** 
   ** @return cssStyle for the border around the chart as a whole
   **
   ** @see #setBorderStyle(String) setBorderStyle
   **
   **/
  public String getBorderStyle() {
     return borderStyle;
  }

 
     /** Returns the previously specified chart footnotes widget.
     *
     * @return widget representing chart's footnotes or <tt>null</tt> if none.
     *
     * @see #setChartFootnotes(Widget) setChartFootnotes(Widget)
     * @see #setChartFootnotes(String) setChartFootnotes(String)
     * @see #getChartTitle getChartTitle
     */
    public Widget getChartFootnotes() {
         return chartFootnotes;
     }
    /** Returns flag indicating if this chart's footnotes are
     *  left-justified or centered.
     *
     * @return true if footnotes are flush against the left edge
     * of the chart, false if they are horizontally centered
     * across the bottom edge of the chart.
     *
     * @see #setChartFootnotesLeftJustified setChartFootnotesLeftJustified
     * @see #setChartFootnotes(String) setChartFootnotes(String)
     * @see #setChartTitle setChartTitle
     */
    public boolean getChartFootnotesLeftJustified() {
         return chartFootnotesLeftJustified;
     }
    /** Returns the thickness (height) of the rectangular region
     ** at the bottom of the chart allocated for footnotes.
     ** <p>
     **
     ** The width of this region always equals the width of
     ** the entire GChart (including legend and axis labels).
     ** <p>
     **
     ** Your footnotes widget is always vertically centered
     ** in this region.
     ** <p>
     **
     **
     ** Your footnotes widget will either be horizontally
     ** centered in this region, or left justified in it,
     ** depending on the property defined by the
     ** <tt>setChartFootnotesLeftJustified</tt> method.
     **
     ** <p>
     **
     **
     ** This method always returns 0 if the footnotes widget
     ** is <tt>null</tt> (the default); the rectangular
     ** footnotes region is entirely eliminated in that case.
     ** <p>
     **
     ** @return the thickness (height) of the rectangular region
     ** at the bottom of the chart allocated for footnotes, in
     ** pixels.
     **
     ** @see #setChartFootnotesThickness(int) setChartFootnotesThickness
     ** @see #setChartFootnotesLeftJustified setChartFootnotesLeftJustified
     **/
     public int getChartFootnotesThickness() {
        int result = 0;
        final int EXTRA_HEIGHT = 3; // 1.5 lines padding above/below
        final int DEF_HEIGHT = 1;
        if (null == getChartFootnotes())
           result = 0;
        else if (GChart.NAI != footnotesThickness)
           result = footnotesThickness;
        else if (getChartFootnotes() instanceof HasHTML)
           result = DEFAULT_FOOTNOTES_THICKNESS * (EXTRA_HEIGHT +
             htmlHeight(((HasHTML) (getChartFootnotes())).getHTML()));
        else
           result = DEFAULT_FOOTNOTES_THICKNESS*
                    (DEF_HEIGHT + EXTRA_HEIGHT);
        return result;
     }
  /** Returns the previously specified widget representing the
   *  chart's title.
   *
   * @return widget representing chart's title or <tt>null</tt>
   * if none
   *
   * @see #setChartTitle(Widget) setChartTitle(Widget)
   * @see #setChartTitle(String) setChartTitle(String)
   *
   */
    public Widget getChartTitle() {
       return chartTitle;
    }

     /**
      ** Returns the thickness (height) of the rectangular region at
      ** the top of the chart allocated for the title.
      ** <p>
      **
      ** This method always returns 0 if the title widget
      ** is <tt>null</tt> (the default); the rectangular
      ** title region is entirely eliminated in that case.
      ** <p>
      **
      ** Your title widget is always centered vertically and
      ** horizontally within this rectangular region.
      **
      **
      ** @return the thickness (height) of the rectangle
      ** that contains the chart's title, in pixels.
      **
      ** @see #setChartTitleThickness setChartTitleThickness
      **
      **/
     public int getChartTitleThickness() {
        int result = 0;
        final int EXTRA_HEIGHT = 3; // 1.5 lines above & below title
        final int DEF_HEIGHT = 1;
        if (null == getChartTitle())
           result = 0;
        else if (GChart.NAI != titleThickness)
           result = titleThickness;
        else if (getChartTitle() instanceof HasHTML)
           result = DEFAULT_TITLE_THICKNESS * (EXTRA_HEIGHT +
              htmlHeight(((HasHTML) (getChartTitle())).getHTML()));
        else
           result = DEFAULT_TITLE_THICKNESS*
                    (EXTRA_HEIGHT + DEF_HEIGHT);
        return result;
      }

    /**
     * Determines if this chart will clip any chart elements
     * that extend beyond the bounds of the decorated chart.
     * The decorated chart includes title, footnotes, etc.
     * as well as the plot area proper.
     *
     * @return true if off-the-decorated-chart elements are
     * clipped, false otherwise.
     *
     * @see #setClipToDecoratedChart setClipToDecoratedChart
     * @see #setClipToPlotArea setClipToPlotArea
     * @see #getXChartSizeDecorated getXChartSizeDecorated
     * @see #getYChartSizeDecorated getYChartSizeDecorated
     *
     */
    public boolean getClipToDecoratedChart() {
       return clipToDecoratedChart;
    }
   
    /**
     * Returns true if graphical aspects of the
     * chart that fall outside of the plot area are being clipped
     * off, false otherwise.
     *
     * @return <tt>true</tt> if clipping to plot area, else
     * <tt>false</tt>.
     *
     * @see #setClipToPlotArea setClipToPlotArea
     */
   
    public boolean getClipToPlotArea() {
       return clipToPlotArea;
    }
   
     // returns point closest to given plot-panel pixel coordinates
     Curve.Point getClosestBrushTouchingPointNoCheck(int x, int y) {
       
       Curve.Point result = null;
       // NAI means mouse is at some unknown, off-the-chart, position
       if (x == GChart.NAI || y == GChart.NAI) return result;
       double dBest = Double.MAX_VALUE; // dist. to closest symbol
      
       // fact that charts tend to have a small number of curves
       // allows us to use simple sequential search across curves
       int nCurves = getNCurves();
       for (int iCurve = 0; iCurve < nCurves; iCurve++) {
         Curve c = getSystemCurve(iCurve)
         if (!c.isVisible()) continue;
         Symbol sym = c.getSymbol();
         if (!sym.getHoverAnnotationEnabled() &&
             !sym.getHoverSelectionEnabled()) continue;
         SymbolType symType = sym.getSymbolType();
         boolean onY2 = c.onY2();
         int iClosest = c.getClosestTouchingPoint(x, y);
         if (GChart.NAI == iClosest) continue; // no hits on this curve

         double xPoint = symType.getCenterX(plotPanel,
                                            sym, iClosest);
         double yPoint = symType.getCenterY(plotPanel,
                                            sym, iClosest, onY2);
         double dx = sym.xScaleFactor*(x-xPoint);
         double dy = sym.yScaleFactor*(y-yPoint);
         // distance, squared, of mouse from symbol's "center"
         double d = dx*dx+dy*dy;
         if (d <= dBest) { // for ties, use later, "on top", point
            dBest = d;
            result = c.getPoint(iClosest);
         }
       }
       return result;
    }
    /**
     *
     * Returns the point that would be touched if the mouse were
     * moved to the given x,y plot-area pixel coordinates, or
     * <tt>null</tt> if the moving the mouse to these coordinates
     * would not have touched any points.<p>
     *
     * This method only works if the chart rendering is
     * up-to-date (if <tt>isUpdateNeeded</tt> returns
     * <tt>false</tt>). Otherwise, <tt>null</tt> is returned.
     * <p>
     *
     * <small> GChart's hit testing method works best if a
     * chart's points are approximately evenly distributed across
     * the plot area's x or y axis, across a small number of
     * curves. In particular, charts that have many points
     * bunched up into a small part of the plot area, or that
     * have many points completely outside of the plot area, or
     * that place each point into a separate curve, could
     * experience significantly worse that usual hit testing
     * performance. Though such cases are expected to be rare, in
     * the worst case, GChart could be reduced to a simple linear
     * search across all the chart's points during hit testing.
     * </small>
     *
     * @param xPlotArea x-coordinate of trial mouse position, in
     * GChart's plot area pixel coordinates.
     * @param yPlotArea y-coordinate of trial mouse position, in
     * GChart's plot area pixel coordinates.
     *
     * @return reference to the point that would have been "touched"
     *   by the mouse, or <tt>null</tt> if positioning the mouse
     *   to these coordinates would not have touched any point.
     *  
     * @see Axis#getMouseCoordinate getMouseCoordinate
     * @see Axis#modelToPlotAreaPixel modelToPlotAreaPixel
     * @see #isUpdateNeeded isUpdateNeeded
     * @see #touch touch
     *
     */

     public Curve.Point getClosestBrushTouchingPoint(int xPlotArea,
                                                     int yPlotArea) {
        Curve.Point result = null;
        if (!isUpdateNeeded()) {
           result = getClosestBrushTouchingPointNoCheck(xPlotArea, yPlotArea);
        }
        return result;
     }
    
/** Convenience method equivalent to <tt>getCurve(getNCurves()-1)</tt>.
   *  <p>
   *  This method, when used in conjunction with no-arg <tt>addCurve</tt>,
   *  method, makes code blocks that create and define the
   *  properties of a chart's curves more readable/editable. For example:
      <pre>
      addCurve(); // add 1st curve
      getCurve().setYAxis(Y2_AXIS); // first setting for 1st curve
      //... other settings for first curve
      addCurve(); // add 2nd curve
      getCurve().setYAxis(Y_AXIS); // first setting for 2nd curve
      // ... other settings for 2nd curve   
      </pre>
   *<p>
   * Note that using the no-arg methods in this way allows you to copy
   * entire groups of curve properties, unchanged, between such curve
   * related blocks.
   *
   *  @return the curve with the highest integer index. In other words,
   *    the curve with an index of <tt>getNCurves()-1</tt>.
   *
   *  @see #getCurve(int) getCurve(int)
   *  @see #getNCurves getNCurves
   *  @see #addCurve() addCurve()
   */
  public Curve getCurve() {
     int N = getNCurves();
     if (N < 1)
        throw new IllegalStateException(
"You must add at least 1 curve before invoking getCurve()");
     Curve result = getSystemCurve(N-1);
     return result;
  }

     /**
     * Returns a reference to the curve at the specified
     * positional index.  Use the reference returned by this method to
     * modify properties of a curve (the symbol, data points, etc.)
     *
     * <p>
     * @param iCurve index of the curve to be retrieved.
     * @return reference to the Curve at the specified position.
     *
     * @see #getCurve() getCurve()
     * @see #addCurve() addCurve()
     * @see #addCurve(int) addCurve(int)
     * @see #removeCurve removeCurve
     * @see #clearCurves clearCurves
     * @see #getNCurves getNCurves
     */
     public Curve getCurve(int iCurve) {

       if (iCurve >= getNCurves())
         throw new IllegalArgumentException(
             "iCurve = " + iCurve +"; iCurve may not exceed getNCurves()-1 (" + (getNCurves()-1) + ")");
       else if (iCurve < 0)
           throw new IllegalArgumentException(
             "iCurve = " + iCurve +"; iCurve may not be negative.");
                                             
       Curve result = getSystemCurve(iCurve);
       return result;
     }

     // Version of getCurve that allows sys curve (negative id) access
     Curve getSystemCurve(int iCurve) {
        int internalIndex = internalCurveIndex(iCurve);
        Curve result = curves.get(internalIndex);
        return result;
     }

     /**
     * Returns the positional index (within this chart's list of
     * curves) of the specified curve.
     * <p>
     *
     * Returns <i>GChart.NAI</i> if the specified curve is not found on
     * this GChart's curve list.
     *
     * <p>
     * @param curve whose list position is to be retrieved
     * @return position of curve in GChart's curve list, or
     *        <i>GChart.NAI</i> if not on this chart's curve list.
     *
     * @see #getCurve() getCurve()
     * @see #getCurve(int) getCurve(int)
     * @see #addCurve() addCurve()
     * @see #addCurve(int) addCurve(int)
     * @see #removeCurve removeCurve
     * @see #clearCurves clearCurves
     * @see #getNCurves getNCurves
     */
     public int getCurveIndex(Curve curve) {
        int internalIndex = curve.getIndexOf();
        int result = externalCurveIndex(internalIndex);
        return result;
     }
     int getInternalCurveIndex(Curve curve) {
        int result = curve.getIndexOf();
        return result;
     }
     // maps all background curve indexes into first rendering panel
     int getRenderingPanelIndex(int internalCurveIndex) {
        int result = 0;
        if (N_PRE_SYSTEM_CURVES <= internalCurveIndex)
           result = internalCurveIndex - N_PRE_SYSTEM_CURVES + 1;       
        return result;
     }
    

    /** Returns the font-family used in tick labels, point annotations,
     ** legends, and as the default in titles, footnotes, and
     ** axis labels.
     **
     ** @see #setFontFamily(String) setFontFamily
     **
     **
     **/
     public String getFontFamily() {
       return fontFamily;
     }

    /**
     ** Returns CSS color specification for all gridlines, axes,
     **   and tickmarks.
     **
     ** @see #setGridColor setGridColor
     **
     ** @return the color, in CSS standard color format,
     **    used for all gridlines, axes, and tick marks.
     **
     **/

     public String getGridColor() {
       Curve cGridlines = getSystemCurve(XGRIDLINES_ID);
       String result = cGridlines.getSymbol().getBorderColor();
       return result;
     }

    
  /**
   ** Returns the background color of the chart's legend.
   **
   ** @return the legend's background color, in a standard
   **   CSS color string format.
   **
   ** @see #setLegendBackgroundColor setLegendBackgroundColor
   **
   **/
  public String getLegendBackgroundColor() {
     return legendBackgroundColor;
  }
  /**
   ** Returns the border color of the chart's legend.
   **
   ** @return the color of the legend's border, in a standard
   **   CSS color string format, or else the special GChart keyword
   **   <tt>TRANSPARENT_BORDER_COLOR</tt>.
   **
   ** @see #setLegendBorderColor setLegendBordergroundColor
   ** @see #TRANSPARENT_BORDER_COLOR TRANSPARENT_BORDER_COLOR
   **
   **/
  public String getLegendBorderColor() {
     return legendBorderColor;
  }

  /**
   ** Returns the width of the chart's legend's border
   ** 
   ** @return width of the legend's border, in pixels
   **
   ** @see #setLegendBorderWidth setLegendBorderWidth
   **
   **/
  public int getLegendBorderWidth() {
     return legendBorderWidth;
  }

  /**
   ** Returns the style of the chart's legend's border
   ** 
   ** @return cssStyle of the legend's border
   **
   ** @see #setLegendBorderStyle setLegendBorderStyle
   **
   **/
  public String getLegendBorderStyle() {
     return legendBorderStyle;
  }
 
     /**
      ** Returns the color of the font used to display the labels
      **   within the legend (chart key)
      **  
      ** @return CSS color string defining the legend text's color
      **
      ** @see #setLegendFontColor setLegendFontColor
      **/
     public String getLegendFontColor() {
        return legendFontColor;
     }
  /**
   * Returns the CSS font size, in pixels, of text displayed
   * in the chart's legend (also know as a chart's key).
   *
   * @return the (previously specified) font size of legend text
   *
   * @see #setLegendFontSize setLegendFontSize
   */
  public int getLegendFontSize() {
     return legendFontSize;
  }
     /**
      ** Returns the font-style in which this GChart's legend text
      ** will be rendered.
      **
      ** @return font-style of legend text (italic, normal, etc.)
      **
      ** @see #setLegendFontStyle setLegendFontStyle
      **/
     public String getLegendFontStyle() {
        return legendFontStyle;
     }
     /**
      ** Returns true if legend text will be rendered in a bold,
      ** or false if in normal, weight font.
      **
      ** @return if the legend's text is in bold or not.
      **
      ** @see #setLegendFontWeight setLegendFontWeight
      **/
     public String getLegendFontWeight() {
        return legendFontWeight;
     }
     /**
      ** Returns the thickness (width) of the rectangular region
      ** to the right of the y2-axis label allocated for the
      ** chart legend.<p>
      **
      ** The region extends vertically in parallel with the
      ** right edge of the plot area. The legend is always
      ** centered vertically and horizontally within this
      ** rectangular region.
      ** <p>
      **
      ** This method always returns 0 if the legend is not
      ** visible; the rectangular legend region is entirely
      ** eliminated in that case.
      **
      ** @return thickness (width) of legend key holding region,
      ** in pixels.
      **
      ** @see #setLegendThickness setLegendThickness
      **/
     public int getLegendThickness() {
        int result = 0;
        if (isLegendVisible() &&
                0 < getNVisibleCurvesOnLegend()) {
           if (GChart.NAI == legendThickness)
              result = getDefaultLegendThickness();
           else
              result = legendThickness;
        }
       
        return result;

     }
  /**
   * Returns the number of curves on this chart.
   *
   * @return the number of curves on this chart
   *
   * @see #getCurve getCurve
   * @see #addCurve() addCurve()
   * @see #addCurve(int) addCurve(int)
   * @see #removeCurve removeCurve
   * @see #clearCurves clearCurves
   */
  public int getNCurves() {
     return curves.size() - N_SYSTEM_CURVES;
  }
  /** Returns the CSS string that specifies the width of the
   ** padding between the chart and it's external border
   ** <p>
   **
   ** @return the CSS string that defines the CSS padding property
   **   for the GChart as a whole.
   **
   ** @see #setPadding(String) setPadding
   **
   **/
  public String getPadding() {
    return padding;
  }
 
     /**
      ** Returns the background color of the area of the chart
      ** in which symbols representing curve data are displayed
      **  
      ** @return CSS color string defining the plot area's background
      **    color
      **
      ** @see #setPlotAreaBackgroundColor setPlotAreaBackgroundColor
      **/
     public String getPlotAreaBackgroundColor() {
        Curve c = getSystemCurve(PLOTAREA_ID);
        String result = c.getSymbol().getBackgroundColor();
        return result;
     }

     /**
      ** Returns the border color of the area of the chart
      ** in which symbols representing curve data are displayed
      **  
      ** @return CSS color string defining the color of the plot
      **    area's border
      **
      ** @see #setPlotAreaBorderColor setPlotAreaBorderColor
      **/
     public String getPlotAreaBorderColor() {
        Curve c = getSystemCurve(PLOTAREA_ID);
        String result = c.getSymbol().getBorderColor();
        return result;
    }
     /**
      ** Returns the width of the border around the area of the
      ** chart in which symbols representing curve data are
      ** displayed.
      **  
      ** @return width, in pixels, of the border around the plot area
      **
      ** @see #setPlotAreaBorderWidth setPlotAreaBorderWidth
      **/
     public int getPlotAreaBorderWidth() {
        Curve c = getSystemCurve(PLOTAREA_ID);
        int result = c.getSymbol().getBorderWidth();
        return result;
     }

     /**
      ** Returns the style of the border around the area of the
      ** chart in which symbols representing curve data are
      ** displayed (the so-called plot area).
      **  
      ** @return CSS style of the border around the plot area
      **
      ** @see #setPlotAreaBorderStyle setPlotAreaBorderStyle
      **/
     public String getPlotAreaBorderStyle() {
        Curve c = getSystemCurve(PLOTAREA_ID);
        String result = c.getSymbol().getBorderStyle();
        return result;
     }
     /**
      *
      * Returns the image URL that will be used to define the
      * plot area's background the next time <tt>update</tt> is called.
      * <p>
      *
      * @return url of image to be used as the background of the plot
      * area the next time that <tt>update</tt> is called.
      *
      * @see #setPlotAreaImageURL setPlotAreaImageURL
      * @see #update update
      *
      */
     public String getPlotAreaImageURL() {
        Curve c = getSystemCurve(PLOTAREA_ID);
        String result = c.getSymbol().getImageURL();
        return result;
     }
     /**
      *
      * Returns a flag that tells if GChart is configured to
      * perform updates so that the chart uses less memory.
      *
      * @return <tt>true</tt> if GChart optimizes updates to
      * save memory, <tt>false</tt> (the default) if it optimizes
      * them to save time.
      *
      * @see #setOptimizeForMemory setOptimizeForMemory
      *
      **/
     public boolean getOptimizeForMemory() {
        return optimizeForMemory;
     }
    
   
    /**
     * @deprecated
     *
     * Equivalent to <tt>!getClipToPlotArea()</tt>. Use that
     * method instead.
     *
     * @see #getClipToPlotArea getClipToPlotArea
     */
   
    public boolean getShowOffChartPoints() {
       return !getClipToPlotArea();
    }


    /** @deprecated
     **
     ** Equivalent to <tt>!getClipToDecoratedChart()</tt>. Use
     ** that method instead.
     **
     ** @see #getClipToDecoratedChart getClipToDecoratedChart
     **
     **/
    public boolean getShowOffDecoratedChartGlyphs() {
       return !getClipToDecoratedChart();
    }

   
    /**
   ** Returns a URL that points to a 1 x 1 pixel blank image
   ** file GChart requires to render its charts without
   ** producing missing image icons.
   **
   ** <p>
   **
   ** @return the URL of the file GChart needs to prevent
   ** missing image icons from appearing on your chart.
   **
   ** @see #setBlankImageURL setBlankImageURL
   **
   **/

     public static String getBlankImageURL() {
        return null == blankImageURL ? DEFAULT_BLANK_IMAGE_URL_FULLPATH :
              blankImageURL;
     }

     private HoverParameterInterpreter hoverParameterInterpreter = null;
     /**
      * Returns this GChart's hover parameter interpreter.
      *
      * @see #setHoverParameterInterpreter setHoverParameterInterpreter
      *
      * @return the hover parameter interpreter used by this
      * GChart, or <tt>null</tt> if none.
      *
      */
     public HoverParameterInterpreter getHoverParameterInterpreter() {
        return hoverParameterInterpreter;
     }

     private boolean hoverTouchingEnabled = true;

     /**
      * Is it possible to select points and have their hover
      * annotations pop up, merely by "touching" them with
      * the mouse-attached "brush"?
      *
      * @return true (the default) if just hovering over a point can
      * select it, false if you must click on a point to select it.
      *
      * @see #setHoverTouchingEnabled setHoverTouchingEnabled
      *
      */
     public boolean getHoverTouchingEnabled() {
        return hoverTouchingEnabled;
     }
    /**
     * Returns the x-axis associated with this chart. Use the
     * returned reference to manipulate axis min and max,
     * number of ticks, tick positions, tick label formats, etc.
     * <p>
     * @return object representing the x-axis of this chart.
     *
     * @see #getYAxis getYAxis
     * @see #getY2Axis getY2Axis
     */
    public Axis getXAxis() {
       return xAxis;
    }

    /**
     * Returns the number of x-pixels in the region of the chart
     * used for curve display purposes.
     *
     * @return the number of x-pixels available for curve display.
     *
     * @see #setXChartSize setXChartSize
     *
     */
    public int getXChartSize() {
       return xChartSize;
    }

    /**
     * Returns the number of x-pixels reserved for the chart as a
     * whole, including space reserved for decorations (title,
     * footnotes, axis labels, ticks, tick labels, legend key,
     * etc.).
     * <p>
     *
     * The returned size does not include the border or padding
     * around the chart as a whole. <p>
     *
     * You cannot directly set the decorated x chart size.
     * Instead, you must set the width of the plot area, and the
     * thicknesses of certain of the decoration-holding regions
     * (using methods linked to below) that, summed together,
     * define the total width of the chart.
     *
     * @return the width of the entire chart, in pixels.
     *
     * @see #setXChartSize setXChartSize
     * @see #getYChartSizeDecorated getYChartSizeDecorated
     * @see Axis#setAxisLabelThickness setAxisLabelThickness
     * @see Axis#setTickLabelThickness setTickLabelThickness
     * @see Axis#setTickLength setTickLength
     * @see Axis#setTickLocation setTickLocation
     * @see Axis#setTickLabelPadding setTickLabelPadding
     * @see Axis#setLegendThickness setLegendThickness
     *
     */
    public int getXChartSizeDecorated() {
       int result = getXChartSize() +
                    getYAxis().getAxisLabelThickness() +
                    getYAxis().getTickLabelThickness() +
                    getYAxis().getTickSpace() +
                    getYAxis().getTickLabelPadding() +
                    getY2Axis().getAxisLabelThickness() +
                    getY2Axis().getTickLabelThickness() +
                    getY2Axis().getTickSpace() +
                    getYAxis().getTickLabelPadding() +
                    getLegendThickness();
       return result;
    }


   
   
    /**
     * Returns the y2-axis (right y axis) associated with this
     * chart. Use the returned reference to manipulate axis
     * min and max, number of ticks, tick positions, tick
     * label formats, etc.
     *
     * <p>
     * @return object representing the y2-axis of this chart.
     *
     * @see #getYAxis getYAxis
     * @see #getXAxis getXAxis
     */
    public Axis getY2Axis() {
       return y2Axis;
    }
    /**
     * Returns the (left) y-axis associated with this chart. Use the
     * returned reference to manipulate axis min and max,
     * number of ticks, tick positions, tick label formats, etc.
     * <p>
     * @return object representing the y-axis of this chart.
     *
     * @see #getXAxis getXAxis
     * @see #getY2Axis getY2Axis
     */
    public Axis getYAxis() {
       return yAxis;
    }
    /**
     * Returns the number of y-pixels in the region of the chart
     * used for curve display purposes.
     *
     * @return the number of y-pixels available for curve display.
     *
     * @see #setYChartSize setYChartSize
     *
     */
    public int getYChartSize() {
       return yChartSize;
    }

    /**
     * Returns the number of y-pixels reserved for the chart as a
     * whole, including space reserved for decorations (title,
     * footnotes, axis labels, ticks, tick labels, etc.).  <p>
     *
     * The returned size does not include the border or padding
     * around the chart as a whole. <p>
     *
     * You cannot directly set the decorated y chart size.
     * Instead, you must set sizes and thicknesses of the
     * plot area and certain of the decoration-holding regions
     * (using the methods linked-to below) that, when summed
     * together, define the height of the decorated chart.
     *
     * @return the height of the entire chart, in pixels.
     *
     * @see #setYChartSize setYChartSize
     * @see #getXChartSizeDecorated getXChartSizeDecorated
     * @see Axis#setAxisLabelThickness setAxisLabelThickness
     * @see Axis#setTickLabelThickness setTickLabelThickness
     * @see Axis#setTickLength setTickLength
     * @see Axis#setTickLocation setTickLocation
     * @see Axis#setTickLabelPadding setTickLabelPadding
     * @see #setChartTitleThickness setChartTitleThickness
     * @see #setChartFootnotesThickness setChartFootnotesThickness
     *
     */
    public int getYChartSizeDecorated() {
       int result = getYChartSize() +
                    getXAxis().getAxisLabelThickness() +
                    getXAxis().getTickLabelThickness() +
                    getXAxis().getTickSpace() +
                    getXAxis().getTickLabelPadding() +
                    getChartTitleThickness() +
                    getChartFootnotesThickness();
      
       return result;
    }



   
     /**
     * Determines if this chart has a "y2" (right) y-axis.
     * <p>
     * Only charts that have at least one curve on the right
     * y axis will have a y2-axis.
     *
     * @return true if the chart has a second y axis, false otherwise.
     *
     * @see Curve#setYAxis Curve.setYAxis
     */
    public boolean hasY2Axis() {
       boolean result = getY2Axis().getNCurvesVisibleOnAxis() > 0;
       return result;
    }
    /**
     * Determines if this chart has an ordinary, or left, y-axis.
     * <p>
     * Only charts that have at least one curve on the left
     * y axis will have a y-axis.
     *
     * @return true if the chart has a left y axis, false otherwise
     *
     * @see Curve#setYAxis Curve.setYAxis
     *
     */
    public boolean hasYAxis() {
       boolean result = getYAxis().getNCurvesVisibleOnAxis() > 0;
       return result;
    }
    /**
     * Determines if the legend of this chart is visible.
     *
     *
     * @return true if the legend is visible, false otherwise.
     *
     * @see #setLegendVisible setLegendVisible
     */
    public boolean isLegendVisible() {return isLegendVisible;}


    /**
     *
     * Is the in-browser rendition of the chart inconsistent with
     * the current chart specs? In other words, is a call to
     * GChart's <tt>update</tt> method needed to bring the
     * browser's display into agreement with current chart specs?
     * <p>
     *
     * <i>Note:</i> Whenever this method returns
     * <tt>true</tt>, GChart "freezes" hover feedback, and
     * can no longer actively track the currently "touched"
     * point.  This is because GChart, to simplify its
     * bookkeeping, assumes in-browser (DOM) rendering and
     * current chart specs are in synch when determining the
     * point selection consequences of mouse events over the
     * chart.
     *
     * @return true if a call to <tt>update</tt> is needed to
     * bring current chart specifications and browser-rendered
     * representation into synch, false otherwise.
     *
     * @see #update update
     * @see #getTouchedPoint getTouchedPoint
     *
     */
    public boolean isUpdateNeeded() {
      boolean result = chartDecorationsChanged || !plotPanel.isValidated();
      return result;
    }

   /**
     * Removes the curve at the specified positional index.
     * <p>
     *
     * @param iCurve index of the curve to be removed
     *
     * @see #removeCurve(Curve) removeCurve(Curve)
     * @see #getCurve getCurve
     * @see #addCurve() addCurve()
     * @see #addCurve(int) addCurve(int)
     * @see #clearCurves clearCurves
     * @see #getNCurves getNCurves
     */
   public void removeCurve(int iCurve) {
     if (iCurve >= getNCurves())
         throw new IllegalArgumentException(
             "iCurve = " + iCurve +"; iCurve may not exceed getNCurves()-1 (" + (getNCurves()-1) + ")");
     else if (iCurve < 0)
       throw new IllegalArgumentException(
         "iCurve = " + iCurve +"; iCurve may not be negative.");

     invalidateDependentSlices(iCurve);

     /*
      * Simulate user moving away from point before it is deleted (this
      * assures that any required hoverCleanup gets called, and clears
      * the otherwise dangling reference to the touched point).
      *
      */
     if (plotPanel.touchedPoint != null &&
         plotPanel.touchedPoint.getParent() == getSystemCurve(iCurve))
        plotPanel.touch(null);

     // remove the rendering panel that corresponds to this curve
     // (must keep the two lists in synch or 1-to-1 mapping breaks)
     int internalIndex = internalCurveIndex(iCurve);
     if (0 != plotPanel.getRenderingPanelCount()) {
       int rpIndex = getRenderingPanelIndex(internalIndex);
       plotPanel.removeGraphicsRenderingPanel(rpIndex);
       plotPanel.removeAnnotationRenderingPanel(rpIndex);
     }
    
     Curve c = curves.get(internalIndex);
     if (c.isVisible()) {
       getXAxis().decrementCurves();
       if (c.getYAxis() == Y_AXIS)
         getYAxis().decrementCurves();
       else
         getY2Axis().decrementCurves();
     }
     c.clearIndex();
     // else before 1st update, no rendering panels created yet
     curves.remove(internalIndex);
  // adjust ArrayList indexes to account for newly removed element
     for (int i = internalIndex; i < curves.size(); i++)
        curves.get(i).decrementIndex();
   }


   /**
    * Removes the given curve from this GChart.
    * <p>
    *
    * If the given curve is <tt>null</tt> or is not a curve on this GChart,
    * an exception is thrown.
    *
    * <p>
    *
    * @param curve the curve to be removed.
    *
    * @see #removeCurve(int) removeCurve(int)
    *
    */
  
   public void removeCurve(Curve curve) {
     if (null == curve)
       throw new IllegalArgumentException("Curve cannot be null.");   
     int index = getCurveIndex(curve);
     if (index == GChart.NAI)
        throw new IllegalArgumentException("Curve is not one of this GChart's curves.");
       
     if (index < 0)
        throw new IllegalArgumentException("System curves cannot be removed (this should be impossible; a GChart bug is likely.)");
      else
        removeCurve(index);
   }
    
  /**
   ** Specifies the background color of the chart as a whole.
   **
   ** <p>
   ** The default background color is <tt>USE_CSS</tt>.
   ** <p>
   **
   ** For more information on standard CSS color
   ** specifications see the discussion in
   ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
   ** <p>
   **       
   ** @param cssColor the chart's background color, in a standard
   **   CSS color string format.
   **
   **
   ** @see #USE_CSS USE_CSS
   **
   **/
  public void setBackgroundColor(String cssColor) {
     chartDecorationsChanged = true;
     backgroundColor = cssColor;
  }

    /**
     ** Specifies a URL that points to the transparent, 1 x 1 pixel,
     ** "blank GIF" that GChart uses in order to render your
     ** chart without adding spurious "missing image" icons to it.
     ** <p>
     **
     ** When GWT compiles an application that imports the GChart
     ** library, it automatically adds an appropriate blank
     ** image, <tt>gchart.gif</tt>, to the module base directory
     ** (this is the directory into which GWT also copies your
     ** compiled Javascript, all the files in your public
     ** directory, etc.).  <p>
     **
     ** By default, GChart uses the following blank image URL:
     ** <p>
     **
     ** <pre>
     **   GWT.getModuleBaseURL() + "gchart.gif"
     ** </pre>
     ** <p>
     **
     ** <small> Earlier versions used "gchart.gif" as this default url.
     ** <a href="http://groups.google.com/group/Google-Web-Toolkit/msg/4be3f19dc14f958a">
     ** This GWT forum post by Dean S. Jones</a> identified the
     ** need to add the <tt>GWT.getModuleBaseURL()</tt> prefix.
     ** </small>
     ** <p>
     **
     ** Note that this default adds a potentially very
     ** long URL to every <tt>img</tt> element added by GChart to
     ** render your chart, which can (in theory) more than double
     ** the memory required to represent your chart in the
     ** browser, because the absolute URLs can be of undetermined
     ** length.  In practice, browser memory usage increases of
     ** 10% have been observed with the on-line demo GChart and a
     ** typicial, 60-odd character absolute URL.  <p>
     **
     ** You have several alternatives to the above default that can
     ** often reduce the length of the URL and thus save browser
     ** memory:
     **
     ** <p>
     **
     ** <ol> <li>Simply copy <tt>gchart.gif</tt> from the module
     **   base directory into your host page's base directory, and
     **   then use <tt>setBlankImageURL("gchart.gif")</tt> to access
     **   this URL relatively.
     **  
     **   <li>If the relative path from the host page base
     **       directory to the module base directory is
     **       reasonably short, pass that alternative
     **       relative URL to this method (note that all
     **       relative URLs are interpreted relative to the base
     **       directory of the host page containing your GChart).
     **
     **   <li>Place a copy of <tt>gchart.gif</tt> into
     **       a directory whose absolute URL is very short,
     **       and then pass that short absolute URL to this method.
     **
     ** </ol>   
     ** <p>
     **
     ** <small> <i>Special note to anyone reading
     ** this who designed HTML's <tt>image</tt> tag:</i> If you
     ** had provided a <tt>src=none</tt> option, this method
     ** would not have to exist.
     ** </small>
     ** <p>
     **
     ** <i>Tip:</i> If you already have an appropriate blank
     ** gif on your site that is accessible from the host
     ** page via a reasonably short URL there is no need to
     ** copy <tt>gchart.gif</tt>. You can just pass that URL
     ** to this method.
     **
     ** <p>
     **
     ** <i>Note:</i> Though GChart uses this blank image by default,
     ** you can use the <tt>setImageURL</tt> method to specify a
     ** non-blank image for use in rendering a specific curve.
     ** <p>
     **
     **
     ** @param blankImageURL a URL that points to a 1 x 1 pixel
     ** transparent image that GChart needs to render your
     ** charts without adding a spurious "missing image" icon.
     **
     ** @see #getBlankImageURL getBlankImageURL
     ** @see #DEFAULT_BLANK_IMAGE_URL DEFAULT_BLANK_IMAGE_URL
     ** @see Symbol#setImageURL setImageURL
     **
     **/

    public static void setBlankImageURL(
       String blankImageURL) {
       if (blankImageURL != GChart.blankImageURL) {
          GChart.blankImageURL = blankImageURL;
// Decided not to prefetch blank image URL because 1) pre-fetching
// doesn't improve performance noticably in tested browsers,
// 2) there are reports of possible memory leaks associated with
// its use in the GWT issue tracker, and 3) users can
// easily do the prefetch on their own if they want to, and that
// is really the right place to do a prefetch anyway.
//          Image.prefetch(GChart.getBlankImageURL());
       }
    }

    /**
     * Defines this GChart's hover parameter interpreter.
     * <p>
     *
     * Hovertext template strings can include <tt>${</tt>...
     * <tt>}</tt> bracketed
     * references to built-in parameters such as <tt>${x}</tt>
     * and <tt>${y}</tt> that get get replaced with appropriate
     * string representations of the x or y values of the
     * hovered-over point in displayed hovertext. You can add
     * new, custom, named parameters, and/or redefine the
     * meaning of built-in parameters, by passing a hover parameter
     * interpreter to this method.
     * <p>
     *
     * For sample code that shows you how to define a hover
     * parameter interpreter, see <tt>HoverParameterInterpreter</tt>.
     *     
     * @see HoverParameterInterpreter HoverParameterInterpreter
     * @see Symbol#setHovertextTemplate setHovertextTemplate
     *
     * @param hpi the hover parameter interpreter to use with all
     * hovertext templates on this GChart (this interpreter is
     * responsible for replacing <tt>${</tt>...<tt>}</tt>
     * bracketed embedded parameter names in the hover text
     * template with appropriate HTML snippets representing the
     * value of that parameter at the hovered-over point).
     *
     */
    public void setHoverParameterInterpreter(HoverParameterInterpreter hpi) {
       hoverParameterInterpreter = hpi;
    }

    /**
     * Specifies if merely hovering over a point is sufficient to select
     * it and display its hover annotation (<tt>true</tt>), or if an
     * actual click is needed (<tt>false</tt>).  <p>
     *
     * With the default of <tt>true</tt>, points are auto-selected as
     * the user "touches" them with the mouse-attached "brush"--no
     * clicking is required.  <p>
     *
     * When hover touching is disabled, a GChart can be used in a manner
     * analogous to a single-selection (sorry there's no multi-selection
     * capability) listbox, with its click-selectable points playing the
     * role of the selectable list items.  Specifically, disabling hover
     * touching lets you move the mouse freely without any danger of
     * changing the selected point--the point even remains selected if
     * the mouse moves entirely off the chart. This is helpful when your
     * application follows the common pattern of "select the thing you
     * want to operate on, then issue a command that operates on that
     * thing". This option is also helpful if you use very
     * compute-intensive hover widgets, or if you simply prefer
     * explictly-clicked-open/closed pop-up annotations.<p>
     *
     * <small> <i>How to Stop Leaky Clicks:</i> In IE7 and the hosted
     * mode browser, clicking ahead on a <tt>Button</tt> widget "leaks"
     * clicks upwards to the enclosing parent, even if you call
     * <tt>event.cancelBubble(true)</tt>. Such "leaky clicks" can
     * inappropriately change the selected point, when you really just
     * wanted to operate on it. This does not happen in Firefox 2, 3, or
     * Chrome, whose buttons properly "eat" the clicks--even when they
     * come in fast. To workaround the problem, you can place the
     * buttons into a hover widget (as shown in
     * <tt>GChartExample21.java</tt> in the chart gallery). This works
     * because GChart applies checks that ignore any mouse events that
     * occur within the rectangular region associated with the opened
     * hover widget.  </small> <p>
     *
     * For an example that uses <tt>setHoverTouchingEnabled(false)</tt>
     * to allow the user to change the y-value of the selected point,
     * see the Chart Gallery's <a
     * href="package-summary.html#GChartExample21"> GChartExample21</a>.
     *
     *
     * @param hoverTouchingEnabled <tt>true</tt> (the default) if you
     *   want users to be able to select points simply by hovering over
     *   them with their mouse, <tt>false</tt> if you want to
     *   require that they actually click on points to select them.
     *
     * @see #getHoverTouchingEnabled getHoverTouchingEnabled
     * @see Symbol#setBrushHeight setBrushHeight
     * @see #touch touch
     * @see #update update
     * @see HoverUpdateable HoverUpdateable
     *
     */
    public void setHoverTouchingEnabled(boolean hoverTouchingEnabled) {
       this.hoverTouchingEnabled = hoverTouchingEnabled;
    }
  /**
   ** Specifies the color of the border around the chart as
   ** a whole.
   **
   ** <p>
   ** The default border color is <tt>USE_CSS</tt>.
   **
   ** <p>
   ** <blockquote><small>
   ** <i>Tip:</i> No border will appear if either <tt>borderStyle</tt>
   ** is <tt>none</tt>, <tt>borderWidth</tt> is <tt>0px</tt> or
   ** <tt>borderColor</tt> is <tt>transparent</tt>. Since
   ** these will often be the "CSS inherited" values,
   ** generally, it's best to set all three properties
   ** whenever you set any one of them.
   ** </small></blockquote>
   ** <p>
   **
   **
   ** For more information on standard CSS color
   ** specifications see the discussion in
   ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
   ** <p>
   **
   ** @param cssColor the color of the chart's border, in a standard
   **   CSS color string format.
   **
   ** @see #setBorderWidth(String) setBorderWidth
   ** @see #setBorderStyle(String) setBorderStyle
   ** @see #getBorderColor getBorderColor
   ** @see #USE_CSS USE_CSS
   **
   **/
  public void setBorderColor(String cssColor) {
     chartDecorationsChanged = true;
     if (borderColor == null ||
         borderColor == TRANSPARENT_BORDER_COLOR)
        throw new IllegalArgumentException(
  "null and TRANSPARENT_BORDER_COLOR are not allowed. This method requires a valid CSS color specification String.");
     borderColor = cssColor;
  }

  /**
   ** Sets style of the border around the chart as a whole.
   **
   ** <p>
   ** The default border style is <tt>USE_CSS</tt>.
   ** <p>
   **
   ** <p>
   ** <blockquote><small>
   ** <i>Tip:</i> No border will appear if either <tt>borderStyle</tt>
   ** is <tt>none</tt>, <tt>borderWidth</tt> is <tt>0px</tt> or
   ** <tt>borderColor</tt> is <tt>transparent</tt>. Since
   ** these will often be the "CSS inherited" values,
   ** generally, it's best to set all three properties
   ** whenever you set any one of them.
   ** </small></blockquote>
   ** <p>
   **
   ** 
   ** @param borderStyle a CSS border style such as
   ** "solid", "dotted", "dashed", etc.
   **
   ** @see #getBorderStyle getBorderStyle
   ** @see #setBackgroundColor(String) setBackgroundColor
   ** @see #setBorderColor(String) setBorderColor
   ** @see #setBorderWidth(String) setBorderWidth
   ** @see #USE_CSS USE_CSS
   **
   **
   **/
     public void setBorderStyle(String borderStyle) {
        chartDecorationsChanged = true;
        this.borderStyle = borderStyle;
     }

  /**
   ** Specifies the width of the border around the chart as a whole.
   ** 
   ** <p>
   ** The default border width is <tt>USE_CSS</tt>.
   **
   ** <p>
   ** <blockquote><small>
   ** <i>Tip:</i> No border will appear if either <tt>borderStyle</tt>
   ** is <tt>none</tt>, <tt>borderWidth</tt> is <tt>0px</tt> or
   ** <tt>borderColor</tt> is <tt>transparent</tt>. Since
   ** these will often be the "CSS inherited" values,
   ** generally, it's best to set all three properties
   ** whenever you set any one of them.
   ** </small></blockquote>
   **
   ** @param cssWidth width of the border around the chart as a whole,
   **   expressed as a CSS border-width specification string, such
   **   as "1px".
   **
   ** @see #getBorderWidth getBorderWidth
   ** @see #setBorderStyle(String) setBorderStyle
   ** @see #setBorderColor(String) setBorderColor
   ** @see #USE_CSS USE_CSS
   **/
  public void setBorderWidth(String cssWidth) {
     chartDecorationsChanged = true;
     borderWidth = cssWidth;
  }


  /**
      * Convenience method equivalent to
      * <tt>setChartFootnotes(new HTML(html))</tt>.
      *
      * @param html HTML text used to define the chart's title.
      *
      * @see #setChartFootnotes(Widget) setChartFootnotes(Widget)
      */
  public void setChartFootnotes(String html) {
    setChartFootnotes(new HTML(html));
  }
    /** Sets widget that appears just below the chart.
     *  <p>
     *
     *  The widget will vertically centered within a band just
     *  below the x axis label that stretches along the entire
     *  bottom edge of the chart, and whose height is defined by
     *  <tt>setChartFootnotesThickness</tt>.
     *
     *  <p>
    
     *  The widget will either be left justified, or horizontally
     *  centered, within this band depending on the property
     *  defined by <tt>setChartFootnotesLeftJustified</tt>
    
     * 
     *  @param chartFootnotes widget representing the chart's footnotes
     *
     *  @see #setChartFootnotes(String) setChartFootnotes(String)
     *  @see #setChartFootnotesThickness setChartFootnotesThickness
     *  @see #getChartFootnotes getChartFootnotes
     *  @see #setChartFootnotesLeftJustified
     *  setChartFootnotesLeftJustified
     */
    public void setChartFootnotes(Widget chartFootnotes) {
       chartDecorationsChanged = true;
       this.chartFootnotes = chartFootnotes;
    }
   
    /** Defines if this chart's footnotes are left justified,
     *  or horizontally centered across the bottom edge of the
     *  chart.
     *  <p>
     *  Note that a chart's footnotes are always vertically
     *  centered within the band at the bottom of the chart
     *  reserved for chart footnotes. Use the
     *  <tt>setChartFootnotesThickness</tt> method to set the
     *  height of this band.
     *
     *  @param footnotesLeftJustified true to position chart footnotes
     *  flush against the left edge of the chart, false (the default) to
     *  center them horizontally across the chart's bottom edge.
     *
     *  @see #setChartFootnotes(String) setChartFootnotes(String)
     *  @see #getChartFootnotes getChartFootnotes
     *  @see #setChartFootnotesThickness
     */
    public void setChartFootnotesLeftJustified(boolean footnotesLeftJustified) {
       chartDecorationsChanged = true;
       chartFootnotesLeftJustified = footnotesLeftJustified;
    }

     /**
      ** Sets the thickness (height) of the rectangular region at
      ** the bottom of the chart allocated for the footnotes.
      ** <p>
      **
      ** The width of this region always equals the width of
      ** the entire GChart (including legend and axis labels).
      ** <p>
      **
      ** Your footnotes widget is always vertically centered
      ** in this region.
      ** <p>
      **
      **
      ** Your footnotes widget will either be horizontally
      ** centered in this region, or left justified in it,
      ** depending on the property defined by the
      ** <tt>setChartFootnotesLeftJustified</tt> method.
      ** <p>
      **
      ** This setting has no impact on chart layout if the
      ** footnotes widget is <tt>null</tt> (the default); the
      ** rectangular footnotes region is entirely eliminated, and
      ** in effect has a 0 thickness, in that case.
      ** <p>
      **
      ** If you set the footnotes thickness to <tt>GChart.NAI</tt>
      ** (the default) GChart will use a thickness based on
      ** the estimated number of (<tt>&lt;br&gt;</tt> or
      ** <tt>&lt;li&gt;</tt>
      ** delimited) lines.
      **
      ** @param thickness the thickness (height) of the rectangle
      ** that contains the footnotes, in pixels, or
      ** <tt>GChart.NAI</tt> to use the default thickness.
      **
      ** @see #getChartFootnotesThickness getChartFootnotesThickness
      ** @see #setChartFootnotesLeftJustified setChartFootnotesLeftJustified
      ** @see GChart#NAI GChart.NAI
      ** @see #DEFAULT_FOOTNOTES_THICKNESS
      ** DEFAULT_FOOTNOTES_THICKNESS
      **
      **/
     public void setChartFootnotesThickness(int thickness) {
        chartDecorationsChanged = true;
        this.footnotesThickness = thickness;
     }

    /**
      * Convenience method equivalent to
      * <tt>setXChartSize(xChartSize); setYChartSize(yChartSize)</tt>.
      *
      * @param xChartSize number of x-pixels in the curve
      *   display area of the chart
      * @param yChartSize number of y-pixels in the curve
      *   display area of the chart
      *
      * @see #setXChartSize setXChartSize
      * @see #setYChartSize setYChartSize
      *
      */
    public void setChartSize(int xChartSize, int yChartSize) {
       setXChartSize(xChartSize);
       setYChartSize(yChartSize);
    }
   
    /**
      * Convenience method equivalent to
      * <tt>setChartTitle(new HTML(html))</tt>.
      *
      * @param html HTML text used to define the chart's title.
      *
      * @see #setChartTitle(Widget) setChartTitle(Widget)
      */
  public void setChartTitle(String html) {
    setChartTitle(new HTML(html));
  }

    // returns x,y min/max over every plotted curve
   
    /**
     * Specifies the widget that appears centered just above the chart.
     *
     * @param chartTitle the widget to be used as this chart's title.
     *
     * @see #setChartTitle(String) setChartTitle(String)
     * @see #setChartTitleThickness setChartTitleThickness
     * @see #getChartTitle getChartTitle
     *
     */
    public void setChartTitle(Widget chartTitle) {
       chartDecorationsChanged = true;
       this.chartTitle = chartTitle;
    }

     /**
      ** Sets the thickness (height) of the rectangular region at
      ** the top of the chart allocated for the title.
      ** <p>
      **
      ** Your title widget is always centered vertically and
      ** horizontally within this rectangular region. <p>
      **
      ** This setting has no impact on chart layout if the title
      ** widget is <tt>null</tt>, since the title-holding
      ** region is entirely eliminated in that case.
      **
      ** If you set the title thickness to <tt>GChart.NAI</tt>
      ** (the default) GChart will use a thickness that is
      ** based on the the number of <tt>&lt;br&gt;</tt> or
      ** <tt>&lt;li&gt;</tt> delimited HTML lines if the title Widget
      ** implements <tt>HasHTML</tt>.
      **
      ** @param thickness the thickness (height) of the rectangle
      ** that contains the title, in pixels, or
      ** <tt>GChart.NAI</tt> to use the default thickness.
      **
      ** @see #getChartTitleThickness getChartTitleThickness
      ** @see GChart#NAI GChart.NAI
      ** @see #DEFAULT_TITLE_THICKNESS
      ** DEFAULT_TITLE_THICKNESS
      **
      **/
     public void setChartTitleThickness(int thickness) {
        chartDecorationsChanged = true;
        this.titleThickness = thickness;
     }

    /**
     * Specifies if this chart will clip any rendered chart elements
     * (including hover selection feedback and popup annotations)
     * that extends beyond the bounds of the decorated chart.
     * <p>
     *
     * The decorated chart includes not just the plot area, but
     * space allocated for titles, footnotes, legend key, axis
     * labels, tick marks, etc. The size of this decorated chart
     * can be obtained via the <tt>getXChartSizeDecorated</tt>
     * and <tt>getYChartSizeDecorated</tt> methods.
     * <p>
     *
     * <small> Note that, in non-IE browsers, drawing a curve via
     * <tt>GWTCanvas</tt> that falls outside the bounds of the
     * decorated chart could occlude mouse events over elements
     * on the enclosing page <i>that fall within the smallest
     * bounding rectangle that contains the canvas-rendered
     * curve</i>. HTML rendering (IE's element-based VML used by
     * <tt>GWTCanvas</tt> is essentially HTML-like in this respect) only
     * creates such occlusions at the positions where the curve
     * is actually rendered.  </small>
     *
     * @param clipToDecoratedChart use <tt>true</tt> to clip
     * off-the-decorated-chart symbols, annotations, etc.  or
     * <tt>false</tt> (the default) to allow such chart elements to be
     * drawn outside of the rectangular region allocated for the
     * chart.
     *
     * @see #getClipToDecoratedChart getClipToDecoratedChart
     * @see #setClipToPlotArea setClipToPlotArea
     * @see #getXChartSizeDecorated getXChartSizeDecorated
     * @see #getYChartSizeDecorated getYChartSizeDecorated
     * @see #setCanvasFactory setCanvasFactory
     *
     */

    public void setClipToDecoratedChart(boolean clipToDecoratedChart) {
      chartDecorationsChanged = true;
      invalidateAccessibleCurves();
      this.clipToDecoratedChart = clipToDecoratedChart;
    }
    /** Specifies if rendered graphics falling
     ** outside the plot area will be clipped off.
     *  <p>
    
     *  <i>Note:</i> This clipping only applies to the graphical parts
     *  of the rendered curves. It does not apply to any annotations,
     *  nor does it apply to the hover selection feedback. In
     *  particular, points that fall outside the plot area, though not
     *  visible, will still display their selection feedback and pop-up
     *  hover annotations when the user mouses over them.
     * 
     * @param clipToPlotArea <tt>false</tt> (the default) to display
     *   off-the-plot-area graphics,
     *   <tt>true</tt>
     *   to clip them off.
     *
     * @see #getClipToPlotArea getClipToPlotArea
     * @see #setClipToDecoratedChart setClipToDecoratedChart
     *
     */
     public void setClipToPlotArea(boolean clipToPlotArea) {
       chartDecorationsChanged = true;
       invalidateAccessibleCurves();
       this.clipToPlotArea = clipToPlotArea;
     }
     /**
      * Sets the symbol border colors that are used by default for
      * newly created curves. The
      * array must contain one or more elements, each a standard
      * CSS color specification string (see the
      * <tt>setBackgroundColor</tt> link below for more
      * on CSS color specification strings) or the
      * special GChart keyword <tt>TRANSPARENT_BORDER_COLOR</tt>.
      * <p>
      *
      * GChart uses the first color in this array as the default border
      * color of the first curve added (via <tt>addCurve</tt>), the
      * second color for the second curve added, and so on. If more
      * curves are added than the number of elements in the default
      * border colors array, the sequence is repeated.
      *
      * <p>
      * <small>
      * Note that each curve/symbol's default color is "locked in" at the
      * point when that curve/symbol is first added, based on the
      * total number of curves at that time.
      * </small>
      *
      * <p>
      *
      * Because, by default, GChart uses a transparent symbol background
      * color, the default border color will usually, in effect, define
      * the default color of each curve. The default border color
      * also defines the
      * default color of point-to-point connecting lines in a line
      * chart.<p>
      *
      * If not explicitly specified via this method, GChart uses
      * <tt>GChart.DEFAULT_SYMBOL_BORDER_COLORS</tt> by default.
      * However, most people find the
      * color sequence <a href=
      * "http://ui.openoffice.org/VisualDesign/OOoChart_colors_drafts.html#02">
      * used by OpenOffice's Charts</a> more aesthetically pleasing.
      * The <a
      * href="package-summary.html#GChartExample22a">World's Simplest
      * Line Chart Editor</a> example chart contains a line of
      * code that makes GChart use the OpenOffice defaults.
      * <p>
      *
      * <small>This feature was added in response to an email from
      * <a href="http://www.profilercorp.com">Joe Cole</a>
      * and <a href="http://gwt-ext.com/forum/viewtopic.php?f=13&t=3465&start=3">
this post</a> by Sanjiv Jivan.
      * They both pointed out the importance of changing GChart's
      * default colors.</small>
      *
      *
      * @param defaultBorderColors array of CSS color strings
      * whose successive elements define the initial symbol border colors
      * for curves in the order that they are added.
      *
      * @see #DEFAULT_SYMBOL_BORDER_COLORS DEFAULT_SYMBOL_BORDER_COLORS
      * @see #TRANSPARENT_BORDER_COLOR TRANSPARENT_BORDER_COLOR
      * @see Symbol#setBackgroundColor setBackgroundColor
      * @see Symbol#setBorderColor setBorderColor
      * @see #addCurve addCurve
      *
      */

     static public void setDefaultSymbolBorderColors(String[] defaultBorderColors) {
        if (null == defaultBorderColors)
           throw new IllegalArgumentException(
              "defaultBorderColors array cannot be null.");
        else if (defaultBorderColors.length < 1)
           throw new IllegalArgumentException(
              "defaultBorderColors array must have at least 1 element.");
        else
           defaultSymbolBorderColors = defaultBorderColors;

     }
       
    /** Sets the font-family used in tick labels, point annotations,
     ** legends, titles, footnotes, and
     ** axis labels.
     ** <p>
     ** If not specified, the default value is <tt>USE_CSS</tt>.
     ** <p>
     **
     ** Note that titles, footnotes and axis labels are
     ** defined via externally created Widgets, which are free
     ** to override the font-family specified by this
     ** method.
     **
     ** @param fontFamily a CSS font-family specification, such
     **   as "Arial, sans-serif"
     **
     ** @see #getFontFamily getFontFamily
     ** @see #USE_CSS USE_CSS
     **
     **/
     public void setFontFamily(String fontFamily) {
       chartDecorationsChanged = true;
       this.fontFamily = fontFamily;
     }


   
    /**
     ** Specifies the single color used for all gridlines, axes
     ** lines, and tick marks.
     **
     **
     ** <p>
     ** For more information on standard CSS color
     ** specifications see the discussion in
     ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
     ** <p>
     **       
     ** @param cssColor the color, in CSS standard color format,
     **    to be used for all gridlines, axes, and tick marks.
     **
     ** @see #getGridColor getGridColor
     ** @see #DEFAULT_GRID_COLOR DEFAULT_GRID_COLOR
     **
     **/
    public void setGridColor(String cssColor) {
//TODO: support line style for dotted/dashed gridlines lines,      
// allow tick and grid colors to be specified separately, etc.
       getSystemCurve(XGRIDLINES_ID).getSymbol().setBorderColor(cssColor);
       getSystemCurve(YGRIDLINES_ID).getSymbol().setBorderColor(cssColor);
       getSystemCurve(Y2GRIDLINES_ID).getSymbol().setBorderColor(cssColor);
       getSystemCurve(XAXIS_ID).getSymbol().setBorderColor(cssColor);
       getSystemCurve(YAXIS_ID).getSymbol().setBorderColor(cssColor);
       getSystemCurve(Y2AXIS_ID).getSymbol().setBorderColor(cssColor);
       getSystemCurve(XTICKS_ID).getSymbol().setBorderColor(cssColor);
       getSystemCurve(YTICKS_ID).getSymbol().setBorderColor(cssColor);
       getSystemCurve(Y2TICKS_ID).getSymbol().setBorderColor(cssColor);
    }
  /**
   ** Sets the background color of the chart's legend.
   **
   **
   ** <p>
   ** For more information on standard CSS color
   ** specifications see the discussion in
   ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
   ** <p>
   **       
   ** @param cssColor the legend's background color, in a standard
   **   CSS color string format.
   **
   ** @see #getLegendBackgroundColor getLegendBackgroundColor
   ** @see #DEFAULT_LEGEND_BACKGROUND_COLOR
   **       DEFAULT_LEGEND_BACKGROUND_COLOR
   **/
  public void setLegendBackgroundColor(String cssColor) {
     chartDecorationsChanged = true;
     legendBackgroundColor = cssColor;
  }
  /**
   ** Sets the border color of the chart's legend.
   **
   **
   ** <p>
   ** For more information on standard CSS color
   ** specifications see the discussion in
   ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
   ** <p>
   **       
   ** @param cssColor the color of the legend's border, in a standard
   **   CSS color string format, of the special GChart keyword
   **   <tt>TRANSPARENT_BORDER_COLOR</tt> for a transparent border.
   **  
   **
   ** @see #getLegendBorderColor getLegendBorderColor
   ** @see #DEFAULT_LEGEND_BORDER_COLOR DEFAULT_LEGEND_BORDER_COLOR
   ** @see #TRANSPARENT_BORDER_COLOR TRANSPARENT_BORDER_COLOR
   **
   **/
  public void setLegendBorderColor(String cssColor) {
     chartDecorationsChanged = true;
     legendBorderColor = cssColor;
  }
  /**
   ** Sets the width of the chart legend's border.
   **
   ** @param width the width of the legend's border, in pixels
   **
   ** @see #getLegendBorderWidth getLegendBorderWidth
   ** @see #DEFAULT_LEGEND_BORDER_WIDTH DEFAULT_LEGEND_BORDER_WIDTH
   **/
  public void setLegendBorderWidth(int width) {
     chartDecorationsChanged = true;
     legendBorderWidth = width;
  }
  /**
   ** Sets style of the border around the chart's legend (key).
   **
   ** <p>
   **
   ** <p>
   ** @param borderStyle a CSS border style such as
   ** "solid", "dotted", "dashed", etc.
   **
   ** @see #getLegendBorderStyle getLegendBorderStyle
   ** @see #setLegendBackgroundColor setLegendBackgroundColor
   ** @see #setLegendBorderColor setLegendBorderColor
   ** @see #DEFAULT_LEGEND_BORDER_STYLE DEFAULT_LEGEND_BORDER_STYLE
   **/
     public void setLegendBorderStyle(String borderStyle) {
        chartDecorationsChanged = true;
        legendBorderStyle = borderStyle;
     }
     /**
      ** Specifies the color of the legend's font. Default is
      ** <tt>DEFAULT_FONT_COLOR</tt>.
      **
      **
      ** <p>
      ** For more information on standard CSS color
      ** specifications see the discussion in
      ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
      ** <p>
      **       
      ** @param cssColor color of the font used to display the
      **    labels in the legend.
      **
      ** @see #getLegendFontColor getLegendFontColor
      ** @see #DEFAULT_FONT_COLOR DEFAULT_FONT_COLOR
      **
      **/
     public void setLegendFontColor(String cssColor) {
        chartDecorationsChanged = true;
        legendFontColor = cssColor;
     }

    /**
     * Specifies the CSS font size, in pixels, of text displayed
     * in the chart's legend (also know as a chart's key).
     * <p>
     * This size also governs the size of the symbol icon
     * displayed in the legend.
     * <p>
     * Default is <tt>DEFAULT_LEGEND_FONTSIZE</tt>.
     *
     * @param legendFontSize the font size of legend text
     *
     * @see #getLegendFontSize getLegendFontSize
     * @see #DEFAULT_LEGEND_FONTSIZE DEFAULT_LEGEND_FONTSIZE
     *
     */
    public void setLegendFontSize(int legendFontSize) {
       chartDecorationsChanged = true;
       this.legendFontSize = legendFontSize;
    }
     /**
      ** Specifies the cssStyle of the font used to render the
      ** legend's labels. Default is <tt>DEFAULT_FONT_STYLE</tt>.
      **
      ** @param cssStyle any valid CSS font-style, namely,
      **   normal, italic, oblique, or inherit.
      **
      ** @see #getLegendFontStyle getLegendFontStyle
      ** @see #DEFAULT_FONT_STYLE DEFAULT_FONT_STYLE
      **/
     public void setLegendFontStyle(String cssStyle) {
        chartDecorationsChanged = true;
        legendFontStyle = cssStyle;
     }
   
     /**
      ** Specifies the weight of the font used in the labels of the
      ** legend. Default is <tt>DEFAULT_FONT_WEIGHT</tt>.
      **
      ** @param cssWeight a CSS font-weight specification, such as
      **    bold, bolder, normal, light, 100, 200, ... or 900.
      **
      ** @see #getLegendFontWeight getLegendFontWeight
      ** @see #DEFAULT_FONT_WEIGHT DEFAULT_FONT_WEIGHT
      **/
     public void setLegendFontWeight(String cssWeight) {
        chartDecorationsChanged = true;
        legendFontWeight = cssWeight;
     }
     /**
      ** Sets the thickness (width) of the rectangular region at
      ** the right of the chart allocated for the legend key.
      ** <p>
      **
      ** This setting has no impact on chart layout if the
      ** legend key is not visible, since the legend key's
      ** rectangular region is entirely eliminated in that
      ** case.
      **
      ** <p>
      **
      ** If the legend thickness is set to <tt>GChart.NAI</tt>
      ** (the default) GChart uses an heuristic to set the legend
      ** thickness based on the number of characters in each
      ** curve's legend label.
      **
      **
      ** @param legendThickness the thickness (width) of the rectangle
      ** that contains the legend key, in pixels, or
      ** <tt>GChart.NAI</tt> to use a built-in heurstic
      ** to determine the legend width.
      **
      ** @see #getLegendThickness getLegendThickness
      ** @see Curve#setLegendLabel setLegendLabel
      ** @see Y2Axis#setAxisLabelThickness Y2Axis.setAxisLabelThickness
      **
      **/
     public void setLegendThickness(int legendThickness) {
        chartDecorationsChanged = true;
        this.legendThickness = legendThickness;
     }
    
    /**
     * Specifies if the legend is to be visible on this chart.
     * Legends are visible by default. However, a legend is only
     * generated if at least one curve's legend label has been
     * specified.
     *
     * @param isLegendVisible true to display the legend, false to
     * hide it.
     *
     * @see #isLegendVisible isLegendVisible
     * @see Curve#setLegendLabel setLegendLabel
     */
    public void setLegendVisible(boolean isLegendVisible) {
       chartDecorationsChanged = true;
       this.isLegendVisible = isLegendVisible;
    }

    /**
     * By default, this property is <tt>false</tt>, which means
     * that GChart will retain no-longer-needed Image and Grid
     * widgets (plus any user object references associated with
     * those widgets, such as those created via the
     * <tt>setAnnotationText</tt> and
     * <tt>setAnnotationWidget</tt> methods) between
     * <tt>updates</tt> in the expectation that they may be
     * needed by future updates.  This strategy often makes
     * updates faster, because building new Image and Grid
     * elements "from scratch" is very expensive.  However,
     * strictly speaking, GChart is holding onto memory it no
     * longer needs to render the chart <i>right now</i>--which
     * would normally be considered a memory leak if it were not
     * being done deliberately.  <p>
     *
     * If <tt>optimizeForMemory</tt> is set to <tt>true</tt>,
     * GChart will (at the very next <tt>update()</tt> call) free
     * up any Image or Grid elements that are no longer required
     * to render the current chart.  Should a chart's size grow back
     * to a former size, the subsequent update would be slower,
     * though.
     *
     * <p> Charts that use exactly the same number of Image and
     * Grid elements for each update (for example a bar chart
     * where the number of bars is fixed) will see no impact on
     * either memory use or update speeds by setting this
     * parameter.  Charts that have a highly variable number of
     * Image or Grid elements (for example, a chart whose number
     * of points varies randomly between 5 and 500) may see a
     * very large impact on speed (false is faster) or memory
     * (true is more compact).
     * <p>
     *
     * The setting of this parameter never has any impact on the
     * speed or memory used on the <i>very first</i> chart
     * update.
     * <p>
     *
     * In one test using the future oil price simulation chart of
     * GChart's live demo (which has only small changes in the
     * number of elements required to render the chart between
     * updates) setting this parameter to true made the updates,
     * on average, around 10% slower, but also reduced the memory
     * footprint by around 2%.
     *
     * @param optimizeForMemory <tt>true</tt> to optimize updates
     * to use less memory, <tt>false</tt> (the default) to
     * optimize them to use less time.
     *
     * @see #update update
     *
     */
    public void setOptimizeForMemory(boolean optimizeForMemory) {
       this.optimizeForMemory = optimizeForMemory;
    }
  /**
   ** Specifies the amount of padding to add just inside of the
   ** chart's border, as a CSS padding specification string.
   ** <p>
   ** 
   ** <p>
   ** The default padding is <tt>USE_CSS</tt>.
   **
   ** <p>
   **
   ** @param cssPadding the width of the padding, as a CSS padding
   **   specification string
   **   (e.g. use "1px" to introduce a 1 pixel padding
   **   just between the chart' border and the chart itself)
   **
   ** @see #getPadding getPadding
   ** @see #setBorderWidth setBorderWidth
   ** @see #setBorderStyle(String) setBorderStyle
   ** @see #setBorderColor(String) setBorderColor
   ** @see #USE_CSS USE_CSS
   **/
  public void setPadding(String cssPadding) {
     chartDecorationsChanged = true;
     padding = cssPadding;
  }

     /**
      ** Specifies the background color of the area of the chart
      ** in which symbols representing curve data are displayed
      **  
      **
      ** <p>
      ** For more information on standard CSS color
      ** specifications see the discussion in
      ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
      ** <p>
      **       
      ** @param cssColor CSS color string defining the plot
      **    area's background color
      **
      ** @see #getPlotAreaBackgroundColor getPlotAreaBackgroundColor
      **/
     public void setPlotAreaBackgroundColor(String cssColor) {
        Curve c = getSystemCurve(PLOTAREA_ID);
        c.getSymbol().setBackgroundColor(cssColor);
     }

     /**
      ** Specifies the color of the border around the area of the
      ** chart in which symbols representing curve data are
      ** displayed.
      **  
      **
      ** <p>
      ** For more information on standard CSS color
      ** specifications see the discussion in
      ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
      ** <p>
      **       
      ** @param cssColor CSS color string defining the color of
      **    the plot area's border
      **
      ** @see #getPlotAreaBorderColor getPlotAreaBorderColor
      **/
     public void setPlotAreaBorderColor(String cssColor) {
        Curve c = getSystemCurve(PLOTAREA_ID);
        c.getSymbol().setBorderColor(cssColor);
     }
     /**
      ** Specifies the width of the border around the area of the
      ** chart in which symbols representing curve data are
      ** displayed.
      **  
      ** @param width the width, in pixels, of the border around
      **   the plot area
      **
      ** @see #getPlotAreaBorderWidth getPlotAreaBorderWidth
      **/
     public void setPlotAreaBorderWidth(int width) {
        Curve c = getSystemCurve(PLOTAREA_ID);
        c.getSymbol().setBorderWidth(width);
     }
  /**
   ** Sets style of the border around the chart's plot area
   ** (the rectangular area where the curves are drawn).
   **
   ** <p>
   **
   ** <p>
   ** @param borderStyle a CSS border style such as
   ** "solid", "dotted", "dashed", etc.
   **
   ** @see #getPlotAreaBorderStyle getPlotAreaBorderStyle
   ** @see #setPlotAreaBackgroundColor setPlotAreaBackgroundColor
   ** @see #setPlotAreaBorderColor setPlotAreaBorderColor
   **/
     public void setPlotAreaBorderStyle(String borderStyle) {
        Curve c = getSystemCurve(PLOTAREA_ID);
        c.getSymbol().setBorderStyle(borderStyle);
     }

     /**
      * Sets the image URL that defines the background of
      * the GChart plot area. The GChart plot area is the
      * rectangular region defined by the x and y axes of
      * the plot, but does not include those axes (or
      * their ticks).
      * <p>
      * Note that by default, or if this URL is set to <tt>null</tt>,
      * GChart will use the URL returned by
      * <tt>getBlankImageURL</tt>.
      * <p>
      *
      * <small><b>Ideas/tips for using the plot area background
      * URL:</b>
      * <blockquote>
      * <ol>
      <li> It's often best to
      *  exactly match the width and height of the image
      *  with the GChart plot area width and height
      *  (defined via (via <tt>setChartSize</tt>). Otherwise,
      *  the image will be scaled up or down to fit the
      *  plot area, which usually doesn't look that great.
      *
      *  <li>Note that since a Google Chart API url is just
      *  an image url to GChart, you can easily use a
      *  Google Chart API url to define the background of an
      *  otherwise client-side chart. For example, you
      *  might place a static 3-D pie chart behind
      *  a rapidly changing client-side GChart bar chart.
      * 
      * <li> Note that this method's image will appear <i>behind</i>
      * every gridline and curve on the chart.  To overlay
      * images <i>on top of</i> the gridlines or other curves, or
      * even to place them outside of the plot area, use a
      * dedicated curve and its symbol's <tt>setImageURL</tt>
      * method, or simply embed such images within HTML-defined
      * point annotations.
      * </ol>
      *
      * </blockquote></small>
      *
      * @see #getPlotAreaImageURL getPlotAreaImageURL
      * @see #setBlankImageURL setBlankImageURL
      * @see GChart.Symbol#setImageURL setImageURL
      *
      * @param imageURL URL of the image used as the background
      * of the plot area. 
      *
      */
    
     public void setPlotAreaImageURL(String imageURL) {
        Curve c = getSystemCurve(PLOTAREA_ID);
        c.getSymbol().setImageURL(imageURL);
     }

     /** @deprecated
      **
      ** Equivalent to
      ** <tt>setClipToPlotArea(!showOffChartPoints)</tt>.
      ** Use that method instead.
      ** <p>
      **
      ** <small>
      ** As of GChart 2.5, the clip-to-plot-area algorithm no
      ** longer drops the entire symbol if it's x,y coordinates
      ** are outside of the plot area; instead, it clips them
      ** off in the traditional "<tt>overflow: hidden</tt>" manner.
      ** Though unlikely you would need to, there is no easy way
      ** to recreate the previous behavior. <p>
      **
      ** This change was made so that both rectangular HTML and
      ** continuous, canvas-rendered
      ** chart elements would be clipped in a consistent and
      ** sensible way.
      ** </small>
      **
      ** @see #setClipToPlotArea setClipToPlotArea
      **
      **/
     public void setShowOffChartPoints(boolean showOffChartPoints) {
        setClipToPlotArea(!showOffChartPoints);
    }


    /** @deprecated
     **
     ** Equivalent to
     ** setClipToDecoratedChart(!showOffDecoratedChart), please
     ** use that method instead.
     **
     ** @see #setClipToDecoratedChart setClipToDecoratedChart
     **/
    public void setShowOffDecoratedChartGlyphs(boolean showOffDecoratedChartGlyphs) {
       setClipToDecoratedChart(!showOffDecoratedChartGlyphs);
    }

     /**
      * Returns the curve that the mouse "brush" is currently
      * "touching" (the so-called "hovered over" point), or <tt>null</tt>
      * if none.
      * <p>
      *
      * Convenience method equivalent to (when the touched point is
      * not <tt>null</tt>) <tt>getTouchedPoint().getParent()</tt>.
      * See <tt>getTouchedPoint</tt> for full details.
      * <p>
      *
      *
      * See the <tt>setBrushHeight</tt> method for the rules
      * GChart uses to determine the currently touched point.
      * <p>
      *
      *
      * @return a reference to the curve that the mouse "brush"
      * is currently "touching".
      *
      * @see #getTouchedPoint getTouchedPoint
      * @see Symbol#setBrushHeight setBrushHeight
      * @see Symbol#setHoverSelectionSymbolType
      *      setHoverSelectionSymbolType
      *
      */
     public Curve getTouchedCurve() {
        Curve result = null;
        if (null != getTouchedPoint())
           result = getTouchedPoint().getParent();
        return result;
     }

     /**
      * Returns the point that the mouse "brush" is currently
      * "touching" (the so-called "hovered over" point), or <tt>null</tt>
      * if none.
      *
      * <p>
      <small> <i>Fine-print:</i> If the chart clicked on needs an
      *  update, this method returns the touched point <i>as
      *  of the last time the chart's in-browser (DOM) display was
      *  up-to-date</i>. If you don't assure that your chart's DOM display
      *  is up-to-date via other means (e.g. updating right after you
      *  change its specifications) a quick check with the
      *  <tt>isUpdateNeeded</tt> method and a subsequent <tt>update</tt>
      *  before accessing the touched point can be a good strategy.
      *  <p> </small>
      *  
      *
      * See the <tt>setBrushHeight</tt> method for the rules
      * GChart uses to determine the currently touched point.
      * <p>
      *
      * <small>
      * <i>Warning:</i> The currently touched point, on FF2 (but not in
      * IE7) can be changed (or set to <tt>null</tt>) by invoking
      * <tt>Window.alert</tt>. Though I originally expected that such
      * a modal alert box would "eat" all mouse events (and it does
      * just that in IE7) in FF2 (and possibly other browsers)
      * some mouse events on the alert box are also passed on up to
      * the GChart. It's best for applications that need to "lock on"
      * to the <i>initially</i> touched point to grab a
      * reference to the touched point <i>before</i> performing any
      * activity that allows the user to interact with the
      * browser in ways that could possibly generate GChart-visible
      * mouse events.
      * </small>
      * <p>
      *
      * @return a reference to the point that the mouse "brush"
      * is currently "touching".
      *
      * @see #getTouchedCurve getTouchedCurve
      * @see #touch touch
      * @see Symbol#setBrushHeight setBrushHeight
      * @see Symbol#setHoverSelectionSymbolType
      *      setHoverSelectionSymbolType
      * @see #isUpdateNeeded isUpdateNeeded
      * @see #update update
      * @see Axis#getMouseCoordinate getMouseCoordinate
      * @see Axis#clientToModel clientToModel
      * @see Axis#modelToClient modelToClient
      * @see Axis#pixelToModel pixelToModel
      * @see Axis#modelToPixel modelToPixel
      *
      */
     public Curve.Point getTouchedPoint() {
        return plotPanel.touchedPoint;
     }

    
    /**
     * Sets the number of pixels, in the horizontal
     * dimension, available for curve display. Note that
     * this curve display area does <i>not</i> include the
     * axes themselves, their tick marks, their labels, etc.
     *
     * <p>
     *
     * <i>Note</i>: Most modern display devices use "square"
     * pixels, that is, pixels whose width and height are
     * the same. GChart tacitly assumes square pixels in
     * many of its default settings.
     *
     *
     * @param xChartSize the number of x-pixels in the chart region
     *   used for curve display.
     *
     * @see #getXChartSize getXChartSize
     * @see #getXChartSizeDecorated getXChartSizeDecorated
     * @see #setYChartSize setYChartSize
     *
     */
    public void setXChartSize(int xChartSize) {       
       chartDecorationsChanged = true;
       this.xChartSize = xChartSize;
       Curve c = getSystemCurve(PLOTAREA_ID);
       c.getSymbol().setWidth(xChartSize);
    }

    /**
     * Sets the number of pixels, in the vertical dimension,
     * available for curve display. Note that this curve
     * display region of the chart does <i>not</i> include
     * the axes themselves, their tick marks, labels, etc.
     *
     * <p>
     *
     * <i>Note</i>: Most modern display devices use "square"
     * pixels, that is, pixels whose width and height are
     * the same. GChart tacitly assumes square pixels in
     * many of its default settings.
     *
     * @param yChartSize the number of y-pixels in the chart region
     *   used for curve display.
     *
     * @see #getYChartSize getYChartSize
     * @see #getYChartSizeDecorated getYChartSizeDecorated
     * @see #setXChartSize setXChartSize
     *
     */
    public void setYChartSize(int yChartSize) {
      chartDecorationsChanged = true;
      this.yChartSize = yChartSize;
      Curve c = getSystemCurve(PLOTAREA_ID);
      c.getSymbol().setHeight(yChartSize);
    }

    /**
     * Simulates the user "touching" a point with the mouse, by
     * performing those operations that occur when the user "hovers
     * over" the specified point. In detail, this method does the
     * following:<p>
     *
     * <ol>
     *
     *  <li> The specified point is made the currently "touched point"
     *  (this is the reference returned by <tt>getTouchedPoint</tt>). <p>
     *
     *  <li>If the previously touched point had a hover widget,
     *  that hover widget's <tt>hoverCleanup</tt> method is called.<p>
     *
     *  <li>If the touched point has an associated hover widget, that
     *  widget's <tt>hoverUpdate</tt> method is called.<p>
     *
     *  <li> Any hover selection feedback or hover annotation on
     *  any previously touched point is removed.<p>
     *
     *  <li>Any hover annotation for the newly touched point is
     *  displayed as per the various hover annotation related
     *  specifications (e.g.  <tt>setHoverLocation</tt>) associated with
     *  the symbol used to render the point.<p>
     *
     *  <li> Any selection feedback for the newly touched point is
     *  displayed in accord with the hover selection feedback
     *  specificiations (e.g.  <tt>setHoverSelectionBorderColor</tt>)
     *  associated with the symbol used to render the point.<p>
    
     *  </ol>
     *
     * Using <tt>null</tt> as the point to touch simulates
     * the user moving the mouse into a region where it is not
     * touching any point (for example, off the chart entirely).
     * <p>
     *
     * Note that, as with all chart specification changes, you must
     * invoke <tt>update</tt> before the point selection and other
     * changes associated with this method will appear on the chart.
     * <p>
     *
     * <i>Tip:</i> The touched point can sometimes be used in lieu of a
     * point selection capability (which GChart lacks). For example, a
     * dialog box that allowed users to choose data points by their
     * names could "touch" the point associated with a user-selected
     * name in order to highlight it on the chart.
     *
     * @param pointToTouch this method will perform appropriate
     *   operations (as described above) in order to simulate the user
     *   "touching" this point with their mouse.
     *
     * @see #getTouchedPoint getTouchedPoint
     * @see #getTouchedCurve getTouchedCurve
     * @see HoverUpdateable#hoverUpdate hoverUpdate
     * @see HoverUpdateable#hoverCleanup hoverCleanup
     * @see Symbol#setHoverWidget setHoverWidget
     * @see Symbol#setHoverLocation setHoverLocation
     * @see Symbol#setHoverSelectionBorderColor
     * setHoverSelectionBorderColor
     * @see Axis#getMouseCoordinate getMouseCoordinate
     * @see Axis#clientToModel clientToModel
     * @see Axis#modelToClient modelToClient
     * @see Axis#pixelToModel pixelToModel
     * @see Axis#modelToPixel modelToPixel
     *
     */
    public void touch(Curve.Point pointToTouch) {
       plotPanel.touch(pointToTouch);
    }
      /**
       ** Builds a chart that reflects current user-specified
       ** chart specs (curve data, symbol choices, etc.)
       ** <p>
       **
       ** Before any of the chart specifications of the other
       ** methods of this class will actually be visible
       ** on the chart, you must call this method.
       ** <p>
       **
       ** Typically, for efficiency, you would call this
       ** method only after you had made all of the desired
       ** chart specifications via the other methods.
       **
       ** <p>
       **
       ** By default, updates are optimized for speed, and this
       ** can end up wasting (usually not too much, though there
       ** are exceptions) memory.  To optimize for memory
       ** instead, use the <tt>setOptimizeForMemory</tt> method.
       ** <p>
       **
       ** For a discussion of Client-side GChart update times and
       ** how minimize them, see
       ** <a
       ** href="{@docRoot}/com/googlecode/gchart/client/doc-files/tipsformakingupdatesfaster.html">
       ** Tips for Making Client-side GChart Updates Faster</a>.
       ** <p>
       **
       ** <i>Note</i> Hover feedback is disabled whenever the currently
       ** rendered chart does not match current chart specs, that is,
       ** whenever <tt>isUpdateNeeded</tt> returns <tt>true</tt>.  Thus,
       ** to assure that hover feedback remains operational once your
       ** code returns control to the browser, be sure to call
       ** <tt>update()</tt> after making a series of changes to your
       ** chart's properties.
       ** <p>
       **
       ** Understanding how <tt>update</tt> impacts visibility and size:
       ** <p>
       ** <blockquote>
       ** <small>
       ** Due to an implementation-related limitation,
       ** <tt>visibility: hidden</tt> won't hide a GChart
       ** (<tt>update</tt>
       ** commandeers the visibility attribute).  Instead use
       ** <tt>display: none</tt> or, equivalently:
       **
       ** <pre>
       **    myGChart.setVisible(false);
       ** </pre>
       **
       ** If you need to avoid <tt>display: none</tt> (it can change
       ** page layout), you can also hide a GChart via lines such as:
       **
       ** <pre>
       **    DOM.setStyleAttribute(myGChart.getElement(),"overflow","hidden");
       **    myGChart.setPixelSize(0, 0);
       ** </pre>
       **
       ** This later approach gives you the option of leaving the top
       ** corner of the GChart visible, etc. Note that, with the next
       ** <tt>update</tt>, GChart will overwrite your size (based on the
       ** GChart properties that define the size of the the chart, such
       ** as <tt>setChartSize</tt> and <tt>set*Thickness</tt>)
       ** and your <tt>overflow:hidden</tt> (based on
       ** <tt>setClipToDecoratedChart</tt>) specifications. To preserve
       ** them (or in other special cases) you may need to apply such
       ** settings to an enclosing parent element.
       **
       ** </small>
       ** </blockquote>
       **
       **
       ** @param option determines how the touched (or "hovered
       ** over") point changes as a result of this update. See
       ** <tt>TouchedPointUpdateOption</tt> for the available
       ** choices.
       **
       ** @see TouchedPointUpdateOption TouchedPointUpdateOption
       ** @see #setOptimizeForMemory setOptimizeForMemory
       ** @see #isUpdateNeeded isUpdateNeeded
       **
       **/
      public void update(TouchedPointUpdateOption option) {

         /*
          * This method defines each curve's default pie slice
          * orientations, and also separates each curve's points
          * into the vertically or horizontally banded bins,
          * that GChart needs to perform the hit testing
          * that allows it to emulate "touching" points with
          * the mouse.
          * <p>
          *
          * Therefore, this line must come first.
          *
          */
         assembleChart();
                
         if (TouchedPointUpdateOption.TOUCHED_POINT_LOCKED == option) {
             // must re-touch (point position, hover-config can change)
             plotPanel.touch(plotPanel.touchedPoint);
         }
         else if (TouchedPointUpdateOption.TOUCHED_POINT_CLEARED == option) {
            // if needed, will clear out touched point & related feedback
            plotPanel.touch(null);
         }
         else if (TouchedPointUpdateOption.TOUCHED_POINT_UPDATED == option) {
           // re-determine which point is underneath the mouse now...
            plotPanel.retouchObjectAtMousePosition();
         }

         /*
          * Because hover feedback curves come at the end of the curve
          * list, given how GChart's rendering process works, this
          * second call only has to update these hover feedback curves
          * (so it's not like we are really building the chart twice)
          *
          */
         assembleChart();
            
      }

      /**
       * Updates the chart, using an appropriate default touched point
       * update option, depending on if hover touching is enabled or
       * not.<p>
       *
       * A convenience method equivalent to:
       * <p>
       *
       * <pre>
       *  if (getHoverTouchingEnabled())
       *     update(TouchedPointUpdateOption.TOUCHED_POINT_UPDATED);
       *  else
       *    update(TouchedPointUpdateOption.TOUCHED_POINT_LOCKED);
       * </pre>
       *
       *
       * @see #update(TouchedPointUpdateOption) update(TouchedPointUpdateOption)
       * @see #setHoverTouchingEnabled setHoverTouchingEnabled
       *
       */
      public void update() {
         if (getHoverTouchingEnabled())
            update(TouchedPointUpdateOption.TOUCHED_POINT_UPDATED);
         else
           update(TouchedPointUpdateOption.TOUCHED_POINT_LOCKED);
      }

    // constructs the chart within the chart panel from current specs
    private void assembleChart() {
          
      if (chartDecorationsChanged ||
          xAxis.limitsChanged() ||
          yAxis.limitsChanged() ||
          y2Axis.limitsChanged() ) {
        plotPanel.reset(xChartSize, yChartSize,
                        hasYAxis(), hasY2Axis(),
                        xAxis, yAxis, y2Axis);
        GChart.setFontFamily(this,getFontFamily());
        GChart.setBackgroundColor(this, getBackgroundColor());
        GChart.setBorderColor(this, getBorderColor());
        GChart.setBorderStyle(this,getBorderStyle());
        GChart.setBorderWidth(this, getBorderWidth());
        GChart.setPadding(this,getPadding());
        GChart.setOverflow(this, getClipToDecoratedChart() ?
                           "hidden" : "visible");

        this.setPixelSize(plotPanel.getXChartSizeDecoratedQuickly(),
                          plotPanel.getYChartSizeDecoratedQuickly());
        updateDecorations(plotPanel.getXChartSizeDecoratedQuickly());
        xAxis.rememberLimits();
        yAxis.rememberLimits();
        y2Axis.rememberLimits();
        invalidateEveryCurve();
        chartDecorationsChanged = false;
      }
      // actually renders chart, including internal curves used
      // to represent the decorations (title, axis labels, etc.)
      realizePlotPanel();

// To avoid order-of-magnitude FF2 performance hit on busy pages,
// first time, must add plotPanel only AFTER building chart     
      if (plotPanel != chartPanel.getWidget()) {
         chartPanel.add(plotPanel);
/*        
* Due to how GChart plays around with visible elements contained inside
* hidden elements to align it's labels properly, if we allowed top
* level <tt>visibility:hidden</tt> the result would be that everything
* <i>except</i> annotations would be invisible.
* <p>
*
* We can prevent such
* weird behavior by setting <tt>visibility:visible</tt> on the top
* level element; this setting effectively short-circuits any
* top level visibility setting the user may have made. <p>
*
* Users must either use <tt>display:none</tt> (as the Widget method
* <tt>setVisible</tt> does) or create an enclosing 0-sized div with
* <tt>overflow:hidden</tt>) to hide a GChart.
* <p>
*
*/
        DOM.setStyleAttribute(getElement(), "visibility","visible");        
      }
      else
/*
* Without these 2 lines IE7 won't repaint GChart's annotations.
* The lines are not needed in FF2; an IE7 bug is suspected.<p>
*
* I got this workaround from <a href=
* "http://examples.roughian.com">Ian Bambury</a> as part of <a
* href="http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/4c54d8b4aea7f98b/6efd1ab4e5fc0e7b?#6efd1ab4e5fc0e7b">
* this discussion on the GWT forum</a>.
* <p>
*
* (Note comment regarding need for explicit visibility above).
*
*/
        DOM.setStyleAttribute(getElement(), "visibility","hidden");
        DOM.setStyleAttribute(getElement(), "visibility","visible");
    
      }
    }    
    // create a Grid representing the chart legend.
    private Grid createLegend(PlotPanel pp) {
       Grid result = new Grid(getNVisibleCurvesOnLegend(), 2);
       int iVisible = 0;
/*
* Simply eliminating the border entirely is a valid transparency
* emulation for the legend (no positional shifting is needed as is
* needed for the images used to draw the main chart's curves) because
* the legend is always positioned by its center point, and the border
* extends around the entire legend key, so removing it does not result
* in any change to the legend key's center position.  <p>
*
* If multiple legend locations (beyond the current "always centered in
* a band along the right edge" option) were ever supported, appropriate
* positional shifts would then have to be introduced to emulate
* transparent borders.
*
*/
       GChart.setBorderWidth(result,
         TRANSPARENT_BORDER_COLOR == getLegendBorderColor() ? 0 :
                             Math.abs(getLegendBorderWidth()));
       GChart.setBorderColor(result,
          TRANSPARENT_BORDER_COLOR == getLegendBorderColor() ?
                             "transparent" :
                             getLegendBorderColor());
       GChart.setBorderStyle(result, getLegendBorderStyle());
       GChart.setBackgroundColor(result, getLegendBackgroundColor());
       int nCurves = getNCurves();
       for (int i = 0; i < nCurves; i++) {
          Curve c = getSystemCurve(i);
          if (c.isVisible() && c.getLegendLabel()!=null) {
             double symBorderFraction =
               c.getSymbol().getBorderWidth()/
                Math.max(
                  Math.max(1.0,c.getSymbol().getFillThickness()),
                  Math.max(c.getSymbol().getWidth(pp),
                           c.getSymbol().getHeight(pp, c.onY2())));
             Image icon =
               c.getSymbol().getSymbolType().createIconImage(
                c.getSymbol(), getLegendFontSize(),
                symBorderFraction);
                 
             result.setWidget(iVisible, 0, icon);
             result.getCellFormatter().setAlignment(iVisible, 0,
                HasHorizontalAlignment.ALIGN_CENTER,
                HasVerticalAlignment.ALIGN_MIDDLE);
            
             HTML label = new HTML(c.getLegendLabel());
             GChart.setFontWeight(label, getLegendFontWeight());
             GChart.setFontStyle(label, getLegendFontStyle());
             GChart.setColor(label, getLegendFontColor());
             GChart.setFontSize(label, getLegendFontSize());
                          
             result.setWidget(iVisible, 1, label);
             result.getCellFormatter().setAlignment(iVisible, 1,
                HasHorizontalAlignment.ALIGN_LEFT,
                HasVerticalAlignment.ALIGN_MIDDLE);

             iVisible++;
          }
       }
       return result;
    }

    // returns char-width-based default legend thickness
    private int getDefaultLegendThickness() {
       final int EXTRA_WIDTH = 5// allow for padding & symbol
       int maxLen = 0;
       int nCurves = getNCurves();
       for (int i = 0; i < nCurves; i++) {
          Curve c = getSystemCurve(i);
          if (c.isVisible() && null != c.getLegendLabel()) {
             maxLen = Math.max(maxLen,
                               htmlWidth(c.getLegendLabel()));
          }
       }
       int result = (int) ((maxLen + EXTRA_WIDTH) *
                           getLegendFontSize() *
                TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
       return result;
    }

    private int getNVisibleCurvesOnLegend() {
       int result = 0;
       int nCurves = getNCurves();
       for (int i = 0; i < nCurves; i++) {
         if (getSystemCurve(i).isVisible() &&
             getSystemCurve(i).getLegendLabel() != null) result++;
       }
       return result;
    }

    // Defines a default curve border color when curves first created
    private void setDefaultBorderColor(Curve curve, int index) {
          curve.getSymbol().setBorderColor(
              defaultSymbolBorderColors[
                     index % defaultSymbolBorderColors.length]);
    }

    // Is the symbol type one of the special ANCHOR_MOUSE types,
    // whose position varies with the mouse cursor location?
   private boolean isMouseAnchored(SymbolType symbolType) {
     boolean result = false
     if (SymbolType.ANCHOR_MOUSE == symbolType ||
         SymbolType.ANCHOR_MOUSE_SNAP_TO_X == symbolType ||    
         SymbolType.ANCHOR_MOUSE_SNAP_TO_Y == symbolType)
        result = true;
     return result;
   }
    
   // renders the curve in the plot panel
   private void realizeCurve(Curve c) {
      if (!c.isValidated()) {
         int internalIndex = getInternalCurveIndex(c);
         int rpIndex = getRenderingPanelIndex(internalIndex);
         GraphicsRenderingPanel grp =
            plotPanel.getGraphicsRenderingPanel(rpIndex);
         AnnotationRenderingPanel arp =
            plotPanel.getAnnotationRenderingPanel(rpIndex);
         if (PlotPanel.DECORATIVE_RENDERING_PANEL_INDEX == rpIndex) {
         // background panel only gets initialized for first curve
            if (0 == internalIndex) {
               // background panel never uses canvas
               grp.beginRendering(null);
               arp.beginRendering();
            }
            c.setWasCanvasRendered(false);
         }
         else if (0 == c.getSymbol().getFillSpacing() &&  // continuous fill
             0 < c.getSymbol().getFillThickness() && // non-empty fill
             null != getCanvasFactory() &&           // canvas available
             c.isVisible()) {
           grp.maybeAddCanvas();
           Rectangle canvasRegion = c.getContainingRectangle(plotPanel);
           grp.beginRendering(canvasRegion);
           arp.beginRendering();
           c.setWasCanvasRendered(true);
         }
         else { // does not use canvas, or it is invisible
           grp.beginRendering(null);
           arp.beginRendering();
           c.setWasCanvasRendered(false);
         }
        
         if (c.isVisible()) {
            // Separate points into vertical/horizontal band-bins provided
            // 1) it is not a system curve and 2) it is not of a type whose
            // position follows the mouse (and thus has no fixed location
            // suitable for banding) and 3) at least one kind of hover feedback
            // is being provided for the curve.
            if (getCurveIndex(c) >= 0 &&
                !isMouseAnchored(c.getSymbol().getSymbolType()) &&
                (c.getSymbol().getHoverSelectionEnabled() ||
                 c.getSymbol().getHoverAnnotationEnabled()))
               c.bandSeparatePoints();
            else // hit test banding calcs unneeded; skip them for speed.
               c.clearBandList();

            // Note: these lines must come AFTER band separation lines above
            int nPoints = c.getNPoints();
            for (int j = 0; j < nPoints; j++) {
               c.realizePoint(plotPanel, grp, arp, j);
            }
         }
         // only end background panel rendering w last background curve
         if (PlotPanel.DECORATIVE_RENDERING_PANEL_INDEX != rpIndex ||
             internalIndex == N_PRE_SYSTEM_CURVES-1) {
            grp.endRendering();
            arp.endRendering();
         }
         // else it's a background panel curve, and not the last one
        
         c.isValidated = true;
       }
   }

   // marks every curve, including system curves, as needing an update
   private void invalidateEveryCurve() {
     for (int i = 0; i < curves.size(); i++) {
        curves.get(i).invalidate();
     }
   }
   // marks every developer-accessible curve as needing an update
   private void invalidateAccessibleCurves() {
     int nCurves = getNCurves();
     for (int i = 0; i < nCurves; i++) {
        getSystemCurve(i).invalidate();
     }
   }

   // invalidates every curve that has a pie slice type
   void invalidateAllSlices() {
      int nCurves = getNCurves();
      for (int i = 0; i < nCurves; i++) {
        Curve c = getSystemCurve(i);
        if (c.getSymbol().getSymbolType() instanceof
            SymbolType.PieSliceSymbolType)
           c.invalidate();
      }
   }
   // Invalidates every pie slice curve whose orientation could
   // depend on the orientation of the given curve
   void invalidateDependentSlices(int iFirstCurve) {
      // only user defined curve can have slice dependency relationships
      if (isSystemCurveIndex(iFirstCurve)) return;
      int nCurves = getNCurves();     
      for (int i = iFirstCurve; i < nCurves; i++) {
        Curve c = getSystemCurve(i);
        if (c.getSymbol().getSymbolType() instanceof
            SymbolType.PieSliceSymbolType)
           c.invalidate();
        else if (i == iFirstCurve) // if first curve isn't a slice,
           break;                  // there are no dependent slices
      }
   }
  
   // Defines the default pie slice orientations for every pie-slice curve
   private void setDefaultPieSliceOrientations() {    
      setLastPieSliceOrientation(getInitialPieSliceOrientation());
      int nCurves = getNCurves();
      for (int i = 0; i < nCurves; i++) {
         Curve c = getSystemCurve(i);
         // keep track of default next orientation for pie slices 
         // (must do this even if we don't have to redraw slice)
         if (c.getSymbol().getSymbolType() instanceof SymbolType.PieSliceSymbolType) {
            c.getSymbol().setDefaultPieSliceOrientation(
               getLastPieSliceOrientation());
            setLastPieSliceOrientation(
                                       c.getSymbol().getDecodedPieSliceOrientation()
                                       + c.getSymbol().getPieSliceSize());
         }
      }
   }

     
   private void realizePlotPanel() {

     setDefaultPieSliceOrientations();
     /*
      * Render both system curves (those with negative ids that
      * are used to render title, ticks, etc.) and ordinary curves.
      */
     int nCurves = getNCurves();
     for (int i = -N_SYSTEM_CURVES; i < nCurves; i++) {
       Curve c = getSystemCurve(i);
       realizeCurve(c);
     }                                
    
  }

   /* Returns true if the rendering panel index is associated
    * with one of the internal, hover-feedback curves.
    * <p>
    *
    * This method relies on the fact that rendering panels
    * appear in this order:
    * <p>
    *
    * <ol>
    * <li> a single rp that renders all chart decorations
    * <li> getNCurves() rps (1 for each developer-defined curve)
    * <li> the two rendering panels associated with the two
    *   system-defined hover feedback curves
    * </ol>
    *
    */
   boolean isHoverFeedbackRenderingPanel(int rpIndex) {
      boolean result = rpIndex > getNCurves();
      return result;
   }


   /*
    * This code works around a bug in GWTCanvas that can cause
    * (in IE) previously rendered VML elements to have their fill
    * and stroke color, and stroke thickness properties revert to
    * some sort of defaults (I saw white, black, and 1px in my
    * tests) when the canvas is re-inserted into the DOM.
    *
    * See TestGChart55.java and TestGChart55a.java for more
    * info on the GWTCanvas bug that makes this code neccessary.
    *
    */
  
   // avoids inefficiency of re-rendering in most common case
   private boolean wasUnloaded = false;
   @Override protected void onUnload() {
      super.onUnload();
      wasUnloaded = true;
   }
  
   @Override protected void onLoad() {
      super.onLoad();
      if (wasUnloaded && plotPanel.getRenderingPanelCount() > 0) {
         boolean isUpToDate = !isUpdateNeeded();
         int nCurves = getNCurves();
         for (int i = 0; i < nCurves; i++) {
            Curve c = getCurve(i);
            if (c.isCanvasRendered()) {
               c.invalidate();
               if (isUpToDate)
                  realizeCurve(c);
               // else since chart needs update, presume they will
               // update later, no need to auto-patch things up
               // (and simple patch-rerender won't work anyway).
            }
         }
      }
      // else never inserted/rendered; skip patchup-rerendering
   }
   
} // end of class GChart
TOP

Related Classes of com.googlecode.gchart.client.GChart

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.