001    /* 
002     * Copyright 2007,2008,2009 John C. Gunther
003     * 
004     * Licensed under the Apache License, Version 2.0 (the
005     * "License"); you may not use this file except in compliance
006     * with the License. You may obtain a copy of the License at:
007     * 
008     *  http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing,
011     * software distributed under the License is distributed on an
012     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013     * either express or implied. See the License for the specific
014     * language governing permissions and limitations under the
015     * License.
016     * 
017     */
018    package com.googlecode.gchart.client;
019    
020    import com.google.gwt.i18n.client.DateTimeFormat;
021    import com.google.gwt.i18n.client.NumberFormat;
022    import com.google.gwt.user.client.DOM;
023    import com.google.gwt.user.client.Window;
024    import com.google.gwt.dom.client.Element;
025    import com.google.gwt.dom.client.EventTarget;
026    import com.google.gwt.event.dom.client.ClickEvent;
027    import com.google.gwt.event.dom.client.ClickHandler;
028    import com.google.gwt.event.dom.client.HasClickHandlers;
029    import com.google.gwt.event.shared.HandlerRegistration;
030    import com.google.gwt.user.client.Event;
031    import com.google.gwt.user.client.ui.AbsolutePanel;
032    import com.google.gwt.user.client.ui.Composite;
033    import com.google.gwt.user.client.ui.Grid;
034    import com.google.gwt.user.client.ui.HasHTML;
035    import com.google.gwt.user.client.ui.HasHorizontalAlignment;
036    import com.google.gwt.user.client.ui.HasText;
037    import com.google.gwt.user.client.ui.HasVerticalAlignment;
038    import com.google.gwt.user.client.ui.HTML;
039    import com.google.gwt.user.client.ui.Image;
040    import com.google.gwt.user.client.ui.SimplePanel;
041    import com.google.gwt.user.client.ui.UIObject;
042    import com.google.gwt.user.client.ui.Widget;
043    import java.util.ArrayList;
044    import java.util.Date;
045    import com.google.gwt.core.client.GWT;
046    
047    /**
048     * A GChart can represent and display a line chart, a bar chart,
049     * a pie chart, an area chart, or a chart that contains arbitrary
050     * combinations of line, bar, pie, and/or area based curves.
051     * 
052     * <p>
053     * For detailed examples, with screen shots, visit the
054     * <a href="package-summary.html#ChartGallery">
055     * Chart Gallery</a>. 
056     * 
057     * <p>
058     * For detailed instructions on how to integrate Client-side GChart
059     * into your GWT application, see
060     * <a href="package-summary.html#InstallingGChart">
061     * Installing Client-side GChart</a>.
062     * 
063     * <p>
064     * <b>CSS Style Rule</b>
065     * <ul>
066     * .gchart-GChart { the GChart's primary top-level styles }
067     * </ul>
068     *
069     *
070     * It is sometimes more natural to consider certain CSS
071     * attributes as properties of a GChart Java object. So, GChart
072     * supports "CSS convenience methods" that let you (optionally) use
073     * Java to specify GChart CSS attributes such as
074     * <tt>border-color</tt> and <tt>background-color</tt>. See
075     * {@link #USE_CSS USE_CSS} for a detailed description of these
076     * CSS convenience methods--which won't interfere with standard
077     * CSS-based specifications if you never invoke them.
078     *
079     * 
080     **/ 
081    
082    public class GChart extends Composite implements HasClickHandlers {
083    
084       
085       /**
086        ** Defines the location of a data point's annotation or hover
087        ** annotation (which can be defined by either plain text, HTML,
088        ** or a widget) relative to the location of that point's
089        ** symbol.  The "Field Summary"
090        ** section below lists all available annotation locations.
091        ** <p>
092        **
093        ** The default annotation location is {@link
094        ** AnnotationLocation#SOUTH SOUTH} for annotations and
095        ** is symbol-type-dependent for hover annotations. See the
096        ** <tt>setHoverLocation</tt> method for list of these defaults.
097        ** 
098        ** <p>
099        ** 
100        ** You can further adjust the position of a point's
101        ** annotation (or hover annotation) by specifying non-zero
102        ** positional shifts via the <tt>setAnnotationXShift</tt>
103        ** and <tt>setAnnotationYShift</tt> (or via the
104        ** <tt>setHoverXShift</tt>, <tt>setHoverYShift</tt>),
105        ** and <tt>setHoverAnnotationSymbolType</tt> methods for
106        ** hover annotations).
107        ** <p>
108        ** 
109        ** @see Curve.Point#setAnnotationLocation Point.setAnnotationLocation
110        ** @see Curve.Point#setAnnotationXShift Point.setAnnotationXShift
111        ** @see Curve.Point#setAnnotationYShift Point.setAnnotationYShift
112        ** @see Symbol#setHoverLocation Symbol.setHoverLocation
113        ** @see Symbol#setHoverAnnotationSymbolType
114        ** Symbol.setHoverAnnotationSymbolType
115        ** @see Symbol#setHoverXShift Symbol.setHoverXShift
116        ** @see Symbol#setHoverYShift Symbol.setHoverYShift
117        ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
118        ** 
119        **/ 
120       public static final class AnnotationLocation {
121    // non-public tagging-only locations used by ANCHOR_MOUSE_* symbol types
122          static final AnnotationLocation AT_THE_MOUSE =
123            new AnnotationLocation(0,0);
124          static final AnnotationLocation AT_THE_MOUSE_SNAP_TO_X =
125            new AnnotationLocation(0,0);
126          static final AnnotationLocation AT_THE_MOUSE_SNAP_TO_Y =
127            new AnnotationLocation(0,0);
128          /**
129           ** Specifies that a point's annotation (label) should
130           ** be positioned so as to be centered on the symbol
131           ** used to represent the point.
132           **
133           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
134           **/
135          public static final AnnotationLocation CENTER =
136            new AnnotationLocation(0,0);
137    
138          private static final AnnotationLocation north =
139             new AnnotationLocation(0,-1);
140          private static final AnnotationLocation west =
141            new AnnotationLocation(-1, 0);
142          private static final AnnotationLocation south =
143              new AnnotationLocation(0, 1);
144          
145          /**
146           ** Specifies that a point's annotation (label) should be
147           ** placed just above, and centered horizontally on,
148           ** vertical bars that grow down from a horizontal
149           ** baseline, and just below, and centered horizontally on,
150           ** vertical bars that grow up from a horizontal baseline.
151           **
152           ** <p>
153           **
154           ** This another name for
155           ** <tt>AnnotationLocation.NORTH</tt>. Its sole purpose is
156           ** to clarify/document the behavior of this location type
157           ** when used in conjunction with curves that employ 
158           ** <tt>VBAR_BASELINE_*</tt> symbol types.
159           **
160           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
161           ** @see SymbolType#VBAR_BASELINE_CENTER SymbolType.VBAR_BASELINE_CENTER
162           ** 
163           **/
164          public static final AnnotationLocation 
165              CLOSEST_TO_HORIZONTAL_BASELINE = north;
166          
167          /**
168           ** Specifies that a point's annotation (label) should be
169           ** placed just to the right of, and centered vertically
170           ** on, horizontal bars that grow left from a vertical
171           ** baseline, and just to the left of, and centered
172           ** vertically on, horizontal bars that grow right from a
173           ** vertical baseline.
174           **
175           ** <p>
176           **
177           ** This another name for
178           ** <tt>AnnotationLocation.WEST</tt>. Its sole purpose is
179           ** to clarify/document the behavior of this location type
180           ** when used in conjunction with curves that employ the
181           ** <tt>HBAR_BASELINE_*</tt> symbol types.
182           **
183           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
184           ** @see SymbolType#HBAR_BASELINE_CENTER SymbolType.HBAR_BASELINE_CENTER
185           ** 
186           **/
187           
188          public static final AnnotationLocation 
189             CLOSEST_TO_VERTICAL_BASELINE = west;
190          
191          /**
192           ** Specifies that a point's annotation (label) should
193           ** be positioned just to the right of, and vertically
194           ** centered on, the symbol used to represent the
195           ** point.
196           **
197           ** @see Curve.Point#setAnnotationLocation
198           **/
199          public static final AnnotationLocation EAST =
200             new AnnotationLocation(1, 0);
201    
202          /**
203           ** Specifies that a point's annotation (label) should be
204           ** placed just below, and centered horizontally on,
205           ** vertical bars that grow down from a horizontal
206           ** baseline, and just above, and centered horizontally on,
207           ** vertical bars that grow up from a horizontal baseline.
208           **
209           ** <p>
210           **
211           ** This another name for
212           ** <tt>AnnotationLocation.SOUTH</tt>. Its sole purpose is
213           ** to clarify/document the behavior of this location type
214           ** when used in conjunction with curves that employ
215           ** <tt>VBAR_BASELINE_*</tt> symbol types.
216           **
217           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
218           ** @see SymbolType#VBAR_BASELINE_CENTER SymbolType.VBAR_BASELINE_CENTER
219           ** 
220           **/
221          public static final AnnotationLocation
222              FARTHEST_FROM_HORIZONTAL_BASELINE = south;
223          
224          /**
225           ** Specifies that a point's annotation (label) should be
226           ** placed just to the left of, and centered vertically on,
227           ** horizontal bars that grow left from a vertical
228           ** baseline, and just to the right of, and centered
229           ** vertically on, horizontal bars that grow right from a
230           ** vertical baseline.
231           **
232           ** <p>
233           **
234           ** This another name for
235           ** <tt>AnnotationLocation.EAST</tt>. Its sole purpose is
236           ** to clarify/document the behavior of this location type
237           ** when used in conjunction with curves that employ the
238           ** <tt>HBAR_BASELINE_*</tt> family of symbol types.
239           **
240           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
241           ** @see SymbolType#HBAR_BASELINE_CENTER SymbolType.HBAR_BASELINE_CENTER
242           ** 
243           **/
244          public static final AnnotationLocation 
245              FARTHEST_FROM_VERTICAL_BASELINE = EAST;
246          
247    
248          /**
249           ** Specifies that a point's annotation (label) should
250           ** be positioned just inside, and centered on, the
251           ** arc side of a pie slice.
252           ** <p>
253           ** 
254           ** You can move a pie slice's annotation a specific number
255           ** of pixels radially away from (or towards) the pie
256           ** center by passing a positive (or negative) argument to
257           ** the associated <tt>Point</tt>'s
258           ** <tt>setAnnotationXShift</tt> method.
259           ** 
260           ** <p> This is pie-friendly synonym for, and when used
261           ** with non-pie symbol types will behave exactly the same
262           ** as, <tt>AnnotationLocation.NORTH</tt>
263           **      
264           ** @see #OUTSIDE_PIE_ARC OUTSIDE_PIE_ARC
265           ** @see #ON_PIE_ARC ON_PIE_ARC
266           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
267           ** @see AnnotationLocation#NORTH NORTH
268           **/
269          public static final AnnotationLocation INSIDE_PIE_ARC = north;
270    
271          /**
272           ** Specifies that a point's annotation (label) should
273           ** be positioned just above, and horizontally centered on,
274           ** the symbol used to represent the point.
275           **
276           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
277           **/
278          public static final AnnotationLocation NORTH = north;
279    
280    
281          /**
282           ** Specifies that a point's annotation (label) should
283           ** be positioned just to the right of and above,
284           ** the symbol used to represent the
285           ** point.
286           **
287           ** @see Curve.Point#setAnnotationLocation
288           **/
289          public static final AnnotationLocation NORTHEAST =
290            new AnnotationLocation(1, -1);
291    
292          /**
293           ** Specifies that a point's annotation (label) should
294           ** be positioned just to the left of and above,
295           ** the symbol used to represent the
296           ** point.
297           **
298           ** @see Curve.Point#setAnnotationLocation
299           **/
300          public static final AnnotationLocation NORTHWEST =
301            new AnnotationLocation(-1, -1);
302    
303          
304          /**
305           ** Specifies that a point's annotation (label) should
306           ** be centered on the center-point of the
307           ** arc side of a pie slice.
308           ** <p>
309           **
310           ** You can move a pie slice's annotation a specific number
311           ** of pixels radially away from (or towards) the pie
312           ** center by passing a positive (or negative) argument to
313           ** the associated <tt>Point</tt>'s
314           ** <tt>setAnnotationXShift</tt> method.
315           ** 
316           **
317           ** 
318           ** <p> This is pie-friendly synonym for, and when used
319           ** with non-pie symbol types will behave exactly the same
320           ** as, <tt>AnnotationLocation.CENTER</tt>
321           **
322           ** @see #OUTSIDE_PIE_ARC OUTSIDE_PIE_ARC
323           ** @see #INSIDE_PIE_ARC INSIDE_PIE_ARC
324           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
325           ** @see AnnotationLocation#CENTER CENTER
326           ** 
327           **/
328          public static final AnnotationLocation ON_PIE_ARC = CENTER;
329    
330          /**
331           ** Specifies that a point's annotation (label) should
332           ** be positioned just outside, and centered on, the
333           ** arc side of a pie slice.
334           ** <p>
335           ** 
336           ** You can move a pie slice's annotation a specific number
337           ** of pixels radially away from (or towards) the pie
338           ** center by passing a positive (or negative) argument to
339           ** the associated <tt>Point</tt>'s
340           ** <tt>setAnnotationXShift</tt> method.
341           ** 
342           ** <p> This is pie-friendly synonym for, and when used
343           ** with non-pie symbol types will behave exactly the same
344           ** as, <tt>AnnotationLocation.SOUTH</tt>
345           **
346           ** @see #INSIDE_PIE_ARC INSIDE_PIE_ARC
347           ** @see #ON_PIE_ARC ON_PIE_ARC
348           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
349           ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
350           ** @see AnnotationLocation#SOUTH SOUTH
351           **/
352          public static final AnnotationLocation OUTSIDE_PIE_ARC = south;
353    
354          /**
355           ** Specifies that a point's annotation (label) should
356           ** be positioned just below, and horizontally centered on,
357           ** the symbol used to represent the point.
358           **
359           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
360           **/
361          public static final AnnotationLocation SOUTH = south;
362    
363          
364          /**
365           ** Specifies that a point's annotation (label) should
366           ** be positioned just to the right of and below,
367           ** the symbol used to represent the
368           ** point.
369           **
370           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
371           **/
372          public static final AnnotationLocation SOUTHEAST =
373            new AnnotationLocation(1, 1);
374          /**
375           ** Specifies that a point's annotation (label) should
376           ** be positioned just to the left of and below,
377           ** the symbol used to represent the
378           ** point.
379           **
380           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
381           **/
382          public static final AnnotationLocation SOUTHWEST =
383            new AnnotationLocation(-1, 1);
384    
385          /**
386           ** Specifies that a point's annotation (label) should
387           ** be positioned just to the left of, and vertically
388           ** centered on, the symbol used to represent the
389           ** point.
390           **
391           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
392           **/
393          public static final AnnotationLocation WEST = west;
394    
395          
396          
397          // these multiply the width and height of the annotation and
398          // the symbol it is attached to in order to define the
399          // center of the annotation (see equations in later code),
400          // and thus the upper left corner anchoring point.
401          private int heightMultiplier;
402          private int widthMultiplier;
403          private AnnotationLocation(int widthMultiplier,
404                                 int heightMultiplier) {
405            validateMultipliers(widthMultiplier, heightMultiplier);
406            this.widthMultiplier = widthMultiplier;
407            this.heightMultiplier = heightMultiplier;
408          }
409          // retrieves a static location given its multipliers
410          private static AnnotationLocation getAnnotationLocation(
411             int widthMultiplier, int heightMultiplier) {
412            final AnnotationLocation[][] locationMap = {
413             {NORTHWEST, NORTH, NORTHEAST},
414             {WEST, CENTER, EAST},
415             {SOUTHWEST, SOUTH, SOUTHEAST}};
416       // assumes both multiplier are -1, 0, or 1   
417            AnnotationLocation result =
418             locationMap[heightMultiplier+1][widthMultiplier+1];
419           return result;                
420          }
421                                                       
422          // Negative width or height "turn the symbol inside-out",
423          // requiring a corresponding "reflection" of annotation
424          // location (only needed for baseline-based bar symbols)
425          static AnnotationLocation transform(AnnotationLocation a,
426                                              int signWidth,
427                                              int signHeight) {
428            AnnotationLocation result = a;
429            if (signWidth < 0 || signHeight < 0) 
430               result = getAnnotationLocation(
431                    signWidth*a.widthMultiplier,
432                    signHeight*a.heightMultiplier);
433    
434            return result;
435          }
436          // These define the alignment of the label within it's
437          // containing 1 x 1 Grid. For example, if this
438          // containing grid is to the left of the labeled
439          // symbol (widthMultiplier==-1) the horizontal
440          // alignment will be ALIGN_RIGHT, so as to bring the
441          // contained label flush against the left edge of the
442          // labeled symbol.
443          HasHorizontalAlignment.HorizontalAlignmentConstant
444                getHorizontalAlignment() {
445             HasHorizontalAlignment.HorizontalAlignmentConstant result;
446             if (widthMultiplier == -1)
447                result = HasHorizontalAlignment.ALIGN_RIGHT;
448             else if (widthMultiplier == 0)
449                result = HasHorizontalAlignment.ALIGN_CENTER;
450             else if (widthMultiplier == 1)
451                result = HasHorizontalAlignment.ALIGN_LEFT;
452             else
453                throw new IllegalStateException(
454                   "Invalid widthMultiplier: " + widthMultiplier +
455                   " 1, 0, or -1 were expected.");
456             return result;
457          }
458    
459          /* Given the x-coordinate at the center of the symbol
460           * that this annotation annotates, the annotation's
461           * width, and the symbol's width, this method returns
462           * the x-coordinate of the upper left corner of
463           * this annotation.
464           */
465          int getUpperLeftX(double x, double w, double symbolW) {
466             int result = (int) Math.round(x +
467               (widthMultiplier * (w + symbolW) - w)/2.);
468             return result;
469          }
470    
471          /* analogous to getUpperLeftX, except for the y-coordinate */
472          int getUpperLeftY(double y, double h, double symbolH) {
473             int result = (int) Math.round(y +
474               (heightMultiplier * (h + symbolH) - h)/2.);
475             return result;
476          }
477          // analogous to getHorizontalAlignment  
478          HasVerticalAlignment.VerticalAlignmentConstant
479                getVerticalAlignment() {
480             HasVerticalAlignment.VerticalAlignmentConstant result;
481             if (heightMultiplier == -1)
482                result = HasVerticalAlignment.ALIGN_BOTTOM;
483             else if (heightMultiplier == 0)
484                result = HasVerticalAlignment.ALIGN_MIDDLE;
485             else if (heightMultiplier == 1)
486                result = HasVerticalAlignment.ALIGN_TOP;
487             else
488                throw new IllegalStateException(
489                   "Invalid heightMultiplier: " + heightMultiplier +
490                   " -1, 0, or 1 were expected.");
491             return result;
492          }
493    
494    
495           /*
496            * This method returns the annotation location whose
497            * "attachment point" keeps the annotation either
498            * completely outside, centered on, or completely inside
499            * (depending on if the heightMultiplier of this annotation
500            * is 1, 0, or -1) the point on the pie's circumference
501            * associated with the given angle.
502            * <p>
503            *
504            * The use of heightMultiplier rather than widthMultiplier
505            * is somewhat arbitrary, but was chosen so that the
506            * NORTH, CENTER, and SOUTH annotation locations have the
507            * same interpretation for a pie slice whose bisecting
508            * radius points due south (due south is the default initial
509            * pie slice orientation) and for a 1px x 1px BOX_CENTER
510            * type symbol positioned at the due south position on the
511            * pie's circumference. As the pie-slice-arc-bisection
512            * point moves clockwise around the pie perimeter, the
513            * attachment point (except for vertically-centered
514            * annotations, which remain centered on the pie arc) also
515            * moves clockwise, but in discrete jumps (e.g. from
516            * NORTH, to NORTHEAST, to EAST, to SOUTHEAST, to SOUTH,
517            * etc. for annotations inside the pie) so the annotation
518            * remains appropriately attached to the center of the
519            * slice's arc as the angle changes.
520            * 
521            */
522         AnnotationLocation decodePieLocation(double thetaMid) {
523            // a sin or cos that is small enough so that the
524            // associated angle is horizontal (for sines) or vertical
525            // (for cosines) enough to warrant use of a "centered"
526            // annotation location.
527           final double LOOKS_VERTICAL_OR_HORIZONTAL_DELTA = 0.1; 
528           double sinTheta = Math.sin(thetaMid); 
529           double cosTheta = Math.cos(thetaMid); 
530           int pieTransformedWidthMultiplier = heightMultiplier *
531              ((cosTheta < -LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? -1 :
532              ((cosTheta > LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? 1 : 0));
533           int pieTransformedHeightMultiplier = heightMultiplier *
534              ((sinTheta < -LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? 1 :
535              ((sinTheta > LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? -1 : 0));
536                 
537           return getAnnotationLocation(pieTransformedWidthMultiplier,
538                                        pieTransformedHeightMultiplier);
539             
540         }
541          
542       } // end of class AnnotationLocation
543    
544    
545      /**
546       ** Represents an axis of the chart, for example, the x,
547       ** y, or y2 axis. An axis consists of the axis itself,
548       ** along with its tick marks, tick labels and gridlines.
549       **
550       ** @see XAxis XAxis
551       ** @see YAxis YAxis
552       ** @see Y2Axis Y2Axis
553       ** @see #getXAxis getXAxis 
554       ** @see #getYAxis getYAxis
555       ** @see #getY2Axis getY2Axis
556       ** 
557       **
558       **/ 
559       public abstract class Axis {
560         protected boolean isHorizontalAxis; // true for X, false for Y
561         protected int ticksId;              // sys curve representing ticks
562         protected int gridlinesId;          // sys curve representing gridlines
563         protected int axisId;               // sys curve representing axis line
564         protected int axisPosition;         // +/-1 for right/left axes
565         protected TickLocation tickLocation = DEFAULT_TICK_LOCATION;
566         private int nCurvesVisibleOnAxis = 0;  // # of developer curves on axis.
567                                           // (count does not include system or
568                                           // invisible curves)
569    
570         void incrementCurves() {nCurvesVisibleOnAxis++;}
571         void decrementCurves() {nCurvesVisibleOnAxis--;}
572         
573         protected class AxisLimits { 
574            double min; double max; // in user-defined model units
575            AxisLimits(double min, double max) {
576               this.min = min;
577               this.max = max;
578            }
579            boolean equals(AxisLimits al) {
580               boolean result =  (al.min == min && al.max == max);
581               return result;
582            }
583         }
584    
585         // different initial curr, prev ==> "limits have changed" state
586         private AxisLimits currentLimits = new AxisLimits(
587            Double.MAX_VALUE, -Double.MAX_VALUE);
588         private AxisLimits previousLimits = new AxisLimits(
589            -Double.MAX_VALUE, Double.MAX_VALUE);
590         
591         private Widget axisLabel;
592         protected int axisLabelThickness = GChart.NAI;
593         private boolean hasGridlines = false;
594         protected int tickCount = DEFAULT_TICK_COUNT;
595    // axes auto-scale whenever min or max are NaN.
596         protected double axisMax = Double.NaN;
597         protected double axisMin = Double.NaN;
598    // this symbol facilitates rendering of gridlines & axes
599         protected String tickLabelFontColor = DEFAULT_TICK_LABEL_FONT_COLOR;
600    // In CSS font-size pixels. These define the height of each
601    // character; our code relies on the rule of thumb that
602    // character width is approximately 3/5th this height to
603    // obtain a reasonably tight upper bound on tick label widths.
604         protected int tickLabelFontSize = DEFAULT_TICK_LABEL_FONTSIZE;
605         protected String tickLabelFontStyle = DEFAULT_TICK_LABEL_FONT_STYLE;
606         protected String tickLabelFontWeight = DEFAULT_TICK_LABEL_FONT_WEIGHT;
607    
608         protected String tickLabelFormat = DEFAULT_TICK_LABEL_FORMAT;
609         protected int tickLabelThickness = GChart.NAI;
610         protected int tickLabelPadding = 0;
611         protected int ticksPerLabel = 1;
612         protected int ticksPerGridline = 1;
613         protected int tickLength = DEFAULT_TICK_LENGTH;
614         
615         // this symbol facilitates rendering of labeled tick-marks
616         protected int tickThickness = DEFAULT_TICK_THICKNESS;
617             
618         // is axis itself visible (has no impact ticks or their labels)
619         boolean axisVisible = true;
620         
621         /**
622          * Adds a tick on this axis at the specified position.
623          * Note that explicitly adding a single tick via this method
624          * will eliminate any implicitly generated ticks associated with the
625          * <tt>setTickCount</tt> method.
626          * <p>
627          * The label associated with this tick will be generated by
628          * applying the format specified via <tt>setTickLabelFormat</tt>
629          * to the specified position.
630          * <p>
631          * This is a convenience method equivalent to
632          * <tt>addTick(tickPosition, thisAxis.formatAsTickLabel(tickPosition), GChart.NAI,
633          * GChart.NAI)</tt>. See {@link #addTick(double,String,int,int)
634          * addTick(tickPosition,tickLabel,widthUpperBound,heightUpperBound)}
635          * for details.
636          * 
637          * @param tickPosition the position, in model units,
638          *   along this axis at which this tick is displayed.
639          *   For example, if the axis range goes from 0 to 100,
640          *   a tick at position 50 would appear in the middle of
641          *   the axis.
642          *
643          * @see #clearTicks clearTicks
644          * @see #addTick(double,String) addTick(double,String)
645          * @see #addTick(double,String,int,int) addTick(double,String,int,int)
646          * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
647          * @see #formatAsTickLabel formatAsTickLabel
648          * @see #setTickCount setTickCount
649          * @see #setTickLabelFormat setTickLabelFormat
650          * @see #setTickLabelFontStyle setTickLabelFontStyle
651          * @see #setTickLabelFontColor setTickLabelFontColor
652          * @see #setTickLabelFontWeight setTickLabelFontWeight
653          * @see #setTickLabelFontSize setTickLabelFontSize
654          * 
655          */
656         public void addTick(double tickPosition) {
657            addTick(tickPosition, formatAsTickLabel(tickPosition));  
658         }
659         // adds a labeled tick mark via this Axis' special system tick curve
660         private void addTickAsPoint(double tickPosition, String tickLabel,
661                                     Widget tickWidget, int widthUpperBound,
662                                     int heightUpperBound) {
663    
664           Curve c = getSystemCurve(ticksId);
665           if (isHorizontalAxis)  
666              c.addPoint(tickPosition, axisPosition*Double.MAX_VALUE);
667           else 
668              c.addPoint(axisPosition*Double.MAX_VALUE, tickPosition);
669    
670           // unlabeled tick--we are done, so return to save time
671           if (null == tickLabel && null == tickWidget)
672              return;
673           
674           //add an annotation representing the tick label
675           Curve.Point p = c.getPoint();
676           if (isHorizontalAxis) { 
677              // below tick on X, above it on (the future) X2
678              p.setAnnotationLocation(
679                (axisPosition < 0) ? AnnotationLocation.SOUTH :
680                                     AnnotationLocation.NORTH);
681              if (tickLabelPadding != 0) // padding < 0 is rare but allowed
682                 p.setAnnotationYShift(axisPosition*tickLabelPadding);
683              // else stick with default of 0 y-shift
684    
685           }
686           else {
687              // to left of tick mark on Y, to right of it on Y2
688              p.setAnnotationLocation(
689                   (axisPosition < 0) ? AnnotationLocation.WEST :
690                                        AnnotationLocation.EAST);
691              if (tickLabelPadding != 0) 
692                p.setAnnotationXShift(axisPosition*tickLabelPadding);
693              // else stick with default of 0 x-shift
694           }
695    
696           
697           if (null != tickLabel) 
698              p.setAnnotationText(tickLabel, widthUpperBound, heightUpperBound);
699           else if (null != tickWidget)
700              p.setAnnotationWidget(tickWidget, widthUpperBound, heightUpperBound);
701           
702           p.setAnnotationFontSize(getTickLabelFontSize());
703           p.setAnnotationFontStyle(getTickLabelFontStyle());
704           p.setAnnotationFontColor(getTickLabelFontColor());
705           p.setAnnotationFontWeight(getTickLabelFontWeight());
706    
707         }
708         /**
709          * Adds a tick at the specified position with the specified
710          * label on this axis, whose width and height are within
711          * the specified upper-bounds.
712          * 
713          * <p>
714          * Note that explicitly adding a single tick via this method
715          * will eliminate any auto-generated ticks associated with the
716          * <tt>setTickCount</tt> method. 
717          * 
718          * <p>
719          * Use this method to specify unusually spaced
720          * tick marks with labels that do not directly
721          * reflect the position (for example, for a logarithmic axis,
722          * or for a bar chart with special keyword-type labels, or
723          * a time axis that places date and time on two separate lines).
724          * 
725          * @param tickPosition the position, in model units, along
726          *   this axis at which the tick is displayed.
727          *   For example, if the axis range goes from 0 to 1,
728          *   a tick at position 0.5 would appear in the middle of
729          *   the axis.
730          *   
731          *  @param tickLabel the label for this tick.  HTML is
732          *  supported in tick labels, but it must be prefixed by
733          *  <tt>&lt;html&gt</tt>.  See the {@link
734          *  Curve.Point#setAnnotationText(String,int,int)
735          *  setAnnotationText} method for more information.
736          *
737          *  @param widthUpperBound an upper bound on the width of
738          *  the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
739          *  get GChart to estimate this width for you. See the
740          *  <tt>setAnnotationText</tt> method for more information.
741          * 
742          *  @param heightUpperBound an upper bound on the height of
743          *  the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
744          *  get GChart to estimate this height for you. See the
745          *  <tt>setAnnotationText</tt> method for more information.
746          *
747          * @see #clearTicks clearTicks
748          * @see #addTick(double) addTick(double)
749          * @see #addTick(double,String) addTick(double,String)
750          * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
751          * @see #setTickCount setTickCount
752          * @see #setTickLabelFormat setTickLabelFormat
753          * @see #setTickLabelFontSize setTickLabelFontSize
754          * @see #setTickLabelFontStyle setTickLabelFontStyle
755          * @see #setTickLabelFontColor setTickLabelFontColor
756          * @see #setTickLabelFontWeight setTickLabelFontWeight
757          * @see Curve.Point#setAnnotationText(String,int,int)
758          *      setAnnotationText
759          * @see Curve.Point#setAnnotationWidget setAnnotationWidget
760          *      
761          */
762         public void addTick(double tickPosition, String tickLabel,
763                             int widthUpperBound,
764                             int heightUpperBound) {
765           chartDecorationsChanged = true;
766           if (GChart.NAI != tickCount) { // clear out any auto-generated ticks
767             Curve cTicks = getSystemCurve(ticksId);  
768             cTicks.clearPoints();
769             tickCount = GChart.NAI;
770           }
771           addTickAsPoint(tickPosition, tickLabel, null, widthUpperBound,
772                    heightUpperBound);       
773         }
774    
775         /**
776          * Adds a tick at the specified position with the specified
777          * label on this axis.
778          * <p>
779          *
780          * This is a convenience method equivalent to
781          * <tt>addTick(tickPosition, tickLabel, GChart.NAI,
782          * GChart.NAI)</tt>. Most applications can usually just
783          * use this convenience method. See {@link #addTick(double,String,int,int)
784          * addTick(tickPosition,tickLabel,
785          * widthUpperBound,heightUpperBound)} for the fine print.
786          *
787          * @param tickPosition the position, in model units, along
788          *   this axis at which the tick is displayed.
789          *   
790          * @param tickLabel the plain text or
791          * (<tt>&lt;html&gt</tt>-prefixed) HTML defining the tick's
792          * label.
793          *
794          * @see #addTick(double,String,int,int) addTick(double,String,int,int)
795          * @see #addTick(double,Widget) addTick(double,Widget)
796          * 
797          */ 
798         public void addTick(double tickPosition,
799                             String tickLabel) {
800              addTick(tickPosition, tickLabel, GChart.NAI, GChart.NAI);
801         }
802         
803         /**
804          *  Adds a widget-defined tick label at the specified
805          *  position, whose width and height are within
806          *  the specified upper-bounds.
807          * 
808          *
809          *  <p>
810          ** 
811          ** This method is similar to
812          ** <tt>addTick(double,String,int,int)</tt> except that it
813          ** uses a widget, rather than a string, to define the
814          ** tick's label. Although the string-based method is faster
815          ** on first chart rendering, and uses less memory, the
816          ** widget-based method allows you to change the label
817          ** independently of the chart--potentially bypassing (or
818          ** speeding up) expensive chart updates later on.
819          **
820          ** <p>
821          ** 
822          ** You might use a widget-based tick label to pop up a
823          ** dialog that allows the user to edit the parameters
824          ** defining the axis (min, max, etc.) whenever they click
825          ** on one of the tick labels on that axis, to define
826          ** hovertext that appears when the user mouses over
827          ** a tick label, to use images for your tick labels, etc. 
828          **
829          * @param tickPosition the position, in model units, along
830          *   this axis at which the tick is displayed.
831          *   For example, if the axis range goes from 0 to 1,
832          *   a tick at position 0.5 would appear in the middle of
833          *   the axis.
834          *   
835          *  @param tickWidget the label for this tick, as defined
836          *  by any GWT Widget.
837          *
838          *  @param widthUpperBound an upper bound on the width of
839          *  the widget, in pixels. If this and the next
840          *  parameter are omitted, GChart will use
841          *  <tt>DEFAULT_WIDGET_WIDTH_UPPERBOUND</tt>.
842          *  
843          *  @param heightUpperBound an upper bound on the height of
844          *  the widget, in pixels. If this and the previous
845          *  parameter are omitted, GChart will use <tt>
846          *  DEFAULT_WIDGET_HEIGHT_UPPERBOUND</tt>
847          *
848          *  @see #addTick(double,Widget) addTick(double,Widget) 
849          *  @see #addTick(double,String,int,int) addTick(double,String,int,int) 
850          *  @see Curve.Point#setAnnotationWidget setAnnotationWidget
851          *  @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
852          *  @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
853          **/ 
854         public void addTick(double tickPosition, 
855                             Widget tickWidget,
856                             int widthUpperBound,
857                             int heightUpperBound) {
858           chartDecorationsChanged = true;
859           if (GChart.NAI != tickCount) { // clear out any auto-generated ticks
860             Curve cTicks = getSystemCurve(ticksId);  
861             cTicks.clearPoints();
862             tickCount = GChart.NAI;
863           }
864           addTickAsPoint(tickPosition, null, tickWidget, widthUpperBound,
865                    heightUpperBound);
866            
867         }
868    
869         /**
870          *  Adds a Widget-defined tick label at the specified
871          *  position. Convenience method equivalent to
872          *  <tt>addTick(tickPosition, tickWidget,
873          *  DEFAULT_WIDGET_WIDTH_UPPERBOUND,
874          *  DEFAULT_WIDGET_HEIGHT_UPPERBOUND)</tt>.
875          *  
876          * @param tickPosition the position, in model units, along
877          *   this axis at which the tick is displayed.
878          *   For example, if the axis range goes from 0 to 1,
879          *   a tick at position 0.5 would appear in the middle of
880          *   the axis.
881          *   
882          * @param tickWidget the label for this tick, as defined
883          *  by any GWT Widget.
884          *  
885          * @see #addTick(double,Widget,int,int)
886          * addTick(double,Widget,int,int)
887          *  
888          */
889         public void addTick(double tickPosition,
890                             Widget tickWidget) {
891            addTick(tickPosition, tickWidget,
892                    DEFAULT_WIDGET_WIDTH_UPPERBOUND,
893                    DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
894         }
895         /**
896          *
897          * Removes all ticks from this axis. Specifically,
898          * erases any ticks that were explicitly specified via
899          * <tt>addTick</tt>, and also sets the tick count to 0.
900          * <p>
901          * 
902          * @see #setTickCount setTickCount
903          * @see #addTick(double) addTick(double)
904          * @see #addTick(double,String) addTick(double,String)
905          * @see #addTick(double,String,int,int) addTick(double,String,int,int)
906          * @see #addTick(double,Widget) addTick(double,Widget)
907          * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
908          * 
909          */
910         public void clearTicks() {
911            chartDecorationsChanged = true;
912            tickCount = GChart.NAI;
913            Curve c = getSystemCurve(ticksId);
914            c.clearPoints();
915         }
916    
917    
918         /**
919          * Converts a pixel, client-window coordinate position along this
920          * axis into the model units associated with this axis.
921          * <p>
922          *
923          * For example, if the client coordinate associated with 
924          * this axis' midpoint were passed to this method, it would return
925          * <tt>(getAxisMin() + getAxisMax())/2.0</tt>.
926          * <p>
927          *
928          * <small> Note that the client/model coordinate mapping used is as
929          * of the last <tt>update</tt>. Before the first <tt>update</tt>,
930          * this method returns <tt>GChart.NaN</tt>.  This method also
931          * invokes either <tt>getAbsoluteTop</tt> (for the y or y2 axis) or
932          * <tt>getAbsoluteLeft</tt> (for the x axis),
933          * and these GWT methods return 0 if the chart isn't actually
934          * rendered within the browser. So, results likely won't be useful
935          * to you until after the page containing your chart becomes
936          * visible to the user. Since most applications are expected to
937          * invoke this method in response to the user mousing over the
938          * page, these requirements should usually be satisfied.  </small>
939          * <p>
940          *
941          * <small> Saurabh Hirani in <a href=
942          * "http://groups.google.com/group/Google-Web-Toolkit/msg/80301715acb6f719">
943          * this GWT Forum post</a> and in GChart <a
944          * href="http://code.google.com/p/gchart/issues/detail?id=21">issue
945          * #22</a> most recently suggested the need for client to model
946          * coordinate mapping. Client to
947          * model conversion was requested earlier in GChart <a
948          * href="http://code.google.com/p/gchart/issues/detail?id=9">issue
949          * #9</a> from <a href="http://yoxel.com">yoxel.com</a>.
950          * </small>
951          *
952          * @param clientCoordinate a pixel-based coordinate that defines
953          * the dimension associated with this axis in the standard
954          * client window coordinates of GWT.
955          *
956          * @return the location defined by the client-coordinate argument,
957          * but converted into the model units associated
958          * with this axis.
959          *
960          * @see #getMouseCoordinate getMouseCoordinate
961          * @see #modelToClient modelToClient
962          * @see #pixelToModel pixelToModel
963          * @see #modelToPixel modelToPixel
964          * 
965          *
966          */
967         public abstract double clientToModel(int clientCoordinate);
968         
969         // these are used in formatting tick positions into tick labels:
970         private NumberFormat numberFormat =
971            NumberFormat.getFormat(DEFAULT_TICK_LABEL_FORMAT);
972         private DateTimeFormat dateFormat =
973            DateTimeFormat.getShortDateTimeFormat();
974         private final int NUMBER_FORMAT_TYPE = 0;
975         private final int DATE_FORMAT_TYPE = 1;
976         private final int LOG10INVERSE_FORMAT_TYPE = 2;
977         private final int LOG2INVERSE_FORMAT_TYPE = 3;
978         private int tickLabelFormatType = NUMBER_FORMAT_TYPE;
979         /**
980         * 
981         * Applies this axis' tick label format to format a given value.
982         * 
983         * @return the value formated as per this axis' currently specified 
984         * tick label format.
985         * 
986         * @see  #setTickLabelFormat(String) setTickLabelFormat
987         * 
988         */
989         public String formatAsTickLabel(double value) {
990           String result = null;
991           switch (tickLabelFormatType) {
992             case DATE_FORMAT_TYPE:
993               Date transDate = new Date((long) value);
994               result = dateFormat.format(transDate);
995               break;
996             case LOG10INVERSE_FORMAT_TYPE:
997               value = Math.pow(10., value);
998               result = numberFormat.format(value);
999               break;
1000             case LOG2INVERSE_FORMAT_TYPE:
1001               value = Math.pow(2., value);
1002               result = numberFormat.format(value);
1003               break;
1004             default:  
1005               result = numberFormat.format(value);
1006               break;
1007           }
1008           
1009           return result;
1010         }
1011         /**
1012          * @deprecated
1013          *
1014          * Equivalent to the better-named formatAsTickLabel.
1015    
1016          * <p>
1017          *
1018          * @see #formatAsTickLabel formatAsTickLabel
1019          * 
1020          */
1021         public String formatNumberAsTickLabel(double value) {
1022            return formatAsTickLabel(value);
1023         }
1024    
1025         /** Returns the previously specified label of this axis.
1026          **
1027          ** @return the Widget used as the label of this axis
1028          **
1029          ** @see #setAxisLabel setAxisLabel
1030          ** 
1031          */
1032         public Widget getAxisLabel() {
1033           return axisLabel;
1034         }
1035         
1036         /** Returns the thickness of the axis-label-holding region
1037          ** adjacent to the region allocated for this axis' tick labels.
1038          ** <p>
1039          **
1040          ** Note that if the axis label is <tt>null</tt> (the
1041          ** default) then this method always returns 0, since
1042          ** in that case no rectangular region will be allocated
1043          ** for the axis label.
1044          ** <p>
1045          ** 
1046          ** @return the thickness of the axis-label-holding
1047          ** region, in pixels.
1048          **
1049          ** @see #setAxisLabelThickness setAxisLabelThickness
1050          ** 
1051          */
1052         public int getAxisLabelThickness() {
1053            int result = 0;
1054    // Base class implementation is for y axes (x-axis will override). 
1055            final int EXTRA_CHARWIDTH = 2; // 1-char padding on each side
1056            final int DEF_CHARWIDTH = 1; // when widget has no text
1057            if (null == getAxisLabel())
1058               result = 0;
1059            else if (GChart.NAI != axisLabelThickness) 
1060               result = axisLabelThickness;
1061            else if (getAxisLabel() instanceof HasHTML) {
1062               int charWidth = htmlWidth(
1063                  ((HasHTML) (getAxisLabel())).getHTML());
1064               result = (int) Math.round((charWidth + EXTRA_CHARWIDTH) *
1065                          getTickLabelFontSize() *
1066                         TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
1067            }
1068            else if (getAxisLabel() instanceof HasText) {
1069               String text = ((HasText) (getAxisLabel())).getText();
1070               result = (int) Math.round((EXTRA_CHARWIDTH + 
1071                          ((null==text)?0:text.length())) *
1072                          getTickLabelFontSize() *
1073                          TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
1074            }
1075            else // non-text widget. Not a clue, just use def width
1076               result = (int) Math.round(
1077                        (DEF_CHARWIDTH + EXTRA_CHARWIDTH) *
1078                         getTickLabelFontSize() *
1079                         TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
1080            return result;
1081         }
1082         /**
1083          ** Returns the maximum value displayed on this axis.
1084          ** If the explicitly specified maximum value is
1085          ** undefined (<tt>Double.NaN</tt>) the maximum value returned
1086          ** by this function is calculated as the maximum of
1087          ** all of the values either displayed on this axis via
1088          ** points on a curve, or explicitly specified via tick
1089          ** positions. 
1090          **
1091          ** @return maximum value visible on this axis, in
1092          ** "model units" (arbitrary, application-specific,
1093          ** units)
1094          **
1095          ** @see #setAxisMax setAxisMax
1096          ** @see #getDataMin getDataMin
1097          ** @see #getDataMax getDataMax
1098          **/ 
1099         public double getAxisMax() {
1100               
1101            if (!(axisMax!=axisMax)) { // x!=x is a faster isNaN
1102               return axisMax;
1103            }
1104            else if (GChart.NAI != tickCount) { 
1105              return getDataMax();
1106            }
1107            else {
1108               return Math.max(getDataMax(), getTickMax());           
1109            }
1110         }
1111         /**
1112          **
1113          ** Returns the minimum value displayed on this axis.
1114          ** If the minimum value is undefined (<tt>Double.NaN</tt>) the
1115          ** minimum value returned by this function is the
1116          ** minimum of all of the values either displayed on
1117          ** this axis via points on a curve, or explicitly specified
1118          ** via tick positions.
1119          **
1120          ** @return minimum value visible on this axis, in
1121          ** "model units" (arbitrary, application-specific,
1122          ** units)
1123          **
1124          ** @see #setAxisMin setAxisMin
1125          **/ 
1126         public double getAxisMin() {
1127            if (!(axisMin!=axisMin)) { // x!=x is a faster isNaN
1128               return axisMin; // explicitly set
1129            }
1130            else if (GChart.NAI != tickCount) { 
1131               return getDataMin();
1132            }
1133            else {  
1134               return Math.min(getDataMin(), getTickMin());           
1135            }
1136         }
1137    
1138       /** Is axis line visible on the chart? Note that
1139        ** this property only determines the visibility of the axis line
1140        ** itself. It does not control the visibility of the
1141        ** tick marks or tick labels along this axis.
1142        ** <p>
1143        **
1144        ** @return true if the axis line is visible, false otherwise.
1145        **
1146        ** @see #setAxisVisible setAxisVisible
1147        ** 
1148        **/ 
1149         public boolean getAxisVisible() {
1150           return axisVisible;
1151         }
1152    
1153    
1154         
1155         /** Returns the maximum data value associated with values
1156         ** represented on this axis. For example, for the left
1157         ** y-axis, this would be the largest y-value of all points
1158         ** contained in curves that are displayed on the left y-axis.
1159         ** 
1160         ** @return the maximum value associated with values
1161         **   mapped onto this axis.
1162         **
1163         ** @see #getDataMin getDataMin
1164         ** @see #getAxisMax getAxisMax
1165         ** @see #getAxisMin getAxisMin
1166         ** 
1167          */
1168        public abstract double getDataMax();
1169         /** Returns the minimum data value associated with values
1170         ** represented on this axis. For example, for the left
1171         ** y-axis, this would be the smallest y-value of all points
1172         ** contained in curves that are displayed on the left y-axis.
1173         **
1174         ** @return the minimum value associated with values
1175         **   mapped onto this axis.
1176         **
1177         ** @see #getDataMax getDataMax
1178         ** @see #getAxisMax getAxisMax
1179         ** @see #getAxisMin getAxisMax
1180         **
1181          */
1182        public abstract double getDataMin();
1183         
1184         /** Returns the gridline setting previously made with
1185          ** <tt>setHasGridlines</tt>.
1186          **
1187          ** @return true if gridlines have been enabled, false if not.
1188          **
1189          ** @see #setHasGridlines setHasGridlines
1190          ** 
1191          **/
1192         public boolean getHasGridlines() {
1193            return hasGridlines;
1194         }
1195    
1196         /**
1197          * Returns the coordinate along this axis that
1198          * is associated with the last "GChart-tracked" mouse
1199          * location.
1200          * <p>
1201          *
1202          * The coordinate returned is in the "scale" associated
1203          * with the axis. For example, if the axis mininum is
1204          * 0 and the maximum is 100, and the mouse is at the
1205          * axis midpoint, this method would return 50.
1206          * <p>
1207          *
1208          * The main intended use for this method is to allow you to create
1209          * points that, if they have x and y coordinates defined by calling
1210          * this method on appropriate axes, will be positioned on the chart
1211          * at the last GChart-tracked mouse location.
1212          * <p>
1213          *
1214          * As the user moves their mouse over the chart, GChart watches
1215          * those mouse moves and updates it's currently "tracked" mouse
1216          * location.  That internally maintained position is the basis for
1217          * the value returned by this method.  Note that the actual,
1218          * physical, mouse cursor position could differ from this 
1219          * GChart-tracked position because:
1220          * 
1221          * <p>
1222          *
1223          * <ol>
1224          * 
1225          *   <li>The mouse has moved off the chart, and it's
1226          *       GChart-tracked location has become undefined
1227          *       (this method returns <tt>Double.NaN</tt> in that case)
1228          *
1229          *   <li>You have invoked <tt>setHoverTouchingEnabled(false)</tt>
1230          *       which means that mouse moves are no longer tracked,
1231          *       so the last GChart-tracked mouse location will
1232          *       be the last position that the user clicked on.
1233          *
1234          *   <li>You have popped up a modal dialog that "eats" mouse
1235          *       moves so GChart no longer sees them. In that case,
1236          *       the GChart-tracked mouse location is the location the
1237          *       mouse was at when the modal dialog popped up.
1238          *
1239          *    <li>You are mousing over the opened hover widget (popup).
1240          *    Note that, to prevent the user from accidentally "touching"
1241          *    nearby points while interacting with the opened
1242          *    hover widget, GChart ignores mouse moves over
1243          *    the opened hover widget.
1244          *
1245          *    <li>Other, similar, reasons.
1246          *
1247          * </ol>
1248          * <p>
1249          *
1250          * In other words, this routine tells you where, for
1251          * hit testing and hover selection feedback purposes,
1252          * GChart considers the mouse to be, not the actual
1253          * physical location of the mouse. Despite the potential
1254          * for differences, in most cases, with the default
1255          * setting of <tt>setHoverTouchingEnabled(true)</tt>,
1256          * and when you are not over the opened hover widget,
1257          * you can use the value returned by this method
1258          * as if it represented the physical mouse location.
1259          * <p>
1260          * 
1261          * For an example that uses this method to create
1262          * points at the current mouse location within a very
1263          * simple line chart editor, see <a
1264          * href="package-summary.html#GChartExample22a">the
1265          * Chart Gallery's GChartExample22a</a>.  <p>
1266          *
1267          * 
1268          * @return the coordinate, projected along this axis, in
1269          *   the scale defined by this axis, representing the
1270          *   position GChart has currently "tracked" the mouse to,
1271          *   or <tt>Double.NaN</tt> if GChart has tracked the mouse
1272          *   right off the edge of the chart.
1273          *
1274          * @see #clientToModel clientToModel
1275          * @see #modelToClient modelToClient
1276          * @see #pixelToModel pixelToModel
1277          * @see #modelToPixel modelToPixel
1278          * @see GChart#setHoverTouchingEnabled setHoverTouchingEnabled
1279          *   
1280          */
1281         public abstract double getMouseCoordinate();
1282         /**
1283          * Returns the number of visible curves displayed on this axis.
1284          * <p>
1285          * 
1286          * @return the number of visible curves on this axis, or <tt>0</tt> if
1287          * there are no visible curves on this axis.
1288          *
1289          * @see Axis#setVisible setVisible
1290          * 
1291          */
1292         public int getNCurvesVisibleOnAxis() { return nCurvesVisibleOnAxis; }     
1293         /**
1294          ** Returns the number of ticks on this axis.
1295          **
1296          ** @return the number of ticks on this axis.
1297          **
1298          ** @see #setTickCount setTickCount
1299          ** @see #addTick(double) addTick(double)
1300          ** @see #addTick(double,String) addTick(double,String)
1301          ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
1302          ** @see #addTick(double,Widget) addTick(double,Widget)
1303          ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
1304          ** @see #clearTicks clearTicks
1305          ** 
1306          **/
1307         public int getTickCount() {
1308            int result = tickCount;
1309            if (GChart.NAI == tickCount) { 
1310              Curve c = getSystemCurve(ticksId);
1311              result = c.getNPoints();
1312            }
1313            return result;
1314    
1315         }
1316         /**
1317          ** Returns the CSS font-weight specification to be used
1318          ** by this axis' tick labels.
1319          **
1320          ** @return font-weight of this axis' tick labels
1321          **
1322          ** @see #setTickLabelFontWeight setTickLabelFontWeight
1323          **/ 
1324         public String getTickLabelFontWeight() {
1325            return tickLabelFontWeight;
1326         }
1327         /**
1328          ** Returns the color of the font used to display the
1329          **    text of the tick labels on this axis.
1330          **   
1331          **   
1332          ** @return CSS color string defining the color of the text of
1333          **    the tick labels for this axis.
1334          **
1335          ** @see #setTickLabelFontColor setTickLabelFontColor
1336          **
1337          ** @see #DEFAULT_TICK_LABEL_FONT_COLOR DEFAULT_TICK_LABEL_FONT_COLOR
1338          **
1339          **
1340          ** 
1341          **/ 
1342         public String getTickLabelFontColor() {
1343            return tickLabelFontColor;
1344         }
1345    
1346         /**
1347          ** Returns the font-style of the font used to render tick
1348          ** labels on this axis (typically either "italic" or
1349          ** "normal") 
1350          **
1351          ** @return the CSS font-style in which tick labels of this axis
1352          **   are rendered.
1353          **
1354          ** @see #setTickLabelFontStyle setTickLabelFontStyle
1355          **/ 
1356         public String getTickLabelFontStyle() {
1357            return tickLabelFontStyle;
1358         }
1359         /** Returns the CSS font size, in pixels, used for tick labels
1360          ** on this axis.
1361          **
1362          ** @return the tick label font size in pixels
1363          **
1364          ** @see #setTickLabelFontSize setTickLabelFontSize
1365          **/ 
1366         public int getTickLabelFontSize() {
1367            return tickLabelFontSize;
1368         }
1369    
1370         /**
1371         ** Returns the tick label numeric format string for this
1372         ** axis.
1373         **
1374         ** @return numeric format used to generate tick labels.
1375         **
1376         ** @see #setTickLabelFormat setTickLabelFormat
1377         ** 
1378         **/ 
1379        public String getTickLabelFormat() {
1380           return tickLabelFormat;
1381        }
1382         /**
1383         ** Returns the amount of padding (blank space) between the
1384         ** ticks and their labels.<p>
1385         ** 
1386         ** @return amount of padding between ticks and their labels,
1387         ** in pixels.
1388         **
1389         ** @see #setTickLabelPadding setTickLabelPadding
1390         ** 
1391         **/ 
1392        public int getTickLabelPadding() {
1393           return tickLabelPadding;
1394        }
1395        // Does real work of public getTickLabelThickness; flag saves time
1396        // during repeated calls made in updateChartDecorations.
1397        int getTickLabelThickness(boolean needsPopulation) {
1398          int maxLength = 0;
1399          int result;
1400          if (tickLabelThickness != GChart.NAI)
1401            result = tickLabelThickness;
1402          else { // use an heuristic to estimate thickness            
1403           if (needsPopulation) maybePopulateTicks();
1404           Curve c = getSystemCurve(ticksId);
1405           int nTicks = c.getNPoints();
1406           for (int i=0; i < nTicks; i++) {
1407             String tt = c.getPoint(i).getAnnotationText();
1408             if (null != tt)
1409                maxLength = Math.max(maxLength,
1410                                     Annotation.getNumberOfCharsWide(tt));
1411           }
1412           result = (int) Math.round(maxLength * tickLabelFontSize *
1413                    TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
1414          }
1415          return result;
1416        }
1417    
1418        /** Returns the thickness of the band adjacent to
1419         ** this axis that GChart will
1420         ** allocate to hold this axis' tick labels.
1421         ** <p>
1422         **
1423         ** @return width of band, in pixels, GChart will reserve
1424         **   for this axis' tick labels.
1425         **   
1426         ** @see #setTickLabelThickness setTickLabelThickness
1427         ** 
1428         **/ 
1429        public int getTickLabelThickness() {
1430           int result = getTickLabelThickness(true);
1431           return result;
1432        }   
1433    
1434        /**
1435         ** Returns the ratio of the number of ticks to the number of
1436         ** ticks that have an associated gridline. 
1437         **
1438         ** @return number of ticks per gridline for this axis
1439         **
1440         ** @see #setTicksPerGridline setTicksPerGridline
1441         **
1442         **/
1443        public int getTicksPerGridline() {
1444           return ticksPerGridline;
1445        }
1446        /**
1447         ** Returns the ratio of the number of ticks to the number of
1448         ** labeled ticks. 
1449         **
1450         ** @return number of ticks per label.
1451         **
1452         ** @see #setTicksPerLabel setTicksPerLabel
1453         **
1454         **/
1455        public int getTicksPerLabel() {
1456          return ticksPerLabel;
1457        }
1458    
1459         /**
1460         * Returns the length of ticks for this axis.
1461         *
1462         * @return the length of this axis' ticks, in pixels.
1463         *
1464         * @see #setTickLength setTickLength
1465         */
1466        public int getTickLength() {
1467           return tickLength;
1468        }
1469    
1470        // GChart adds a pixel to even, centered, tick lengths (only
1471        // odd-length HTML ticks can be exactly centered on 1px axis)
1472        int getActualTickLength() {
1473           int result = tickLength;
1474           if (TickLocation.CENTERED == tickLocation &&
1475               0 == (tickLength % 2)  && tickLength > 0)
1476              result++;
1477           return result;
1478        }
1479    
1480         /**
1481          * Returns relative location of ticks on this axis.
1482          * <p>
1483          * 
1484          * @see #setTickLocation setTickLocation
1485          *
1486          * @return <tt>TickLocation.INSIDE</tt>,
1487          *         <tt>TickLocation.OUTSIDE</tt>, or
1488          *         <tt>TickLocation.CENTERED</tt>
1489          *
1490          */
1491         public TickLocation getTickLocation() {
1492            return tickLocation;
1493         }
1494        
1495        
1496        /** Returns the amount of space along the axis reserved for
1497         *  the tick marks themselves, in pixels.
1498         *  <p>
1499         *
1500         *  This equals the length of
1501         *  the part of the tick that is outside of the plot area.
1502         *
1503         * @see #setTickLength setTickLength
1504         * @see #setTickLabelPadding setTickLabelPadding
1505         * @see #setTickLocation setTickLocation 
1506         * 
1507         * @return the space GChart will allocate just outside the
1508         * axis to hold any tick marks.
1509         * 
1510         */
1511        
1512        public int getTickSpace() {
1513           int result;
1514           if (TickLocation.CENTERED == tickLocation)
1515             result = (tickLength+1)/2;  // round up to nearest pixel
1516           else if (TickLocation.OUTSIDE == tickLocation)
1517              result = tickLength;
1518           else // INSIDE
1519              result = 0;
1520    
1521           return result;
1522        }
1523    
1524        
1525        /**
1526         * Returns the thickness of ticks for this axis.
1527         *
1528         * @return the thickness of this axis' ticks, in pixels.
1529         *
1530         * @see #setTickThickness setTickThickness
1531         * @see #getTickLength getTickLength
1532         */
1533        public int getTickThickness() {
1534           return tickThickness;
1535        }
1536    
1537    
1538         /**
1539          * Converts a coordinate position in the model units associated
1540          * with this axis into a corresponding coordinate position
1541          * expressed in standard GWT client-window pixel coordinates.
1542          * 
1543          * <p>
1544          *
1545          * For example, consider a completely undecorated chart (no axes,
1546          * tick labels, legend keys, etc.) that exactly fills a
1547          * 1000px wide client window, and whose x-axis min and max
1548          * are 0 and 100. Then <tt>getXAxis().modelToClient(50)</tt> would
1549          * return <tt>500</tt>.
1550          * <p>
1551          *
1552          * <small> Note that the client/model coordinate mapping used is as
1553          * of the last <tt>update</tt>. Before the first <tt>update</tt>,
1554          * this method returns <tt>GChart.NaN</tt>.  This method also
1555          * invokes either <tt>getAbsoluteTop</tt> (for the y or y2 axis) or
1556          * <tt>getAbsoluteLeft</tt> (for the x axis),
1557          * and these GWT methods return 0 if the chart isn't actually
1558          * rendered within the browser. So, results likely won't be useful
1559          * to you until after the page containing your chart becomes
1560          * visible to the user. Since most applications are expected to
1561          * invoke this method in response to the user mousing over the
1562          * page, these requirements should usually be satisfied.  </small>
1563          * 
1564          * @param modelCoordinate the position along this axis defined 
1565          *  in the model units associated with this axis.
1566          *
1567          * @return a pixel-based coordinate that defines
1568          * the position associated with the argument in the standard
1569          * pixel, client window, coordinates of GWT.
1570          *
1571          * @see #getMouseCoordinate getMouseCoordinate
1572          * @see #clientToModel clientToModel
1573          * @see #pixelToModel pixelToModel
1574          * @see #modelToPixel modelToPixel
1575          * 
1576          *
1577          */
1578         public abstract double modelToClient(double modelCoordinate);
1579    
1580         /**
1581          * Converts a coordinate position in the model units associated
1582          * with this axis into a corresponding coordinate position
1583          * expressed in GChart's decorated chart pixel coordinates.
1584          * <p>
1585          *
1586          * These
1587          * coordinates have their origin at the upper left corner
1588          * of the decorated GChart, and x pixel-coordinates that increase
1589          * as you move right, and y pixel-coordinates that increase
1590          * as you move down. They are related to GWT's standard
1591          * client window coordinates via the following equations:
1592          *
1593          * <pre>
1594          *   xClient = plotPanel.getAbsoluteLeft()
1595          *             - Window.getScrollLeft()
1596          *             + xPixel;
1597          *   yClient = plotPanel.getAbsoluteTop()
1598          *             - Window.getScrollTop()
1599          *             + yPixel;
1600          * </pre>
1601          * <p>
1602          *
1603          * In the above <tt>plotPanel</tt> is an internal
1604          * <tt>AbsolutePanel</tt>
1605          * GChart creates to hold the entire, decorated, chart. Apart from
1606          * borders and such applied to the GChart as a whole, its
1607          * absolute top and left positions should be the same as
1608          * those of the GChart itself.
1609          * <p>
1610          * 
1611          * <i>Tip:</i> In applications that continuously track mouse moves
1612          * over the chart, and where absolute and scroll positions cannot
1613          * change, you can gain a significant performance boost by
1614          * computing the difference between pixel and client coordinates
1615          * once (<tt>modelToPixel(axisMin)-modelToClient(axisMin)</tt>)
1616          * and then adding that difference to the client coordinates
1617          * to get the pixel coordinates, and then using
1618          * <tt>pixelToModel</tt>, instead of using <tt>clientToModel</tt>
1619          * directly, which must repeatedly call GWT's scroll and absolute
1620          * position methods.
1621          * 
1622          * <p>
1623          * 
1624          *
1625          * For example, for a completely undecorated chart (no tick labels,
1626          * legend keys, etc.) the plot area takes up the entire chart. In
1627          * that case, if the pixel units of the plot area range from
1628          * <tt>0...100</tt> along this axis, and the model coordinates range
1629          * from <tt>0...10</tt> along this axis, then
1630          * <tt>modelToPixel(modelCoordinate)</tt> returns
1631          * <tt>10*modelCoordinate</tt>.  <p>
1632          *
1633          * The model/pixel mapping is as of the last <tt>update</tt>;
1634          * this method returns <tt>Double.NaN</tt> before the first
1635          * <tt>update</tt>. Note that, unlike <tt>clientToModel</tt>
1636          * and <tt>modelToClient</tt>, the GChart does <i>not</i>
1637          * need to be actually rendered within the browser for you to
1638          * use this method--a call to update is sufficient.
1639          * <p>
1640          * 
1641          * <i>Tip:</i> If you need to access this mapping before
1642          * the first real update, you can explicitly specify the min and
1643          * max of this axis via <tt>setAxisMin</tt> and
1644          * <tt>setAxisMax</tt>, and then call <tt>update</tt> before adding
1645          * any curves to the chart (which, since the chart is empty, should
1646          * be very fast). This approach will allow you to convert between
1647          * model and pixel coordinates before the first real update, and
1648          * before the chart is rendered in the browser. 
1649          * <p>
1650          *
1651          * 
1652          *
1653          * @param modelCoordinate a position on this axis expressed 
1654          *  in the model units associated with this axis.
1655          *
1656          * @return the distance,
1657          * in pixels, from the left edge (for the x axis) or top
1658          * edge (for the y or y2 axis) of
1659          * the decorated chart to the given position on this axis.
1660          *
1661          * @see #getMouseCoordinate getMouseCoordinate
1662          * @see #clientToModel clientToModel
1663          * @see #modelToClient modelToClient
1664          * @see #modelToPlotAreaPixel modelToClient
1665          * @see #pixelToModel pixelToModel
1666          * 
1667          *
1668          */
1669         public abstract double modelToPixel(double modelCoordinate);
1670    
1671    
1672         /**
1673          * Converts a coordinate position in the model units associated
1674          * with this axis into a corresponding coordinate position
1675          * expressed in GChart's plot area pixel coordinates.
1676          * <p>
1677          *
1678          * These
1679          * coordinates have their origin at the upper left corner
1680          * of the plot area, and x pixel-coordinates that increase
1681          * as you move right, and y pixel-coordinates that increase
1682          * as you move down.
1683          * <p>
1684          *
1685          * The plot area is the rectangular region bounded by the
1686          * chart's axes, and with a size specified via
1687          * <tt>setChartSize</tt>, where the chart's curves are
1688          * typically displayed. 
1689          * <p>
1690          * 
1691          * Apart from a shift in the origin of the pixel coordinates
1692          * used, this method works just like <tt>modelToPixel</tt>;
1693          * see that method for additional details, tips, and
1694          * restrictions.
1695          *
1696          * @param modelCoordinate a position on this axis expressed 
1697          *  in the model units associated with this axis.
1698          *
1699          * @return the distance,
1700          * in pixels, from the left edge (for the x axis) or top
1701          * edge (for the y or y2 axis) of
1702          * the plot area to the given position on this axis.
1703          *
1704          * @see #getMouseCoordinate getMouseCoordinate
1705          * @see #plotAreaPixelToModel plotAreaPixelToModel
1706          * @see #modelToPixel modelToPixel
1707          * @see #setChartSize setChartSize
1708          *
1709          */
1710         public abstract double modelToPlotAreaPixel(double modelCoordinate);
1711    
1712         
1713         /**
1714          * Converts a coordinate position in GChart's decorated
1715          * chart pixel
1716          * coordinates into the model units associated with this axis.
1717          * <p>
1718          * 
1719          * GChart's decorated chart pixel
1720          * coordinates have their origin at the upper left corner
1721          * of the decorated GChart, and x pixel-coordinates that increase
1722          * as you move right, and y pixel-coordinates that increase
1723          * as you move down. They are related to GWT's standard
1724          * client window coordinates via the following equations:
1725          *
1726          * <pre>
1727          *   xClient = plotPanel.getAbsoluteLeft()
1728          *             - Window.getScrollLeft()
1729          *             + xPixel;
1730          *   yClient = plotPanel.getAbsoluteTop()
1731          *             - Window.getScrollTop()
1732          *             + yPixel;
1733          * </pre>
1734          * <p>
1735          *
1736          * 
1737          * In the above <tt>plotPanel</tt> is an internal
1738          * <tt>AbsolutePanel</tt>
1739          * GChart creates to hold the entire, decorated, chart. Apart from
1740          * borders and such applied to the GChart as a whole, its
1741          * absolute top and left positions should be the same as
1742          * those of the GChart itself.
1743          * <p>
1744          *
1745          * For example, for a completely undecorated chart (no tick labels,
1746          * legend keys, etc.) the plot area takes up the entire chart. In
1747          * that case, if the pixel units of the plot area range from
1748          * <tt>0...100</tt> along this axis, and the model coordinates range
1749          * from <tt>0...10</tt> along this axis, then
1750          * <tt>pixelToModel(pixelCoordinate)</tt> returns
1751          * <tt>pixelCoordinate/10.</tt>.  <p>
1752          *
1753          * The model/pixel mapping is as of the last <tt>update</tt>;
1754          * this method returns <tt>Double.NaN</tt> before the first
1755          * <tt>update</tt>. Note that, unlike <tt>clientToModel</tt>
1756          * and <tt>modelToClient</tt>, the GChart does <i>not</i>
1757          * need to be actually rendered within the browser for you to
1758          * use this method.
1759          * <p>
1760          * 
1761          * <i>Tip:</i> If you need to access this mapping before
1762          * the first real update, you can explicitly specify the min and
1763          * max of this axis via <tt>setAxisMin</tt> and
1764          * <tt>setAxisMax</tt>, and then call <tt>update</tt> before adding
1765          * any curves to the chart (which, since the chart is empty, should
1766          * be very fast). This approach will allow you to convert between
1767          * model and pixel coordinates before the first real update, and
1768          * before the chart is rendered in the browser. 
1769          * <p>
1770          *
1771          * @param pixelCoordinate the distance,
1772          * in pixels, from the left edge (for the x axis) or top
1773          * edge (for the y or y2 axis) of
1774          * the decorated chart to a point on this axis.
1775          *
1776          * @return that same position on this axis expressed in the
1777          *  the model units associated with this axis.
1778          *  
1779          * @see #getMouseCoordinate getMouseCoordinate
1780          * @see #clientToModel clientToModel
1781          * @see #modelToClient modelToClient
1782          * @see #modelToPixel modelToPixel
1783          * @see #plotAreaPixelToModel plotAreaPixelToModel
1784          *
1785          */
1786         public abstract double pixelToModel(int pixelCoordinate);
1787    
1788    
1789         /**
1790          * Converts a coordinate position in GChart's plot area
1791          * pixel
1792          * coordinates into the model units associated with this axis.
1793          * <p>
1794          * 
1795          * GChart's plot area pixel
1796          * coordinates have their origin at the upper left corner
1797          * of the plot area, and x pixel-coordinates that increase
1798          * as you move right, and y pixel-coordinates that increase
1799          * as you move down.
1800          * <p>
1801          *
1802          * The plot area is the rectangular region bounded by the
1803          * chart's axes, and with a size specified via
1804          * <tt>setChartSize</tt>, where the chart's curves are
1805          * typically displayed. 
1806          * <p>
1807          * 
1808          * Apart from a shift in the origin of the pixel coordinates
1809          * used, this method works just like <tt>pixelToModel</tt>;
1810          * see that method for additional details, tips, and
1811          * restrictions.
1812          *
1813          * @param pixelCoordinate the distance,
1814          * in pixels, from the left edge (for the x axis) or top
1815          * edge (for the y or y2 axis) of
1816          * the plot area to a point on this axis.
1817          *
1818          * @return that same position on this axis expressed in the
1819          *  the model units associated with this axis.
1820          *  
1821          * @see #modelToPlotAreaPixel modelToPlotAreaPixel
1822          * @see #pixelToModel pixelToModel
1823          * @see #setChartSize setChartSize
1824          *
1825          */
1826         public abstract double plotAreaPixelToModel(int pixelCoordinate);
1827    
1828    
1829         
1830       
1831         /** Specifies the label of this axis.
1832          ** <p>
1833          **
1834          ** This label will be positioned just outside of, and
1835          ** centered lengthwise on, the region adjacent to
1836          ** this axis that GChart reserves for this axis' tick labels.
1837          **
1838          ** @param axisLabel a Widget to use as the label of this axis.
1839          **
1840          ** @see #getAxisLabel getAxisLabel
1841          ** @see #setTickLabelThickness setTickLabelThickness
1842          ** @see #setAxisLabelThickness setAxisLabelThickness
1843          ** 
1844          */
1845         
1846         public void setAxisLabel(Widget axisLabel) {
1847            this.axisLabel = axisLabel;
1848            chartDecorationsChanged = true;
1849         }
1850         
1851         /**
1852          * Convenience method equivalent to
1853          * <tt>setAxisLabel(new HTML(html))</tt>
1854          *
1855          * @param html HTML text used to define the axis label
1856          * 
1857          * @see #setAxisLabel(Widget) setAxisLabel(Widget)
1858          */
1859          public void setAxisLabel(String html) {
1860           setAxisLabel(new HTML(html));
1861          }
1862    
1863         /** Sets the thickness of the axis-label-holding region
1864          ** adjacent to the region allocated for tick labels.<p>
1865          ** 
1866          ** The axis label widget will be centered in this region.
1867          ** Choose a thickness large enough to hold the largest
1868          ** font size you want users to be able to zoom up to
1869          ** without the axis label spilling over into
1870          ** adjacent regions.
1871          ** <p>
1872          **
1873          ** If the axis label thickness is <tt>GChart.NAI</tt> (the
1874          ** default), and the widget defining the axis label
1875          ** implements <tt>HasHTML</tt> (or <tt>HasText</tt>) then
1876          ** GChart uses a thickness based on the estimated number of
1877          ** non-tag characters in the first <tt>&lt;br&gt;</tt> or
1878          ** <tt>&lt;li&gt;</tt>
1879          ** delimited line for y-axis labels, and based on the
1880          ** estimated number of (<tt>&lt;br&gt;</tt> or
1881          ** <tt>&lt;li&gt;</tt> delimited)
1882          ** text lines for x-axis labels.<p>
1883          ** 
1884          ** Note that if the axis label is <tt>null</tt> (its
1885          ** default setting) then no space is allocated for the axis
1886          ** label, regardless of this thickness setting.
1887          ** <p>
1888          **
1889          ** @param thickness the thickness of the axis-label-holding
1890          ** region, in pixels, or <tt>GChart.NAI</tt> to use
1891          ** GChart's character-based default thickness estimates.
1892          **
1893          ** @see #getAxisLabelThickness getAxisLabelThickness
1894          ** @see #setAxisLabel setAxisLabel
1895          */
1896         public void setAxisLabelThickness(int thickness) {
1897           axisLabelThickness = thickness;
1898           chartDecorationsChanged = true;
1899         }
1900    
1901         /**
1902          ** Specifies the maximum value visible on this axis.
1903          ** <p>
1904          ** 
1905          ** Aspects of the chart rendered beyond this maximum will
1906          ** be clipped if the chart's <tt>clipToPlotArea</tt>
1907          ** property is <tt>true</tt>.
1908          ** 
1909          ** <p>
1910          ** 
1911          ** If <tt>Double.NaN</tt> is specified, this maximum
1912          ** is auto-determined as described in <tt>getAxisMax</tt>.
1913          ** 
1914          ** <p> <i>Performance tip:</i> Using auto-determined axis
1915          ** limits (via <tt>Double.NaN</tt>) forces GChart, at the
1916          ** next update, to re-render many chart elements whenever
1917          ** the min or max data value displayed on this axis
1918          ** changes.  These (often expensive) re-renderings can be
1919          ** avoided by using explicitly specified axis limits
1920          ** whenever possible. <p>
1921          **
1922          ** @param max maximum value visible on this axis, in "model units"
1923          ** (arbitrary, application-specific, units) or <tt>Double.NaN</tt>
1924          ** (the default value) to use an auto-determined maximum.
1925          **
1926          ** @see #getAxisMax getAxisMax
1927          ** @see #getDataMin getDataMin
1928          ** @see #getDataMax getDataMax
1929          ** @see GChart#setClipToPlotArea setClipToPlotArea
1930          ** 
1931          **/ 
1932         public void setAxisMax(double max) {
1933            chartDecorationsChanged = true;
1934            this.axisMax = max;
1935         }
1936         /**
1937          ** Specifies the minimum value of this axis.
1938          ** <p>
1939          ** 
1940          ** Aspects of the chart rendered at positions before this
1941          ** minimum
1942          ** value will be clipped if the chart's
1943          ** <tt>clipToPlotArea</tt> property is <tt>true</tt>.
1944          ** <p>
1945          ** 
1946          ** If <tt>Double.NaN</tt> is specified, this minimum
1947          ** is auto-determined as described in <tt>getAxisMin</tt>.
1948          ** 
1949          ** <p> <i>Performance tip:</i> Using auto-determined axis
1950          ** limits (via <tt>Double.NaN</tt>) forces GChart, at the
1951          ** next update, to re-render many chart elements whenever
1952          ** the min or max data value displayed on this axis
1953          ** changes.  These (often expensive) re-renderings can be
1954          ** avoided by using explicitly specified axis limits
1955          ** whenever possible. <p>
1956          ** 
1957          ** @param min minimum value visible on this axis, in "model units"
1958          ** (arbitrary, application-specific, units), or Double.NaN
1959          ** (the default) to use an auto-determined minimum.
1960          **
1961          ** @see #getAxisMin getAxisMin
1962          ** @see #getDataMin getDataMin
1963          ** @see #getDataMax getDataMax
1964          ** 
1965          **/ 
1966         public void setAxisMin(double min) {
1967    // min can change axis label width ==> changes position of plot area
1968            chartDecorationsChanged = true;
1969            this.axisMin = min;
1970         }
1971    
1972      /**
1973       ** Defines if this axis is visible. Note that
1974       ** this property only defines the visibility of the axis line
1975       ** itself, it does not control the visibility of
1976       ** tick marks or tick labels associated with the axis.
1977       ** 
1978       ** <p>
1979       ** <i>Tip:</i>Tick marks can be made invisible by using
1980       ** <tt>setTickThickness</tt> to set the tick thickness
1981       ** to 0. Tick labels can be made invisible by using
1982       ** <tt>setTickLabelFontColor</tt> to set the tick label
1983       ** color to the chart's background color.
1984       ** <p>
1985       ** 
1986       ** @param axisVisible false to hide axis, true to show it.
1987       **
1988       ** @see #setTickThickness setTickThickness
1989       ** @see #setTickLabelFontColor setTickLabelFontColor
1990       ** @see #getAxisVisible getAxisVisible
1991       **/ 
1992       public void setAxisVisible(boolean axisVisible) {
1993          chartDecorationsChanged = true;
1994          this.axisVisible = axisVisible;
1995       }   
1996         
1997         /**
1998          ** Specifies if this axis should have gridlines. When an
1999          ** axis has gridlines, tick marks with indexes <tt>0, N,
2000          ** 2*N,...</tt> where <tt>N</tt> is the value of this axis'
2001          ** <tt>ticksPerGridline</tt> property, are in effect
2002          ** extended across the entire chart.
2003          **
2004          ** @param hasGridlines true to display gridlines,
2005          ** false (the default) to not display them.
2006          **
2007          ** @see #getHasGridlines getHasGridlines
2008          ** @see #setTicksPerGridline setTicksPerGridline
2009          ** 
2010          **/ 
2011         public void setHasGridlines(boolean hasGridlines) {
2012            chartDecorationsChanged = true;
2013            this.hasGridlines = hasGridlines;
2014         }
2015         /** Sets the number of ticks to be placed on this axis. The
2016          ** default tick count is 10. Ticks are always evenly
2017          ** spaced across the entire axis, unless explicitly
2018          ** specified via <tt>addTick</tt>.
2019          ** <p>
2020          ** 
2021          ** Note that setting the tick count overrides (erases)
2022          ** any ticks explicitly specified via <tt>addTick</tt>.
2023          ** 
2024          ** @param tickCount the number of ticks for this axis. 
2025          ** 
2026          ** @see #getTickCount getTickCount
2027          ** @see #addTick(double) addTick(double)
2028          ** @see #addTick(double,String) addTick(double, String)
2029          ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2030          ** @see #addTick(double,Widget) addTick(double,Widget)
2031          ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2032          ** @see #setTickLabelFormat setTickLabelFormat
2033          ** @see #setTickLabelFontSize setTickLabelFontSize
2034          ** @see #setTickLabelFontStyle setTickLabelFontStyle
2035          ** @see #setTickLabelFontColor setTickLabelFontColor
2036          ** @see #setTickLabelFontWeight setTickLabelFontWeight
2037          **
2038          **/
2039         public void setTickCount(int tickCount) {
2040            chartDecorationsChanged = true;
2041            getSystemCurve(ticksId).clearPoints(); // eliminate user specified ticks
2042            this.tickCount = tickCount;
2043         }
2044         /**
2045          ** Specifies the weight of the font used in this axis' tick
2046          ** labels.
2047          ** 
2048          ** @param cssWeight the weight of the font, such as bold,
2049          **    normal, light, 100, 200, ... 900, for tick labels.
2050          **
2051          ** @see #getTickLabelFontWeight getTickLabelFontWeight  
2052          ** @see #setTickLabelFormat setTickLabelFormat
2053          ** @see #setTickCount setTickCount
2054          ** @see #addTick(double) addTick(double)
2055          ** @see #addTick(double,String) addTick(double,String)
2056          ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2057          ** @see #addTick(double,Widget) addTick(double,Widget)
2058          ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2059          ** @see #setTickLabelFontStyle setTickLabelFontStyle
2060          ** @see #setTickLabelFontColor setTickLabelFontColor
2061          ** @see #setTickLabelFontSize setTickLabelFontSize
2062          ** @see #DEFAULT_TICK_LABEL_FONT_WEIGHT DEFAULT_TICK_LABEL_FONT_WEIGHT
2063          **/ 
2064         public void setTickLabelFontWeight(String cssWeight) {
2065            chartDecorationsChanged = true;
2066            // assure that any existing ticks are updated with new weight
2067            Curve c = getSystemCurve(ticksId);
2068            int nPoints = c.getNPoints(); 
2069            for (int i = 0; i < nPoints; i++)
2070               c.getPoint(i).setAnnotationFontWeight(cssWeight);
2071            tickLabelFontWeight = cssWeight;
2072         }
2073         /**
2074          ** Specifies the color of the font used to render tick labels
2075          ** for this axis.
2076          ** 
2077          ** <p>
2078          ** For more information on standard CSS color
2079          ** specifications see the discussion in
2080          ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
2081          ** <p>
2082          **        
2083          ** @param cssColor color of the font used to display this
2084          **    axis' tick labels, in standard CSS format.
2085          **
2086          ** @see #getTickLabelFontColor getTickLabelFontColor  
2087          ** @see #setTickLabelFormat setTickLabelFormat
2088          ** @see #setTickCount setTickCount
2089          ** @see #addTick(double) addTick(double)
2090          ** @see #addTick(double,String) addTick(double,String)
2091          ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2092          ** @see #addTick(double,Widget) addTick(double,Widget)
2093          ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2094          ** @see #setTickLabelFontStyle setTickLabelFontStyle
2095          ** @see #setTickLabelFontWeight setTickLabelFontWeight
2096          ** @see #setTickLabelFontSize setTickLabelFontSize
2097          **/ 
2098         public void setTickLabelFontColor(String cssColor) {
2099            chartDecorationsChanged = true;
2100            Curve c = getSystemCurve(ticksId);
2101            int nPoints = c.getNPoints(); 
2102            for (int i = 0; i < nPoints; i++)
2103               c.getPoint(i).setAnnotationFontColor(cssColor);
2104            tickLabelFontColor = cssColor;
2105         }
2106    
2107         /**
2108          ** Specifies the CSS font-style of this
2109          ** axis' tick labels.
2110          **
2111          ** @param cssStyle any valid CSS font-style, namely,
2112          **   normal, italic, oblique, or inherit.
2113          **
2114          ** @see #getTickLabelFontStyle getTickLabelFontStyle  
2115          ** @see #setTickLabelFormat setTickLabelFormat
2116          ** @see #setTickCount setTickCount
2117          ** @see #addTick(double) addTick(double)
2118          ** @see #addTick(double,String) addTick(double,String)
2119          ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2120          ** @see #addTick(double,Widget) addTick(double,Widget)
2121          ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2122          ** @see #setTickLabelFontColor setTickLabelFontColor
2123          ** @see #setTickLabelFontWeight setTickLabelFontWeight
2124          ** @see #setTickLabelFontSize setTickLabelFontSize
2125          ** @see #DEFAULT_TICK_LABEL_FONT_STYLE
2126          ** DEFAULT_TICK_LABEL_FONT_STYLE
2127          **/ 
2128         public void setTickLabelFontStyle(String cssStyle) {
2129            chartDecorationsChanged = true;
2130            Curve c = getSystemCurve(ticksId);
2131            int nPoints = c.getNPoints(); 
2132            for (int i = 0; i < nPoints; i++)
2133               c.getPoint(i).setAnnotationFontStyle(cssStyle);
2134            tickLabelFontStyle = cssStyle;
2135         }
2136    
2137         /**
2138          ** Sets the CSS font size for tick labels on this
2139          ** axis, in pixels.
2140          **
2141          ** @param tickLabelFontSize the font size of tick labels
2142          **   displayed on this axis.
2143          **
2144          ** @see #getTickLabelFontSize getTickLabelFontSize
2145          ** @see #setTickLabelFormat setTickLabelFormat
2146          ** @see #setTickCount setTickCount
2147          ** @see #addTick(double) addTick(double)
2148          ** @see #addTick(double,String) addTick(double,String)
2149          ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2150          ** @see #addTick(double,Widget) addTick(double,Widget)
2151          ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2152          ** @see #setTickLabelFontStyle setTickLabelFontStyle
2153          ** @see #setTickLabelFontColor setTickLabelFontColor
2154          ** @see #setTickLabelFontWeight setTickLabelFontWeight
2155          ** @see GChart#DEFAULT_TICK_LABEL_FONTSIZE DEFAULT_TICK_LABEL_FONTSIZE
2156          ** 
2157          **/ 
2158    
2159         public void setTickLabelFontSize(int tickLabelFontSize) {
2160            chartDecorationsChanged = true;
2161            Curve c = getSystemCurve(ticksId);
2162            int nPoints = c.getNPoints(); 
2163            for (int i = 0; i < nPoints; i++)
2164               c.getPoint(i).setAnnotationFontSize(tickLabelFontSize);
2165            this.tickLabelFontSize = tickLabelFontSize;
2166         }
2167    
2168         /**
2169         * Specifies a format string to be used in
2170         * converting the numeric values associated with each
2171         * tick on this axis into tick labels.  This string must
2172         * follow the conventions of the number format patterns
2173         * used by the GWT <a
2174         * href="http://google-web-toolkit.googlecode.com/svn/javadoc/1.4/com/google/gwt/i18n/client/NumberFormat.html">
2175         * NumberFormat</a> class, <i>with three
2176         * exceptions:</i>
2177         * <p>
2178         * <ol>
2179         * 
2180         *  <li><i>Log10 inverse prefix</i>: If the string begins
2181         *  with the prefix <tt>=10^</tt> the value is replaced with
2182         *  <tt>pow(10.,value)</tt> and the so-transformed value is
2183         *  then formatted using the part of the format string that
2184         *  comes after this prefix, which must be a valid GWT 
2185         *  <tt>NumberFormat</tt> pattern (e.g. "##.##").
2186         *  <p>
2187         *  For an example of how to use this prefix to create a
2188         *  semi-log plot, see <a
2189         *  href="package-summary.html#GChartExample04">the
2190         *  Chart Gallery's GChartExample04</a>.
2191         *  <p>
2192         *
2193         *  <li><i>Log2 inverse prefix</i>: If the string begins with
2194         *  the prefix <tt>=2^</tt> the value is replaced with
2195         *  <tt>pow(2.,value)</tt> and the so-transformed value is
2196         *  then formatted using the part of the format string that
2197         *  comes after this prefix, which must be a valid GWT
2198         *  <tt>NumberFormat</tt> pattern.
2199         *  <p>
2200         *  
2201         *  <li><i>Date casting prefix</i>: If the string begins with
2202         *  the prefix <tt>=(Date)</tt> the value is replaced with
2203         *  <tt>new Date((long) value)</tt> and the so-transformed
2204         *  value is then formatted using the format string that
2205         *  comes after this prefix, which must be a valid GWT
2206         *  <a href="http://google-web-toolkit.googlecode.com/svn/javadoc/1.4/com/google/gwt/i18n/client/DateTimeFormat.html">
2207         *  DateTimeFormat</a>  pattern (e.g. "yyyy-MM-dd&nbsp;HH:mm").
2208         *  For the special case format string of <tt>"=(Date)"</tt>
2209         *  (just the date casting prefix) GChart uses the 
2210         *  <tt>DateTimeFormat</tt> returned by the
2211         *  <tt>DateTimeFormat.getShortDateTimeFormat</tt> method.  <p>
2212         *  
2213         *  Note that the values associated with this Axis must
2214         *  represent the number of milliseconds since January 1,
2215         *  1970 (in the GMT time zone) whenever this date
2216         *  casting prefix is used.  <p>
2217         *
2218         *  
2219         *  For example, if the x-axis tick label format were
2220         *  "=(Date)MMM-dd-yyyy HH", then, for a tick located at the
2221         *  x position of 0, the tick label would be "Jan-01-1970 00"
2222         *  (on a client in the GMT time zone) and for a tick located
2223         *  at the x position of 25*60*60*1000 (one day + one hour,
2224         *  in milliseconds) the tick label would be "Jan-02-1970 01"
2225         *  (again, on a GMT-based client). Results would be
2226         *  shifted appropriately on clients in different time zones.
2227         *  <p>
2228         *
2229         *  Note that if your chart is based on absolute, GMT-based,
2230         *  millisecond times then date labels will change when your
2231         *  chart is displayed on clients in different time zones.
2232         *  Sometimes, this is what you want. To keep the date labels
2233         *  the same in all time zones, convert date labels into Java
2234         *  <tt>Date</tt> objects in your client-side code, then use
2235         *  the <tt>Date.getTime</tt> method, also in your
2236         *  client-side code, to convert those dates into the
2237         *  millisecond values GChart requires.  The <a
2238         *  href="package-summary.html#GChartExample12"> Chart
2239         *  Gallery's GChartExample12</a> illustrates how to use this
2240         *  second approach to produce a time series chart whose
2241         *  date-time labels are the same in all time zones.
2242         *  
2243         *  <p>
2244         *  
2245         *  <blockquote><small>
2246         *  
2247         *  Ben Martin describes an alternative (and more flexible)
2248         *  approach to formatting time series tick labels in his <a
2249         *  href="http://www.linux.com/feature/132854">GChart
2250         *  tutorial</a>. Ben's article, along with Malcolm Gorman's
2251         *  related <a
2252         *  href="http://groups.google.com/group/Google-Web-Toolkit/msg/6125ce39fd2339ac">
2253         *  GWT forum post</a> were the origin of this date
2254         *  casting prefix. Thanks! </small></blockquote>
2255         *    
2256         * </ol>
2257         * <p>
2258         * 
2259         * 
2260         * <p> Though HTML text is not supported in the tick label
2261         * format string, you can change the size, weight, style, and
2262         * color of tick label text via the
2263         * <tt>setTickLabelFont*</tt> family of methods. You
2264         * <i>can</i> use HTML in tick labels (e.g. for a multi-line
2265         * x-axis label) but but only if you define each tick label
2266         * explicitly using the <tt>addTick</tt> method.
2267         * 
2268         * @param format an appropriately prefixed
2269         *   GWT <tt>NumberFormat</tt> compatible or
2270         *   GWT <tt>DateTimeFormat</tt> compatible format string that
2271         *   defines how to convert tick values into tick labels.
2272         *
2273         * @see #setTickCount setTickCount
2274         * @see #addTick(double) addTick(double)
2275         * @see #addTick(double,String) addTick(double,String)
2276         * @see #addTick(double,String,int,int) addTick(double,String,int,int)
2277         * @see #addTick(double,Widget) addTick(double,Widget)
2278         * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2279         * @see #setTickLabelFontSize setTickLabelFontSize
2280         * @see #setTickLabelFontStyle setTickLabelFontStyle
2281         * @see #setTickLabelFontColor setTickLabelFontColor
2282         * @see #setTickLabelFontWeight setTickLabelFontWeight
2283         * @see #getTickLabelFormat getTickLabelFormat
2284         */
2285         public void setTickLabelFormat(String format) {
2286           // interpret prefixes and create an appropriate formatter
2287           if (!tickLabelFormat.equals(format)) {
2288             chartDecorationsChanged = true;
2289             if (format.startsWith("=(Date)")) {
2290               String transFormat = format.substring("=(Date)".length());
2291               if (transFormat.equals("")) // so "=(Date)" works
2292                 dateFormat = DateTimeFormat.getShortDateTimeFormat();
2293               else // e.g. "=(Date)mm/dd/yy hh:mm"
2294                 dateFormat = DateTimeFormat.getFormat(transFormat);
2295               tickLabelFormatType = DATE_FORMAT_TYPE;
2296             }
2297             else if (format.startsWith("=10^")) {
2298               String transFormat = format.substring("=10^".length());
2299               numberFormat = NumberFormat.getFormat(transFormat);
2300               tickLabelFormatType = LOG10INVERSE_FORMAT_TYPE;
2301             }
2302             else if (format.startsWith("=2^")) {
2303               String transFormat = format.substring("=2^".length());
2304               numberFormat = NumberFormat.getFormat(transFormat);
2305               tickLabelFormatType = LOG2INVERSE_FORMAT_TYPE;
2306             }
2307             else {
2308               numberFormat = NumberFormat.getFormat(format);
2309               tickLabelFormatType = NUMBER_FORMAT_TYPE;
2310             }
2311           }
2312           // remember original format (for use with the getter)
2313           tickLabelFormat = format;
2314         }
2315    
2316         /** Specifies the number of pixels of padding (blank space)
2317          ** between the tick marks and their labels. <p>
2318          ** 
2319          ** With the default of <tt>0</tt>, each
2320          ** tick label is flush against its tick mark.
2321          **
2322          ** @param tickLabelPadding the amount of padding between
2323          ** tick labels and tick marks, in pixels.
2324          ** 
2325          ** 
2326          ** @see #getTickLabelPadding getTickLabelPadding
2327          ** @see #setTickLength setTickLength
2328          ** @see #setTickLocation setTickLocation
2329          ** 
2330          **/
2331         public void setTickLabelPadding(int tickLabelPadding) {
2332           chartDecorationsChanged = true;
2333           this.tickLabelPadding = tickLabelPadding;   
2334         }
2335         /** Specifies the thickness of the region adjacent to
2336          ** this axis that GChart will reserve for purposes of
2337          ** holding this axis' tick labels.  <p>
2338          ** <p>
2339          **
2340          ** For vertical axes, this represents the width of the
2341          ** widest tick label, for horizontal axes, this represents
2342          ** the height of highest tick label.
2343          ** <p>
2344          **
2345          ** 
2346          ** By default, this property has the special "undefined"
2347          ** value <tt>GChart.NAI</tt>. With this value, the
2348          ** companion method <tt>getTickLabelThickness</tt> uses an
2349          ** HTML-based heuristic to estimate the thickness.
2350          **
2351          ** 
2352          ** @see #getTickLabelThickness getTickLabelThickness
2353          ** @see #setTickLabelFontSize setTickLabelFontSize
2354          ** @see #setTickLocation setTickLocation
2355          ** @see #setTickLabelPadding setTickLabelPadding
2356          ** @see #setAxisLabel setAxisLabel
2357          ** @see GChart#NAI NAI
2358          ** 
2359          **/
2360         public void setTickLabelThickness(int tickLabelThickness) {
2361           chartDecorationsChanged = true;
2362           this.tickLabelThickness = tickLabelThickness;
2363         }
2364         /** Specifies the ratio of the number of tick marks on the
2365          ** axis, to the number of gridlines on the axis. 
2366          ** <p>
2367          ** 
2368          ** For example, with the default value of 1, every tick has
2369          ** an associated gridline, whereas with a
2370          ** <tt>ticksPerGridline</tt> setting of 2, only the first,
2371          ** third, fifth, etc. ticks have gridlines.  
2372          ** 
2373          ** <p>
2374          ** 
2375          ** This setting only has an impact when the axis' gridlines
2376          ** are turned on, that is, when this axis'
2377          ** <tt>getHasGridlines</tt> method returns true.
2378          **      
2379          ** @see #setHasGridlines setHasGridlines
2380          ** @see #setTickCount setTickCount
2381          ** @see #addTick(double) addTick(double)
2382          ** @see #addTick(double,String) addTick(double,String)
2383          ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2384          ** @see #addTick(double,Widget) addTick(double,Widget)
2385          ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2386          **
2387          ** @param ticksPerGridline the number of ticks on this
2388          **   axis per "gridline-extended" tick.
2389          ** 
2390          **/ 
2391         public void setTicksPerGridline(int ticksPerGridline) {
2392           if (ticksPerGridline <= 0)
2393             throw new IllegalArgumentException("ticksPerGridline=" +
2394               ticksPerGridline + "; ticksPerGridline must be > 0");
2395           chartDecorationsChanged = true; 
2396           this.ticksPerGridline = ticksPerGridline;
2397         }
2398         /** Specifies the ratio of the number of tick marks on the
2399          ** axis, to the number of labeled tick marks on the axis.
2400          ** <p>
2401          ** 
2402          ** For example, with the default value of 1, every tick is
2403          ** labeled, whereas with a <tt>ticksPerLabel</tt> setting
2404          ** of 2, only the first, third, fifth, etc. ticks are
2405          ** labeled.
2406          ** 
2407          ** <p>
2408          ** 
2409          ** This setting is only used when tick labels
2410          ** are specified implicitly via <tt>setTickCount</tt>. It
2411          ** is ignored when tick positions and their labels are
2412          ** explicitly specified via <tt>addTick</tt>.
2413          **
2414          ** @see #setTickCount setTickCount
2415          ** @see #addTick(double) addTick(double)
2416          ** @see #addTick(double,String) addTick(double,String)
2417          ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2418          ** @see #addTick(double,Widget) addTick(double,Widget)
2419          ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2420          **
2421          ** @param ticksPerLabel the ratio of the number of ticks,
2422          **  to the number of labeled ticks.
2423          **
2424          **/ 
2425         public void setTicksPerLabel(int ticksPerLabel) {
2426           chartDecorationsChanged = true;
2427           if (ticksPerLabel <= 0)
2428               throw new IllegalArgumentException("ticksPerLabel=" +
2429               ticksPerLabel + "; ticksPerLabel must be > 0");
2430            this.ticksPerLabel = ticksPerLabel;
2431         }
2432    
2433         /**
2434         * Sets this axis' tick length. Set the tick length to zero to
2435         * eliminate the tick entirely.
2436         * <p>
2437         * 
2438         *
2439         * @param tickLength the length of the tick.
2440         *
2441         * @see #getTickLength getTickLength
2442         * @see #setTickThickness setTickThickness
2443         * @see #setTickLabelPadding setTickLabelPadding
2444         * @see #setTickLocation setTickLocation
2445         * 
2446         */
2447        abstract public void setTickLength(int tickLength);
2448    
2449    
2450         /**
2451          * Specifies the location of the tick marks relative to this
2452          * axis, namely, if tick marks are outside, inside, or
2453          * centered on this axis.
2454          * <p>
2455          *
2456          * @see #getTickLocation getTickLocation
2457          * @see #setTickThickness setTickThickness
2458          * @see #setTickLength setTickLength
2459          * @see #setTickLabelPadding setTickLabelPadding
2460          * 
2461          * @param tickLocation Specify either
2462          * <tt>TickLocation.INSIDE</tt>,
2463          * <tt>TickLocation.OUTSIDE</tt>, or
2464          * <tt>TickLocation.CENTERED</tt>
2465          *
2466          */
2467         public void setTickLocation(TickLocation tickLocation) {
2468            this.tickLocation = tickLocation;
2469            chartDecorationsChanged = true;
2470            GChart.Symbol sym = getSystemCurve(ticksId).getSymbol();
2471            if (isHorizontalAxis) {
2472              sym.setSymbolType(
2473                 tickLocation.getXAxisSymbolType(axisPosition));
2474              sym.setHeight(getActualTickLength());
2475            }
2476            else {
2477              sym.setSymbolType(
2478                 tickLocation.getYAxisSymbolType(axisPosition));
2479              sym.setWidth(getActualTickLength());
2480            }
2481         }
2482    
2483    
2484         /**
2485         * Sets this axis' tick thickness.
2486         * <p>
2487         *
2488         * @param tickThickness the thickness of the tick.
2489         *
2490         * @see #getTickThickness getTickThickness
2491         * @see #setTickLength setTickLength
2492         * @see #setTickLabelPadding setTickLabelPadding
2493         * @see #setTickLocation setTickLocation
2494         * 
2495         */
2496        abstract public void setTickThickness(int tickThickness);
2497        void maybePopulateTicks() {
2498           if (tickCount != GChart.NAI) populateTicks();
2499        }
2500    
2501        // fills in the tick positions when auto-generated.
2502        private void populateTicks() {
2503           getSystemCurve(ticksId).clearPoints();
2504    //TODO: It should be possible to control the visibility of each axis,
2505    // including ticks and tick labels, independent of the specifications of
2506    // the tick marks on that axis, and independent of if any curves are
2507    // mapped to that axis or not.  A setVisible(Boolean isVisible) as a
2508    // three-way, with null being the current, dependent, defaults, and
2509    // TRUE, FALSE explicitly making the entire axis, including tick marks
2510    // and labels visible or not without having to zero the tick count, add
2511    // dummy curve to the axis, etc. to control axis visibility is needed.
2512           if (XTICKS_ID == ticksId || // x, y ticks are drawn even
2513               YTICKS_ID == ticksId || // if no curves are on these axes
2514               0 < getNCurvesVisibleOnAxis()) {
2515              AxisLimits l = getAxisLimits();
2516              for (int i = 0; i < tickCount; i++) {
2517                 // linear interpolation between min and max
2518                 double position =(tickCount == 1) ? l.max : 
2519                                  (l.min * ((tickCount-1)-i) + i * l.max)/(tickCount-1.0);
2520                 addTickAsPoint(position,
2521                                (0 == i % ticksPerLabel) ?
2522                                formatAsTickLabel(position) : null, null,
2523                                GChart.NAI, GChart.NAI);
2524              }
2525           }
2526        }
2527    
2528    
2529        // fills in the gridlines; ticks are assumed already populated
2530        void populateGridlines() {
2531           Curve cTicks = getSystemCurve(ticksId);  
2532           Curve cGridlines = getSystemCurve(gridlinesId);
2533           cGridlines.clearPoints();
2534           int nTicks = cTicks.getNPoints();
2535           for (int iTick = 0; iTick < nTicks; iTick++) {
2536              if (hasGridlines && (iTick % ticksPerGridline) == 0) {
2537                 Curve.Point p = cTicks.getPoint(iTick);
2538                 cGridlines.addPoint(p.getX(), p.getY());
2539              }
2540           }
2541        }
2542    
2543        protected void getAxisLimits(AxisLimits result) {
2544           // so we get 1-unit changes between adjacent ticks
2545           final int DEFAULT_AXIS_RANGE = DEFAULT_TICK_COUNT-1;
2546           double min = getAxisMin();
2547           double max = getAxisMax();
2548           // Adjust min/max so that special cases, like one-point
2549           // charts, do not have axes that shrink down to a point,
2550           // which would create numerical and visual difficulties.
2551           if ((min!=min) && (max!=max)) { // x!=x is a faster isNaN
2552           // e.g. no data and no explicitly specified ticks
2553              min = 0;                                   
2554              max = min + DEFAULT_AXIS_RANGE;
2555           }
2556           else if ((min!=min) && !(max!=max)) { // x!=x is a faster isNaN
2557             // e.g. no data but only max explicitly set
2558              min = max - DEFAULT_AXIS_RANGE;
2559           }
2560           else if (!(min!=min) && (max!=max)) { // x!=x is a faster isNaN
2561             // e.g. no data but only min explicitly set
2562              max = min + DEFAULT_AXIS_RANGE;
2563           }
2564           else if (min == max) {
2565             // e.g one data point only, or they set min=max
2566              max = min + DEFAULT_AXIS_RANGE;
2567           }
2568           result.min = min;
2569           result.max = max;
2570        }
2571        AxisLimits getAxisLimits() {
2572           getAxisLimits(currentLimits);
2573           return currentLimits;
2574        }
2575           
2576        void rememberLimits() {
2577           getAxisLimits(previousLimits);
2578        }
2579        boolean limitsChanged() {
2580           boolean result = !getAxisLimits().equals(previousLimits);
2581           return result;
2582        }
2583        
2584    
2585        /* similar to getTickText, except for the tick position */
2586         private double getTickPosition(Curve c, int iTick) {
2587            double result;
2588            if (isHorizontalAxis)
2589               result = c.getPoint(iTick).getX();
2590            else
2591               result = c.getPoint(iTick).getY();
2592            return result;
2593         }
2594    
2595        // returns the largest, explicitly specified, tick position
2596         private double getTickMax() {
2597            double result = -Double.MAX_VALUE;
2598            Curve c = getSystemCurve(ticksId);
2599            int nTicks = c.getNPoints();
2600            for (int i = 0; i < nTicks; i++)
2601               result = Math.max(result, getTickPosition(c, i));
2602            return result;
2603         }
2604    
2605        // returns the smallest, explicitly specified, tick position
2606         private double getTickMin() {
2607            double result = Double.MAX_VALUE;
2608            Curve c = getSystemCurve(ticksId);
2609            int nTicks = c.getNPoints();
2610            for (int i = 0; i < nTicks; i++)
2611               result = Math.min(result, getTickPosition(c, i));
2612            return result;
2613         }
2614         
2615    
2616        // Same as max, except treats NaN/MAX_VALUE values as "not there"
2617        protected double maxIgnoreNaNAndMaxValue(double x1, double x2) {
2618           double result;
2619           if ((x1!=x1) ||
2620               Double.MAX_VALUE == x1 ||
2621               -Double.MAX_VALUE == x1) // x!=x is a faster isNaN
2622              result = x2;
2623           else if ((x2!=x2) ||
2624                    Double.MAX_VALUE == x2 ||
2625                    -Double.MAX_VALUE == x2)
2626              result = x1;
2627           else
2628              result = Math.max(x1, x2);
2629           return result;
2630        }
2631        // Same as Math.min, except treats NaN/MAX_VALUE values as "not there"
2632        protected double minIgnoreNaNAndMaxValue(double x1, double x2) {
2633           double result;
2634           if ((x1!=x1) ||
2635               Double.MAX_VALUE == x1 ||
2636               -Double.MAX_VALUE == x1 ) // x!=x is a faster isNaN
2637              result = x2;
2638           else if ((x2!=x2) ||
2639                    Double.MAX_VALUE == x2 ||
2640                    -Double.MAX_VALUE == x2)
2641              result = x1;
2642           else
2643              result = Math.min(x1, x2);
2644           return result;
2645        }
2646         // does a dummy set of any dynamically determined axis
2647         // limit, so, for update purposes, they are considered
2648         // to have changed.
2649         void invalidateDynamicAxisLimits() {
2650            // x!=x is a faster isNaN
2651            if ((axisMin!=axisMin)) setAxisMin(axisMin); 
2652             if ((axisMax!=axisMax)) setAxisMax(axisMax);
2653         }
2654    
2655    
2656            
2657      } // end of class Axis       
2658    
2659      // creates canvas Widgets GChart needs for *_CANVAS symbol types.
2660      private static GChartCanvasFactory canvasFactory = null;
2661      /**
2662       *
2663       * Tells GChart how to create the canvas widgets it needs
2664       * (specifically, widgets that implement GChart's
2665       * <tt>GChartCanvasLite</tt> interface) to render your
2666       * charts using an external vector graphics library.  <p>
2667       *
2668       * You must define a class that implements
2669       * <tt>GChartCanvasFactory</tt> and then pass an instance of that
2670       * class to this method, if you want to have the fast, crisply drawn
2671       * connecting lines, polygonal areas, and 2-D pie slices that only a
2672       * vector graphics library can provide.
2673       * <p>
2674       *
2675       * <small>
2676       * <i>Note:</i> If all of your charts only have
2677       * rectangular elements (e.g. bar charts) GChart will continue
2678       * to render those charts using HTML elements, even if a
2679       * canvas factory is provided. Thus, there is no point
2680       * to defining a canvas factory if all you use GChart for
2681       * is bar charts, scatter plots without connecting lines
2682       * between each point, banded-fill pie slices, and so on.
2683       * <p>
2684       * 
2685       * On the other hand, if you need continously connected lines,
2686       * solid-fill pie slices, or solid-fill area charts (all of which
2687       * will also need a <tt>setFillSpacing(0)</tt> to specify continuous
2688       * filling) you will gain substantial performance/quality
2689       * improvements if you add a canvas factory.  <p>
2690       * </small>
2691       *
2692       * In detail, to exploit browser-based vector graphics (Mozilla's
2693       * canvas, IE's VML, etc.)  rendering in your charts, you must:
2694       *  
2695       * <ol>
2696       *  
2697       *   <li>Import an external GWT canvas library, such
2698       *   as provided by the
2699       *   <a href="http://code.google.com/p/google-web-toolkit-incubator/">
2700    GWT incubator project's</a> <tt>GWTCanvas</tt> class, into your project.
2701       *   For example, our GChart test application uses
2702       *   <tt>GWTCanvas</tt>
2703       *   and imports it by adding this line to its
2704       *   <tt>.gwt.xml</tt> file:
2705       * <p>  
2706       * <pre>
2707       *   &lt;inherits name='com.google.gwt.widgetideas.GWTCanvas' /&gt;
2708       * </pre>
2709       *
2710       * To make this work, we also had to add the
2711       * <a href="http://code.google.com/p/google-web-toolkit-incubator/">
2712       * gwt-incubator.jar</a> file (which contains
2713       * <tt>GWTCanvas</tt>) to our build path via Eclipse's
2714       * "Configure Build Path..." command and to the libraries
2715       * listed in the
2716       * <tt>classpath=...</tt> line of our ant build script's
2717       * <tt>java</tt> task.
2718       * 
2719       * <p>
2720       *
2721       * <small>For more on the <tt>GWTCanvas</tt> widget, see the <a
2722       * href="http://code.google.com/p/google-web-toolkit-incubator/wiki/GWTCanvas">
2723       * GWTCanvas Wiki page</a> within the GWT
2724       * incubator site.</small>
2725       * 
2726       * <p>
2727       * 
2728       *   <li>Implement a class that extends <tt>Widget</tt> and
2729       *   implements the <tt>GChartCanvasLite</tt> interface that GChart
2730       *   expects. Again, in GChart's <tt>GWTCanvas</tt>-based test
2731       *   application we use:
2732       * 
2733       * {@code.sample ..\..\..\..\..\..\gcharttestapp\src\com\googlecode\gchart\gcharttestapp\client\GWTCanvasBasedCanvasLite.java} 
2734       *   
2735       *   <li>Create a <tt>GChartCanvasFactory</tt> class that has a
2736       *   single <tt>create</tt> method that returns new
2737       *   instances of your <tt>GChartCanvasLite</tt> Widget.
2738       *   GChart's test application uses:
2739       *
2740       *
2741       * {@code.sample ..\..\..\..\..\..\gcharttestapp\src\com\googlecode\gchart\gcharttestapp\client\GWTCanvasBasedCanvasFactory.java} 
2742       *
2743       *   <li>Finally pass an instance of that factory to
2744       *   GChart via a single invocation of
2745       *   <tt>setCanvasFactory</tt>:
2746       *
2747       *   <pre>
2748       *  static { 
2749       *    GChart.setCanvasFactory(new GWTCanvasBasedCanvasFactory());
2750       *  } 
2751       *   </pre>
2752       *  <p>
2753       *  
2754       *  <small>Because the above line essentially completes the definition
2755       *  of the GChart class (and thus should only be executed once per
2756       *  application) a good home for it is in a static initializer, as
2757       *  shown above. I recommend placing that initializer within your
2758       *  application's <tt>EntryPoint</tt> class.</small>
2759       * 
2760       * </ol>
2761       * 
2762       *  <p>
2763       *
2764       *
2765       * <i>Note:</i> To make things a bit simpler for you, the following
2766       * code combines steps 2, 3, and 4 into a single chunk of easily pasted
2767       * boilerplate:
2768       * 
2769       * <p>
2770       * <small><small>
2771       * {@code.sample
2772       * ..\..\..\..\..\..\gchartdemoapp\src\com\googlecode\gchart\gchartdemoapp\client\GChartDemoApp.java#1} 
2773       * {@code.sample
2774       * ..\..\..\..\..\..\gchartdemoapp\src\com\googlecode\gchart\gchartdemoapp\client\GChartDemoApp.java#2} 
2775       * </small></small>
2776       * 
2777       * <p>
2778       *
2779       * To see the above boilerplate within a working example application,
2780       * follow the link at the bottom of GChart's
2781       * <a href="http://gchart.googlecode.com/svn/trunk/live-demo/v2_6/com.googlecode.gchart.gchartdemoapp.GChartDemoApp/GChartDemoApp.html">
2782       * live demo page</a> to examine its <tt>EntryPoint</tt> class' code.
2783       * 
2784       * <p>
2785       * 
2786       *  <i>GChart's mixed canvas and HTML rendering</i>
2787       *
2788       *  <small><blockquote>
2789       *
2790       *  Even with an external canvas factory enabled, many aspects
2791       *  of your chart will remain HTML-rendered. In general, GChart
2792       *  only renders non-rectangular, "continuously filled" aspects
2793       *  of your chart using canvas. Other aspects, such as the bars
2794       *  on a bar chart, or the individual rectangular point markers
2795       *  on a line chart, as well as all chart text, will remain HTML
2796       *  rendered.
2797       *  <p>
2798       *
2799       *  The rendering mode (HTML-only or HTML+canvas) for each curve is
2800       *  defined by if a canvas factory is available and if that curve uses
2801       *  the special <tt>setFillSpacing(0)</tt> setting (meaning:
2802       *  "continuously filled"). So, to force any curve to be only HTML
2803       *  rendered, simply set this fill spacing to a value greater than
2804       *  <tt>0</tt>.  <p>
2805       *
2806       *  HTML rendering offers some features not available with canvas
2807       *  rendering.  For example, only HTML-rendered curves can overwrite
2808       *  the enclosing page without monopolizing mouse events within that
2809       *  curve's bounding rectangle, and only HTML-rendered curves can
2810       *  define their "fill" via an image. HTML rendered curves can
2811       *  sometimes even be more memory-efficent, provided the number of
2812       *  pixels greatly exceeds the number of HTML elements. And the HTML
2813       *  only rendering option provides a useful least common
2814       *  denominator/fall-back, supported by even the most obscure
2815       *  browsers.<p>
2816       *
2817       *  Note that the internal curves that GChart uses to render tick
2818       *  marks, gridlines, etc.  are never canvas rendered, because they
2819       *  only involve vertical or horizontal rectangles, which GChart
2820       *  always renders with HTML. 
2821       *  
2822       *  </blockquote></small>
2823       *  
2824       * <p>
2825       * 
2826       * Finally, if you are content with GChart's built-in HTML-based
2827       * rendering, or if your charts only use rectangular elements (e.g.
2828       * bar charts) you don't need to bother with any of the steps listed
2829       * above. Your charts will then only depend on the standard GWT
2830       * distribution and the 3,000 or so lines of pure GWT Java that
2831       * implement GChart.  <p>
2832       *
2833       * <small> <i>Important</i>: GChart only uses your external
2834       * canvas facility to draw a chart's non-rectangular aspects.
2835       * Given how GChart works, a curve can only have
2836       * non-rectangular aspects if <tt>setFillSpacing</tt> has been
2837       * set to <tt>0</tt> (which implies "continuous filling"), and
2838       * <tt>setFillThickness</tt> has been set to a value <tt>&gt;
2839       * 0</tt>. If you are not seeing crisp, canvas-rendered area,
2840       * line, or pie charts, be sure to check these two settings on
2841       * the curves in question.  </small>
2842       * 
2843       * @see GChartCanvasFactory GChartCanvasFactory
2844       * @see GChartCanvasLite GChartCanvasLite
2845       * @see #getCanvasFactory getCanvasFactory
2846       * @see GChart.Symbol#setFillSpacing setFillSpacing
2847       * @see GChart.Symbol#setFillThickness setFillThickness
2848       * 
2849       */
2850      public static void setCanvasFactory(GChartCanvasFactory factory) {
2851         canvasFactory = factory;
2852      }
2853    
2854      /**
2855       * Returns the GChart class' canvas factory, or <tt>null</tt>
2856       * if no canvas factory has been specified.
2857       * 
2858       * @return the previously specified canvas factory
2859       *
2860       * @see #setCanvasFactory setCanvasFactory
2861       * 
2862       */
2863      public static GChartCanvasFactory getCanvasFactory() {
2864         return canvasFactory;
2865      }
2866      
2867      /**
2868       * Represents a curve on a chart, which includes
2869       * information such as the x,y coordinates of each point,
2870       * the symbol used to represent points on the curve, etc.
2871       * <p>
2872       * To create a new curve, use the <tt>GChart.addCurve</tt>
2873       * method.
2874       *
2875       * @see GChart#addCurve() addCurve()
2876       *
2877       */
2878      public class Curve {
2879         private boolean isVisible = true;
2880         private String legendHTML = null;
2881         private ArrayList<Point> points = new ArrayList<Point>();
2882         // symbol defines how every point on this curve is rendered
2883         private Symbol symbol = new Symbol(this);
2884    
2885         private YAxisId yAxisId = Y_AXIS;
2886    
2887         private boolean isValidated = false;
2888         boolean isValidated() { return isValidated; }
2889    /*
2890     * TestGChart14d revealed that curves.indexOf(curve) could, due to its
2891     * sequential search, create a performance bug if the chart had
2892     * over 100 curves (e.g. the 160 pie chart slices/curves of TestGChart14d)
2893     * <p>
2894     * 
2895     * With a little extra bookkeeping during add/remove curve to call these
2896     * methods (and the extra int) this problem was corrected.
2897     *
2898     *
2899     */ 
2900         private int indexOf = GChart.NAI; 
2901         void incrementIndex() {indexOf++;}
2902         void decrementIndex() {indexOf--;}
2903         void clearIndex() {indexOf = GChart.NAI;}
2904         int getIndexOf() {return indexOf;}
2905    //     private void assertCurveNotRemoved() {
2906    //       if (indexOf == GChart.NAI)
2907    //          throw new IllegalStateException(
2908    //            "Removed curves should not be modified. " + 
2909    //            "You removed a curve, but retained a reference " + 
2910    //            "to that curve, and then tried to modify one of " +
2911    //            "its properties after you removed it."); 
2912    //     }
2913         
2914         /*
2915          * No public constructor because curves are always
2916          * contained within, and managed by, their containing
2917          * GChart via its addCurve, removeCurve, and related
2918          * methods.
2919          *
2920          */
2921         Curve(int indexOf) {
2922            super();
2923            this.indexOf = indexOf;
2924         }
2925         /**
2926         * Adds a new point to the curve, at the end of the current
2927         * list of points, with the specified
2928         * coordinates in model-units (arbitrary, application-specific,
2929         * units).
2930         * <p>
2931         *
2932         * GChart gives a special interpretation to the following values:
2933         * <p>
2934         * 
2935         * <ol>
2936         * 
2937         * <li> If <tt>-Double.MAX_VALUE</tt> is specified for either x or y,
2938         * the point acts as if it were placed at the minimum visible
2939         * x or y position within the plot area.
2940         * <p>
2941         * 
2942         * <li> Similarly, if <tt>Double.MAX_VALUE</tt> is specified for
2943         * either x or y, the point acts as if it were placed at the
2944         * maximum visible x or y position within the plot area.  <p>
2945         * 
2946         * <p>
2947         * <li>If <tt>Double.NaN</tt> is specified for either x or y, the
2948         * point is created, but it will not be visible in the
2949         * charting region.
2950         * 
2951         * <p>
2952         * <i>Tip:</i> Connecting lines to/from such 
2953         * <tt>Double.NaN</tt> points are elided, so you can use such a
2954         * point to create a break in an otherwise connected curve.
2955         *
2956         * </ol>
2957         * 
2958         * 
2959         * @param x the x-coordinate of the new point
2960         * @param y the y-coordinate of the new point
2961         *
2962         * @see #getPoint getPoint
2963         * @see #addPoint(int,double,double) addPoint(int,double,double)
2964         * @see #removePoint removePoint
2965         * @see #clearPoints clearPoints
2966         * @see #getNPoints getNPoints
2967         */
2968         public void addPoint(double x, double y) {
2969           invalidate(); 
2970           points.add(new Point(x, y));
2971        }
2972    
2973         /**
2974         * Adds a new point at the specified position in the point
2975         * sequence, increasing the indexes of existing points at or after
2976         * the specified position by 1.
2977         *
2978         * @param iPoint the position that the new point will occupy
2979         * @param x the x-coordinate of the new point (model units)
2980         * @param y the y-coordinate of the new point (model units) 
2981         *
2982         * @see #getPoint getPoint
2983         * @see #addPoint(double, double) addPoint(double,double)
2984         * @see #removePoint removePoint
2985         * @see #clearPoints clearPoints
2986         * @see #getNPoints getNPoints
2987         */  
2988         public void addPoint(int iPoint, double x, double y) {
2989            invalidate(); 
2990            points.add(iPoint, new Point(x, y));
2991        }
2992    
2993       /**
2994       * Removes every point this curve contains.
2995       *
2996       * @see Point Point
2997       * @see #getPoint getPoint
2998       * @see #addPoint(double, double) addPoint(double,double)
2999       * @see #addPoint(int,double,double) addPoint(int,double,double)
3000       * @see #removePoint removePoint
3001       * @see #getNPoints getNPoints
3002       */
3003       public void clearPoints() {
3004          if (this == getTouchedCurve()) 
3005             plotPanel.touch(null);
3006          invalidate(); 
3007          points.clear();
3008       }
3009    
3010    
3011      /*
3012       * Locates index of vertical or horizontal hit-testing band
3013       * that the given point appears in. The first and last
3014       * "pseudo-bands" are devoted to holding all points that fall
3015       * either to the left of or to the right of (or above or below
3016       * for horizontal banding) the first or last "normal" band
3017       * covering the decorated chart's containing "box".
3018       * <p>
3019       *
3020       * Note that some points just slightly off the right or bottom edge
3021       * may not end up in a pseudo-band, due to the fact that the chart
3022       * width (or height) need not be an even multiple of the (fixed) band
3023       * thickness (the "last band sticking out a bit" effect).
3024       * 
3025       */
3026       private int getBand(int iPoint, double bandThickness) {
3027          int result = GChart.NAI;
3028          SymbolType symType = getSymbol().getSymbolType();
3029          double xPx = symType.getCenterX(plotPanel, getSymbol(), iPoint);
3030          if (xPx!=xPx) return result; // NaN points not in any band
3031          double yPx = symType.getCenterY(
3032                          plotPanel, getSymbol(), iPoint, onY2());
3033          if (yPx!=yPx) return result; // NaN points not in any band
3034    
3035          // now, we've got a point with x,y values in some sort of band
3036          
3037          if (getSymbol().isHorizontallyBanded()) {
3038             if (yPx < 0)    
3039                result = 0; // off-chart point above chart
3040             else if (yPx >= (bandList.length-EXTRA_BANDS)*bandThickness)
3041                result = bandList.length-1; // off-chart point below chart
3042             else
3043                // inside a normal, chart-covering, band
3044                result = 1 + (int) Math.floor(yPx/bandThickness);
3045          }
3046          else { // vertically banded
3047             if (xPx < 0)
3048                result = 0;  // off-chart point to the left
3049             else if (xPx >= (bandList.length-EXTRA_BANDS)*bandThickness)
3050                result = bandList.length-1; // off-chart point to the right
3051             else
3052                // within one of the real bands covering the chart
3053                result = 1 + (int) Math.floor(xPx/bandThickness);
3054          }
3055          return result;
3056       }
3057       
3058       /*
3059        * Number of hit-test bands for this curve, for a given band
3060        * thickness. 
3061        *
3062        */ 
3063       private int EXTRA_BANDS = 2;   // far left, right (top, bottom) bands
3064       private int getNBands(double bandThickness) {
3065          int result = EXTRA_BANDS;
3066          if (getSymbol().isHorizontallyBanded()) 
3067            result += (int) Math.ceil(getYChartSize()/bandThickness);
3068          else 
3069            result += (int) Math.ceil(getXChartSize()/bandThickness);
3070          return result;
3071       }
3072    
3073       /*
3074        * Separates points on this curve into bins associated with
3075        * successive vertical (or horizontal) bands across the entire
3076        * decorated chart.
3077        * <p>
3078        * 
3079        * Because busy charts typically distribute points evenly
3080        * across the chart, by jumping to the appropriate band's
3081        * list, we can (usually) greatly accelerate worst case mouse
3082        * hit testing. And because the bin organizing step only
3083        * requires a single pass over all the points (and less than a
3084        * two int memory overhead per point) it should almost always
3085        * be a "good deal" performance-wise (compared to a simple
3086        * full point-list scan with every hit test).<p>
3087        *
3088        * Points are placed into bins based on the (pixel) position
3089        * of the x (or, with horizontal bands, y) at the center of
3090        * the rendered symbol.  We choose bin size to guarantee that
3091        * bins are at least as wide (or high, for horizontally banded
3092        * hit testing) as the rendered symbols on this curve. This
3093        * simplifies hit testing, since bins are big enough to assure
3094        * that a single symbol straddles at most two adjacent bands.
3095        * Exploits fact that all symbols on the same curve have the
3096        * same size, and that curves with many points on them tend to
3097        * have smaller sized symbols.
3098        * 
3099        * <p>
3100        *
3101        * Note that, for bin placement purposes, pie slices are
3102        * considered to have a "center" equal to the center of the
3103        * pie that contains them, and to have a width and height
3104        * equal to the diameter of the pie containing the slice
3105        * (the "worst-case slice").
3106        * 
3107        * <p>
3108        *
3109        * Also note that a symbol whose center is in the right side
3110        * of a vertical band may overlap into the following band, and
3111        * one in the left side may overlap the preceding band. Thus
3112        * during hit testing, we must check not only the lists of
3113        * points in the bands "touched" by the mouse-cursor-centered
3114        * brush, but also 1) the band to the immediate left of the
3115        * leftmost touched band, whenever a left-side sub-band of that band
3116        * is touched by the brush and 2) the band to the immediate
3117        * right of the rightmost touched band, whenever a right-side
3118        * sub-band of that band is touched. The thickness of these left
3119        * and right side sub-bands equals half the symbol width.
3120        * Expanding the brush a half-symbol width on either edge
3121        * is the easiest way to apply these rules. Exactly
3122        * analogous statements apply to horizontal bands.  <p>
3123        *
3124        * A minimum band size is enforced to prevent the number of
3125        * bands from growing too large with small symbols. Each
3126        * symbol type defines if vertical or horizontal banding is
3127        * more appropriate, or if brush shape should determine
3128        * banding strategy (as of this writing, only horizontal bar
3129        * symbol types require horizontal hit-test bands).  Exploits the
3130        * fact that all symbols have a fixed size for at least one of
3131        * their dimensions (for example, vertical bars have variable
3132        * height but fixed width).<p>
3133        *
3134        * Note: this method must be called after the curve is
3135        * rendered during an update(), to assure that hit-test-bins
3136        * are consistent with rendered curves, and ready for use
3137        * before the first mouse hit testing is done.
3138        * <p> 
3139        *
3140        * After running this method, points on this curve within a given
3141        * band can be enumerated as in the following code:
3142        * <p>
3143        * 
3144        * <pre>
3145        *   Point p = null;
3146        *   for (int iPoint = bandList[iBand];
3147        *        iPoint != GChart.NAI;
3148        *        iPoint = p.getINextInBand()) {
3149        *      p = getPoint(iPoint);
3150        *      // do something requiring points in a given band...
3151        *   }
3152        *
3153        * </pre>
3154        *
3155        */
3156       private int[] bandList = null; // index of first point in each band
3157       private double bandThickness = Double.NaN;
3158       void clearBandList() {bandList = null;}
3159       void bandSeparatePoints() {
3160         bandThickness =
3161            getSymbol().getSymbolType().getBandThickness(plotPanel,
3162                                                         getSymbol(),onY2());
3163         int nBands = getNBands(bandThickness); 
3164             
3165         if (bandList == null || bandList.length != nBands)
3166            bandList = new int[nBands];
3167         // else bandList already has required length, reuse it.
3168    
3169         // all bands contain NAI terminated, empty lists to start with
3170         for (int i = 0; i < bandList.length; i++)
3171           bandList[i] = GChart.NAI;  
3172    
3173         for (int iPoint = 0; iPoint < getNPoints(); iPoint++) {
3174           int iBand = getBand(iPoint, bandThickness);
3175           Point p = getPoint(iPoint);
3176           if (GChart.NAI == iBand) {
3177              // point isn't rendered at all, so isn't in a band (a next
3178              // link pointing to self means "I'm not in any band"). To let
3179              // us skip over these points quickly during rendering.
3180              p.setINextInBand(iPoint);
3181           }
3182           else {
3183         // Add point to front of list for whatever band it's in
3184         // (note that point order therefore gets reversed).
3185             p.setINextInBand(bandList[iBand]);
3186             bandList[iBand] = iPoint;
3187           }
3188           
3189         }
3190       }
3191    /*
3192     * Returns the index of the point on this curve whose rendered
3193     * symbol intersects a rectangle with the specified width and
3194     * height centered on the specified point (this rectangle is
3195     * typically a point selection "brush", centered on the mouse
3196     * cursor).  <p>
3197     *
3198     * In the event that more than one point's rendered symbol
3199     * intersects with the specified rectangle, the point whose
3200     * center is closest to the specified rectangle's center is
3201     * returned. In the event of a tie, the point with the largest
3202     * point index is returned. If no point "touches" the rectangle,
3203     * <tt>GChart.NAI</tt> is returned.  <p>
3204     *
3205     * Assumes/requires up-to-date <tt>bandList</tt> array and
3206     * related <tt>iNextInBand</tt> indexes (these get defined within
3207     * the <tt>bandSeparatePoints</tt> method).
3208     * 
3209     */
3210    
3211       int getClosestTouchingPoint(int xBrush, int yBrush) {
3212    
3213         int result = GChart.NAI;
3214         // ANCHOR_MOUSE symbol type curves not band separated/hit tested
3215         if (null == bandList) return result; 
3216         SymbolType symType = getSymbol().getSymbolType();
3217         double dBest = Double.MAX_VALUE; // closest touching pt's distance^2
3218      
3219         int iBandFirst; 
3220         int iBandLast;
3221    
3222         int brushWidth = symType.getBrushWidth(getSymbol());
3223    /*
3224     * In every tested browser EXCEPT FF3, we don't need the +1 below to
3225     * select a 1px tall, off-chart, symbol with a 1x1 px brush
3226     * (specifically, to select the leftmost vertical bar on TestGChart28).
3227     * The +1  below in effect adds 1 px to the height of the brush to
3228     * workaround this problem. 
3229     * 
3230     */ 
3231         int brushHeight = symType.getBrushHeight(getSymbol()) + 1; 
3232         AnnotationLocation brushLocation = symType.getBrushLocation(
3233            getSymbol());
3234         int nBands = bandList.length;
3235    
3236         // Determine range of bands touched by brush, taking into
3237         // account potential for symbols whose centers are in one
3238         // band to "stick out" into an adjacent band by half-band
3239         // thickening of either end of the brush. 
3240         //
3241         // Note that the 0th and (nBand-1)th bands represent
3242         // "pseudo-bands" that hold all points that fall to the left
3243         // or right (or above or below if horizontally banded) the
3244         // rectangle occupied by the decorated chart.  The tacit
3245         // assumption is that such completely off-the-chart points
3246         // are rare, so it's OK to bunch them up into just 2 bands.
3247         if (getSymbol().isHorizontallyBanded()) {
3248            // horizontal bars and some curves with "wider than high" brushes
3249           double top = brushLocation.getUpperLeftY(yBrush, brushHeight, 0);
3250           double bottom = top + brushHeight;
3251           top -= bandThickness/2.;
3252           bottom += bandThickness/2.; 
3253           iBandFirst = (int) Math.max(0, Math.min(nBands-1,1+Math.floor(
3254              top / bandThickness))); 
3255           iBandLast = (int) Math.max(0, Math.min(nBands-1, 1+Math.floor(
3256              bottom / bandThickness))); 
3257         }
3258         else { // vertical bars, some curves with "tall or square" brushes 
3259           double left = brushLocation.getUpperLeftX(xBrush, brushWidth, 0); 
3260           double right = left + brushWidth;
3261           left -= bandThickness/2.0;
3262           right += bandThickness/2.0;       
3263           iBandFirst = (int) Math.max(0, Math.min(nBands-1, 1+Math.floor(
3264              left / bandThickness))); 
3265           iBandLast = (int) Math.max(0, Math.min(nBands-1, 1+Math.floor(
3266              right / bandThickness))); 
3267         }
3268    
3269         // Every point whose symbol touches the brush must be in one
3270         // of these bands. Search them to find closest touching point.
3271         for (int iBand = iBandFirst; iBand <= iBandLast; iBand++) {
3272           Point p = null;
3273           for (int iPoint = bandList[iBand];
3274                iPoint != GChart.NAI;
3275                iPoint=p.getINextInBand()) {
3276             if (iPoint < 0 || iPoint >= getNPoints())
3277                 throw new IllegalStateException(
3278    "Inappropriately terminated band-point-list, GChart bug likely. " + 
3279    "iPoint=" + iPoint + " nPoints=" + getNPoints() +
3280    " iBand="+iBand+" iBandFirst="+iBandFirst+" iBandLast="+iBandLast +
3281    " xBrush="+xBrush+" yBrush="+yBrush+" brushWidth="+brushWidth +
3282    " brushHeight=" +brushHeight + " bandThickness=" + bandThickness);
3283             p  = getPoint(iPoint);
3284             if (symType.isIntersecting(plotPanel, getSymbol(), 
3285                                        iPoint, onY2(),
3286                                        xBrush, yBrush,
3287                                        brushWidth, brushHeight)) {
3288               // this point touches the brush (keep it if closest)  
3289               double xPoint = symType.getCenterX(plotPanel, 
3290                                             getSymbol(), iPoint);
3291               double yPoint = symType.getCenterY(plotPanel, 
3292                                             getSymbol(), iPoint, onY2());
3293               double dx = getSymbol().xScaleFactor*(xPoint-xBrush);
3294               double dy = getSymbol().yScaleFactor*(yPoint-yBrush);
3295               double d = dx*dx + dy*dy;
3296               if (d < dBest) {  
3297                 result = iPoint;
3298                 dBest = d;      
3299               }
3300               else if (d == dBest && iPoint > result) {
3301                  // in the case of ties, choose largest point index
3302                  // (highest "z-order" -- the one "on top")
3303                 result = iPoint;
3304                 dBest = d;      
3305               }
3306                  
3307             }
3308           }
3309         }
3310                
3311         return result;
3312    
3313       }
3314       
3315      /**
3316       * @deprecated
3317       *
3318       * This method is equivalent to: 
3319       * <p>
3320       *<tt>getSymbol().getHovertextTemplate()</tt>
3321       * <p>
3322       * 
3323       * It is retained only for GChart 1.1 compatibility purposes.
3324       * 
3325       * @see Symbol#getHovertextTemplate() Symbol.getHovertextTemplate
3326       *
3327       */ 
3328        public String getHovertextTemplate() {
3329          return symbol.getHovertextTemplate();
3330        }
3331    
3332      /**
3333       ** Returns the HTML defining this curve's legend label.
3334       **
3335       ** @return the legend label HTML for this curve
3336       **
3337       ** @see #setLegendLabel setLegendLabel
3338       **
3339       **/ 
3340        public String getLegendLabel() {
3341          return legendHTML;
3342        }
3343      
3344      /**
3345       * Returns the number of points this curve contains.
3346       *
3347       * @return the number of points this curve contains.
3348       * 
3349       * @see #getPoint getPoint
3350       * @see #addPoint(double, double) addPoint(double,double)
3351       * @see #addPoint(int,double,double) addPoint(int,double,double)
3352       * @see #removePoint removePoint
3353       * @see #clearPoints clearPoints
3354       */  
3355        public int getNPoints() {
3356          return points.size();
3357        }
3358    
3359        /**
3360         * Returns a reference to the GChart that contains this
3361         * curve.
3362         *
3363         * @return GChart that contains this curve--its "parent".
3364         *
3365         */
3366        public GChart getParent() {return GChart.this; }
3367      
3368      /**
3369       * Convenience method equivalent to <tt>getPoint(getNPoints()-1)</tt>.
3370       * <p>
3371       * This method makes code more readable for the common case when
3372       * you first add a point to the end of a curve, and then modify that
3373       * point's attributes, as illustrated below:
3374       * <p>
3375       * <pre>
3376       *   class MyChart extends GChart {
3377       *     public MyChart() {
3378       *       addCurve();
3379       *       for (int i=0; i < 10; i++) {
3380       *         getCurve().addPoint(i,i);
3381       *         getCurve().getPoint().setAnnotationText("Point " + i);
3382       *       }
3383       *       update();
3384       *     }
3385       *   }
3386       * </pre>
3387       * 
3388       * @return the point on the curve with the highest integer index
3389       *
3390       * @see #getPoint(int) getPoint(int)
3391       * @see #getNPoints() getNPoints()
3392       * 
3393       */
3394        public Point getPoint() {
3395           Point result = getPoint(getNPoints()-1);
3396           return result;
3397        }
3398    
3399      /**
3400       * Returns a reference to the point at the specified
3401       * index.  The returned reference can be used to modify
3402       * various properties of the point, such as
3403       * its optional annotation (text label).
3404       * 
3405       * <p>
3406       * @param iPoint the index of the point to be returned.
3407       * @return a reference to the Point at the specified index.
3408       *
3409       * @see #addPoint(double, double) addPoint(double,double)
3410       * @see #addPoint(int,double,double) addPoint(int,double,double)
3411       * @see #removePoint removePoint
3412       * @see #clearPoints clearPoints
3413       * @see #getNPoints getNPoints
3414       */
3415        public Point getPoint(int iPoint) {
3416           if (iPoint < 0 || iPoint >= points.size())
3417              throw new IllegalArgumentException(
3418               "Point index iPoint=" + iPoint + ". " + 
3419               "is either < 0 or >= the number of points on the curve.");
3420           Point result = points.get(iPoint);
3421           return result;
3422        }
3423    
3424        
3425        /**
3426         * Returns the positional index (within this curve's list of
3427         * points) of the specified point.
3428         * <p>
3429         * 
3430         * Returns <tt>GChart.NAI</tt> if the specified point is not found on
3431         * this curve's point list.
3432         * 
3433         * <p>
3434         * @param point point whose list position is to be retrieved
3435         * @return position of point on this curve's point list, or
3436         *        <tt>GChart.NAI</tt>
3437         *        if not on the list.
3438         *
3439         * @see #getPoint() getPoint()
3440         * @see #getPoint(int) getPoint(int)
3441         * @see #addPoint addPoint
3442         * @see #removePoint removePoint
3443         * @see #clearPoints clearPoints
3444         * @see #getNPoints getNPoints
3445         */
3446        public int getPointIndex(Point point) {
3447           int result = points.indexOf(point);
3448           if (-1 == result) result = GChart.NAI;
3449           return result;
3450        }
3451      /**
3452        ** Returns the symbol associated with this curve.
3453        ** <p>
3454        ** 
3455        ** Though you cannot set the symbol itself (there is no
3456        ** <tt>setSymbol</tt> method) you can have essentially
3457        ** the same effect by setting the <tt>SymbolType</tt> (to get
3458        ** qualitatively different kinds of symbols, e.g.
3459        ** bar-chart bars vs. boxes) and by changing symbol
3460        ** attributes such as background color, height, and
3461        ** width.
3462        ** 
3463        ** @return the symbol used to represent points on this curve
3464        **
3465        ** @see Symbol#setSymbolType Symbol.setSymbolType
3466        ** @see Symbol#setBackgroundColor Symbol.setBackgroundColor
3467        ** @see Symbol#setBorderWidth Symbol.setBorderWidth
3468        ** @see Symbol#setBorderStyle Symbol.setBorderStyle
3469        ** @see Symbol#setWidth Symbol.setWidth
3470        ** @see Symbol#setHeight Symbol.setHeight
3471        ** @see Symbol#setModelWidth Symbol.setModelWidth
3472        ** @see Symbol#setModelHeight Symbol.setModelHeight
3473        ** 
3474        **/
3475         public Symbol getSymbol() {
3476           return symbol;
3477         }
3478      
3479      /**
3480       * Returns the y-axis (Y_AXIS or Y2_AXIS) this curve is
3481       * plotted on.
3482       *
3483       * @return an identifier, either Y_AXIS, or Y2_AXIS, indicating
3484       *   if this curve is plotted on the left (y) or right (y2) y-axis
3485       * 
3486       ** @see #setYAxis setYAxis
3487       ** @see GChart#Y_AXIS Y_AXIS
3488       ** @see GChart#Y2_AXIS Y2_AXIS
3489       ** 
3490       */
3491        public YAxisId getYAxis() {
3492          return yAxisId;
3493        }
3494    
3495      /** Is this curve visible on the chart and legend key,
3496        ** or is it hidden from view.
3497        **
3498        ** @return true if the curve is visible, false otherwise.
3499        **
3500        ** @see #setVisible setVisible
3501        **/ 
3502         public boolean isVisible() {return isVisible;}
3503    
3504      /** Convenience method equivalent to <tt>getYAxis()==Y2_AXIS</tt>.
3505       *
3506       * @return true if curve is on second y-axis, else false
3507       *
3508       * @see #getYAxis getYAxis
3509       */ 
3510        public boolean onY2() {
3511           return yAxisId == Y2_AXIS;
3512        }
3513      /**
3514       * Removes the point at the specified index.
3515       *
3516       * @param iPoint index of point to be removed.
3517       * 
3518       * @see #getPoint getPoint
3519       * @see #addPoint(double, double) addPoint(double,double)
3520       * @see #addPoint(int,double,double) addPoint(int,double,double)
3521       * @see #clearPoints clearPoints
3522       * @see #getNPoints getNPoints
3523       */
3524        public void removePoint(int iPoint) {
3525           if (iPoint < 0 || iPoint >= getNPoints())
3526              throw new IllegalArgumentException(
3527                 "iPoint=" + iPoint + " iPoint arg must be >= 0 and < " + 
3528                  getNPoints() + ", the number of points on the curve.");
3529           invalidate(); 
3530    
3531           // simulate user moving away from point before it is deleted
3532           // (this assures that any required hoverCleanup gets called,
3533           //  and clears the otherwise dangling reference to the point)
3534           if (plotPanel.touchedPoint == getPoint(iPoint))
3535              plotPanel.touch(null);  
3536           
3537           points.remove(iPoint);
3538        }
3539    
3540        /**
3541         * Removes the given point from this curve.
3542         * <p>
3543         *
3544         * If the given point is not on this curve, or is
3545         * <tt>null</tt>, an exception is thrown.
3546         *
3547         * @param p the point to be removed. 
3548         *
3549         *
3550         */
3551        public void removePoint(Point p) {
3552           if (null == p)
3553              throw new IllegalArgumentException("p cannot be null.");
3554           int index = getPointIndex(p);
3555           if (GChart.NAI == index)
3556              throw new IllegalArgumentException("p must be a point on this curve " +
3557                "(whose curveIndex is " + getParent().getCurveIndex(this) + ")");
3558           removePoint(index);
3559        }
3560      /**
3561       ** @deprecated
3562       **
3563       ** This method is equivalent to:
3564       ** <p>
3565       **  <tt>getSymbol().setHovertextTemplate(hovertextTemplate)</tt>
3566       ** <p>
3567       ** It is retained only for GChart 1.1 compatibility purposes.
3568       **
3569       ** @see Symbol#setHovertextTemplate Symbol.setHovertextTemplate
3570       **/ 
3571        public void setHovertextTemplate(String hovertextTemplate) {
3572           symbol.setHovertextTemplate(hovertextTemplate);
3573        }
3574      /**
3575       ** Sets the HTML that defines the label shown to the
3576       ** right of the icon representing the curve's symbol in
3577       ** the chart's legend.
3578       ** 
3579       ** <p>
3580       ** Setting the legend label to <tt>null</tt> removes the
3581       ** entire row (the label and the icon) associated with
3582       ** this curve from the chart key. 
3583       ** <p>
3584       ** Note that, since <tt>null</tt> is the default, unless
3585       ** you set at least one legend label, no chart key will
3586       ** appear at all.
3587       ** 
3588       ** @param legendHTML the HTML defining this curve's legend label.
3589       **                   or <tt>null</tt> to remove the curve from
3590       **                   the legend entirely.
3591       **                   
3592       ** @see #getLegendLabel getLegendLabel
3593       ** @see GChart#setLegendThickness setLegendThickness
3594       ** 
3595       **/ 
3596        public void setLegendLabel(String legendHTML) {
3597           chartDecorationsChanged = true;
3598           this.legendHTML = legendHTML;
3599        }
3600      /**
3601        ** Defines if this curve is visible both in the plotting
3602        ** region and on the legend key.
3603        ** <p>
3604        ** 
3605        ** <i>Notes:</i>
3606        **
3607        ** <ol>
3608        **  <li>A curve must also have a non-<tt>null</tt> legend label
3609        ** if it is to appear on the legend key.
3610        ** <p>
3611        ** 
3612        **  <li>Hidden curves are excluded from the computation
3613        ** of any auto-computed axis limits.
3614        **
3615        ** </ol>
3616        **
3617        ** @param isVisible false to hide curve, true to reveal it.
3618        **
3619        ** @see #isVisible() isVisible
3620        ** @see #setLegendLabel setLegendLabel
3621        ** 
3622        **/ 
3623        public void setVisible(boolean isVisible) {
3624           /* Axis curve count bookkeeping requires that curve be on the
3625            * list of curves.
3626            * <p>
3627            * 
3628            * Developer refs to removed curves could screw up this
3629            * bookkeeping.
3630            * <p>
3631            * 
3632            * Though often this would be a developer error, best not to
3633            * throw an exception because some developers could use deleted
3634            * curves as repositories for curve state data, or an event
3635            * sequence might produce setVisible calls through a dangling
3636            * curve reference after a curve had been removed, and best to
3637            * let developer get away with that kind of thing.  <p>
3638            * 
3639            * No point in invalidating since removed curves can never
3640            * become part of a rendered GChart again.
3641            * 
3642            */
3643           
3644            if (getIndexOf() == GChart.NAI) {
3645               this.isVisible = isVisible;
3646               return;          
3647            }
3648           
3649            invalidate();
3650    
3651            // hover selection feedback curves (which are system curves)
3652            // never impact curve counts, need to refresh decorations, etc.
3653            if (isSystemCurve()) {
3654               this.isVisible = isVisible;
3655               return;
3656            }
3657            
3658            if (this.isVisible != isVisible) {
3659               Axis yaxis = (getYAxis() == Y_AXIS) ?
3660                             GChart.this.getYAxis() :
3661                             GChart.this.getY2Axis();
3662               boolean axisCreatedOrDestroyed;
3663               if (isVisible) {
3664                 axisCreatedOrDestroyed =
3665                    (yaxis.getNCurvesVisibleOnAxis() == 0);
3666                 getXAxis().incrementCurves();
3667                 yaxis.incrementCurves();
3668               }
3669               else {
3670                 getXAxis().decrementCurves();
3671                 yaxis.decrementCurves(); 
3672                 axisCreatedOrDestroyed =
3673                   (yaxis.getNCurvesVisibleOnAxis() == 0);
3674               }
3675    
3676               if ((null != getLegendLabel() && isLegendVisible()) ||
3677                   axisCreatedOrDestroyed)
3678                 chartDecorationsChanged = true;
3679    
3680               this.isVisible = isVisible;
3681            }
3682         }
3683         
3684      /** Sets the y-axis that this curve is plotted on.
3685       ** <p>
3686       ** @param axisId must be either GChart.Y_AXIS or
3687       **               GChart.Y2_AXIS
3688       **
3689       ** @see #getYAxis getYAxis
3690       ** @see GChart#Y_AXIS Y_AXIS
3691       ** @see GChart#Y2_AXIS Y2_AXIS
3692       ** 
3693       ** 
3694       **/
3695        public void setYAxis(YAxisId axisId) {
3696           invalidate();
3697           if (isSystemCurve()) {
3698              yAxisId = axisId;
3699           }
3700           else if (axisId != yAxisId) {
3701              if (axisId == Y2_AXIS) { // from Y to Y2
3702                     GChart.this.getYAxis().decrementCurves();
3703                     GChart.this.getY2Axis().incrementCurves();
3704              }
3705              else {   // from Y2 to Y
3706                     GChart.this.getY2Axis().decrementCurves();
3707                     GChart.this.getYAxis().incrementCurves();
3708              }
3709              yAxisId = axisId;
3710           }
3711        }
3712    
3713        // Is this specific curve actually clipped to the plot area? 
3714        private boolean getActuallyClippedToPlotArea() {
3715           boolean result = getClipToPlotArea();
3716           if (result) {
3717             // decorative, hover feedback curves are never clipped 
3718             int rpIndex = getRenderingPanelIndex(getIndexOf());
3719             if (PlotPanel.DECORATIVE_RENDERING_PANEL_INDEX == rpIndex ||
3720                 isHoverFeedbackRenderingPanel(rpIndex))
3721                result = false;
3722           }
3723           return result;
3724        }
3725        
3726        /*
3727         * Is this curve one of GChart's special, internally created, system
3728         * curves? These curves can't be directly accessed by users, and are
3729         * used by GChart to render special features of the chart, such as
3730         * the hover selection cursors, titles, footnotes, etc.
3731         * 
3732         */ 
3733        private boolean isSystemCurve() {
3734           // negative curve indexes are reserved for system curves
3735           boolean result = (indexOf != GChart.NAI) &&
3736                            externalCurveIndex(indexOf) < 0;
3737           return result;
3738        }
3739      // renders the specified point of this curve on the given panel
3740        void realizePoint(PlotPanel pp,
3741                          GraphicsRenderingPanel grp,
3742                          AnnotationRenderingPanel arp,
3743                          int iPoint) {
3744         Point p = points.get(iPoint);
3745         double x = p.getX();
3746         double y = p.getY();
3747         // skip points at undefined locations
3748         if ((x!=x) || (y!=y)) return; // x!=x is a faster isNaN
3749         double prevX = Double.NaN;
3750         double prevY = Double.NaN;
3751         if (iPoint > 0) {
3752            Point prevP = points.get(iPoint-1);
3753            prevX = prevP.getX();
3754            prevY = prevP.getY();
3755         }
3756         double nextX = Double.NaN;
3757         double nextY = Double.NaN;
3758         Point nextP = null;
3759         if (iPoint < getNPoints()-1) {
3760            nextP = points.get(iPoint+1);
3761            nextX = nextP.getX();
3762            nextY = nextP.getY();
3763         }
3764    
3765         // if point was not assigned to any band, it's not drawn
3766         // at all (undefined x or y, or off chart entirely)
3767         boolean drawMainSymbol = (p.getINextInBand() != iPoint);
3768    
3769         getSymbol().realizeSymbol(pp, grp, arp, p.getAnnotation(), onY2(),
3770                                   getActuallyClippedToPlotArea(),
3771                                   getClipToDecoratedChart(),
3772                                   drawMainSymbol,
3773                                   x, y, prevX, prevY, nextX, nextY);
3774    //    }
3775      }
3776      /**
3777       ** Represents a single point on one of the chart's
3778       ** curves. This includes the x, y values of the point in
3779       ** "model coordinates" (arbitrary, application-specific,
3780       ** units), as well as an optional annotation (text label)
3781       ** for the point.
3782       ** <p>
3783       ** To create points, use a curve's <tt>addPoint</tt> method.
3784       ** 
3785       ** @see Curve#addPoint addPoint
3786       **/ 
3787      public class Point {
3788         
3789         // x, y location (user coordinates) of point
3790         // (points are drawn using the containing curve's symbol)
3791         private double x; 
3792         private double y; 
3793         Annotation annotation = null;
3794         // Points to index of next point in a vertical or horizontal
3795         // band (used by the <tt>bandSeparatePoints</tt> method).
3796         private int iNextInBand = GChart.NAI;
3797         int getINextInBand() { return iNextInBand;}
3798         void setINextInBand(int iNext) {iNextInBand = iNext;}
3799         Point(double x, double y) {
3800           this.x = x;   
3801           this.y = y;
3802         }
3803    
3804         /**
3805          ** Returns true if annotation will be rendered in a bold,
3806          ** or false if in normal, weight font. 
3807          **
3808          ** @return if this annotation is in bold or not.
3809          **
3810          ** @see #setAnnotationFontWeight setAnnotationFontWeight
3811          **/ 
3812         public String getAnnotationFontWeight() {
3813            if (null == annotation) annotation = new Annotation();   
3814            return annotation.getFontWeight();
3815         }
3816    
3817         /**
3818          ** Returns the color of the font used to display the point's
3819          **   annotation text.
3820          **   
3821          ** @return CSS color string defining the annotation's color
3822          **
3823          ** @see #setAnnotationFontColor setAnnotationFontColor
3824          **/ 
3825         public String getAnnotationFontColor() {
3826            if (null == annotation) annotation = new Annotation();   
3827            return annotation.getFontColor();
3828         }
3829    
3830         
3831         /**
3832          ** Returns the CSS font-style in which the text of this
3833          **  annotation will be rendered.
3834          **
3835          ** @return the font-style used by this annotation (italic,
3836          **   normal, etc.)
3837          **
3838          ** @see #setAnnotationFontStyle setAnnotationFontStyle
3839          **/ 
3840         public String getAnnotationFontStyle() {
3841            if (null == annotation) annotation = new Annotation();   
3842            return annotation.getFontStyle();
3843         }
3844    
3845         /**
3846          ** Returns the CSS font size of this point's annotation
3847          ** (text label), in pixels.
3848          **
3849          ** @return the font size of this point's annotation.
3850          **
3851          ** @see #setAnnotationFontSize setAnnotationFontSize
3852          **/ 
3853         public int getAnnotationFontSize() {
3854            if (null == annotation) annotation = new Annotation();   
3855            return annotation.getFontSize();
3856         }
3857    
3858         
3859         
3860         /** Returns the previously specified location, relative
3861          ** to the symbol representing the point, of the
3862          ** annotation (text label) associated with this point.
3863          **
3864          ** @return relative location of the point's annotation
3865          ** 
3866          ** @see #setAnnotationLocation setAnnotationLocation
3867          ** 
3868          **/ 
3869         public AnnotationLocation getAnnotationLocation() {
3870            if (null == annotation) annotation = new Annotation();   
3871            AnnotationLocation result = annotation.getLocation();
3872            if (null == result) result =
3873              getParent().getSymbol().getSymbolType().defaultAnnotationLocation();
3874            return result;
3875         }
3876         /**
3877          ** Returns the text of this point's annotation.
3878          **
3879          ** @return the text of the annotation, or <tt>null</tt> if this
3880          **   point either lacks an annotation or uses a widget-based
3881          **   annotation.
3882          **
3883          ** @see #setAnnotationText setAnnotationText
3884          ** 
3885          **/ 
3886         public String getAnnotationText() {
3887            if (null == annotation) annotation = new Annotation();   
3888            return annotation.getText();
3889         }
3890    
3891         /**
3892          * Returns the widget reference that defines this point's
3893          * annotation as previously specified by
3894          * <tt>setAnnotationWidget</tt>. Returns <tt>null</tt> if
3895          * the annotation has not yet been specified, or if it was
3896          * defined via <tt>setAnnotationText</tt>.
3897          *
3898          * @return reference to the widget defining this point's
3899          * annotation, or <tt>null</tt> if none.
3900          *
3901          * @see #setAnnotationWidget setAnnotationWidget
3902          * @see #setAnnotationText setAnnotationText
3903          *
3904          */
3905    
3906         public Widget getAnnotationWidget() {
3907            if (null == annotation) annotation = new Annotation();   
3908            return annotation.getWidget();
3909         }
3910         
3911         /**
3912          ** Returns true is the point's annotation is visible, false
3913          ** otherwise
3914          **
3915          ** @return if the a annotation defined for this point will
3916          **    be visible or not after the next update.
3917          **
3918          ** @see #setAnnotationVisible setAnnotationVisible
3919          **/ 
3920         public boolean getAnnotationVisible() {
3921            if (null == annotation) annotation = new Annotation();   
3922           return annotation.getVisible();
3923         }
3924    
3925         /**
3926          ** Returns the distance, in pixels, that this annotation
3927          ** will be shifted along the x-axis from it's default
3928          ** location.  <p>
3929          **
3930          ** @return amount annotation will be shifted along the x-axis,
3931          **   in pixels.
3932          **   
3933          ** @see #setAnnotationXShift setAnnotationXShift
3934          **/ 
3935          public int getAnnotationXShift() {
3936            if (null == annotation) annotation = new Annotation();   
3937            return annotation.getXShift();
3938          }
3939         
3940         /**
3941          ** Returns the distance, in pixels, that this annotation
3942          ** will be shifted along the y-axis from it's default
3943          ** location.  <p>
3944          **
3945          ** @return amount annotation will be shifted along the y-axis,
3946          **   in pixels.
3947          **   
3948          ** @see #setAnnotationYShift setAnnotationYShift
3949          **/ 
3950          public int getAnnotationYShift() {
3951            if (null == annotation) annotation = new Annotation();   
3952            return annotation.getYShift();
3953          }
3954    
3955          /** Returns the <tt>Curve</tt> that this point was added to.
3956           **
3957           ** @return a reference to the <tt>Curve</tt> that contains
3958           ** this point (its "parent").
3959           **
3960           **/ 
3961          public Curve getParent() {return Curve.this;}
3962          
3963         /** Returns the x-coordinate of this point in "model units"
3964          ** (arbitrary, application-specific, units). 
3965          **
3966          ** @return the x-coordinate, in model units
3967          **
3968          ** @see #setX setX
3969          ** @see #setY setY
3970          ** @see #getY getY
3971          ** 
3972          **/ 
3973         public double getX() {
3974            return x;
3975         }
3976         /** Returns the y-coordinate of this point in "model units"
3977          ** (arbitrary, application-specific, units). 
3978          **
3979          ** @return the y-coordinate, in model units
3980          **
3981          ** @see #getX getX
3982          ** @see #setX setX
3983          ** @see #setY setY
3984          ** 
3985          **/ 
3986         public double getY() {
3987            return y;
3988         }
3989    
3990         /**
3991          ** Specifies the weight of the font that will be used
3992          ** to render the text of this point's annotation.
3993          ** <p>
3994          ** 
3995          ** @param cssWeight A standard CSS font-weight
3996          **    specification such as normal, bold, bolder, lighter,
3997          **    100, 200, ... 900, or inherit
3998          **
3999          ** @see #getAnnotationFontWeight getAnnotationFontWeight  
4000          ** @see #setAnnotationFontColor setAnnotationFontColor
4001          ** @see #setAnnotationFontStyle setAnnotationFontStyle
4002          ** @see #setAnnotationFontSize setAnnotationFontSize
4003          ** @see #setAnnotationXShift setAnnotationXShift
4004          ** @see #setAnnotationYShift setAnnotationYShift
4005          ** @see #setAnnotationText setAnnotationText
4006          ** @see #setAnnotationVisible setAnnotationVisible
4007          **/ 
4008         public void setAnnotationFontWeight(String cssWeight) {
4009           getParent().invalidate();
4010           if (null == annotation) annotation = new Annotation();   
4011           annotation.setFontWeight(cssWeight);
4012         }
4013         /**
4014          ** Specifies the color of the annotation's font.
4015          **
4016          ** 
4017          ** <p>
4018          ** For more information on standard CSS color
4019          ** specifications see the discussion in
4020          ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
4021          ** <p>
4022          **        
4023          ** @param cssColor color of the font used to display this
4024          **    point's annotation message.
4025          **
4026          ** @see #getAnnotationFontColor getAnnotationFontColor  
4027          ** @see #setAnnotationFontWeight setAnnotationFontWeight
4028          ** @see #setAnnotationFontStyle setAnnotationFontStyle
4029          ** @see #setAnnotationFontSize setAnnotationFontSize
4030          ** @see #setAnnotationXShift setAnnotationXShift
4031          ** @see #setAnnotationYShift setAnnotationYShift
4032          ** @see #setAnnotationText setAnnotationText
4033          ** @see #setAnnotationVisible setAnnotationVisible
4034          **/ 
4035         public void setAnnotationFontColor(String cssColor) {
4036            getParent().invalidate();
4037            if (null == annotation) annotation = new Annotation();   
4038            annotation.setFontColor(cssColor);
4039         }
4040    
4041         
4042         /**
4043          ** Specifies the CSS font-style used by this point's annotation
4044          ** message.
4045          **
4046          ** @param cssStyle any valid CSS font-style, namely,
4047          **   normal, italic, oblique, or inherit.
4048          **
4049          ** @see #getAnnotationFontStyle getAnnotationFontStyle  
4050          ** @see #setAnnotationFontWeight setAnnotationFontWeight
4051          ** @see #setAnnotationFontColor setAnnotationFontColor
4052          ** @see #setAnnotationFontSize setAnnotationFontSize
4053          ** @see #setAnnotationXShift setAnnotationXShift
4054          ** @see #setAnnotationYShift setAnnotationYShift
4055          ** @see #setAnnotationText setAnnotationText
4056          ** @see #setAnnotationVisible setAnnotationVisible
4057          **/ 
4058         public void setAnnotationFontStyle(String cssStyle) {
4059            getParent().invalidate();
4060            if (null == annotation) annotation = new Annotation();   
4061            annotation.setFontStyle(cssStyle);
4062         }
4063         /**
4064          ** Specifies the CSS font size of this point's annotation, in
4065          ** pixels.
4066          **
4067          ** @param fontSize the font size of this point's annotation, in
4068          **   pixels.
4069          **
4070          ** @see #getAnnotationFontSize getAnnotationFontSize  
4071          ** @see #setAnnotationFontWeight setAnnotationFontWeight
4072          ** @see #setAnnotationFontColor setAnnotationFontColor
4073          ** @see #setAnnotationFontStyle setAnnotationFontStyle
4074          ** @see #setAnnotationXShift setAnnotationXShift
4075          ** @see #setAnnotationYShift setAnnotationYShift
4076          ** @see #setAnnotationText setAnnotationText
4077          ** @see #setAnnotationVisible setAnnotationVisible
4078          **/ 
4079         public void setAnnotationFontSize(int fontSize) {
4080            getParent().invalidate();
4081            if (null == annotation) annotation = new Annotation();   
4082            annotation.setFontSize(fontSize);
4083         }
4084         /**
4085          ** Specifies the location, relative to this point's symbol,
4086          ** of this point's annotation (text label).
4087          ** <p>
4088          ** 
4089          ** You can further adjust the position of a point's
4090          ** annotation by specifying non-zero positional shifts via
4091          ** the <tt>setAnnotationXShift</tt> and
4092          ** <tt>setAnnotationYShift</tt> methods.
4093          **
4094          ** 
4095          ** @param annotationLocation the relative location of
4096          ** the annotation
4097          **
4098          ** @see #getAnnotationLocation getAnnotationLocation
4099          ** @see #setAnnotationFontWeight setAnnotationFontWeight
4100          ** @see #setAnnotationFontColor setAnnotationFontColor
4101          ** @see #setAnnotationFontStyle setAnnotationFontStyle
4102          ** @see #setAnnotationFontSize setAnnotationFontSize
4103          ** @see #setAnnotationText setAnnotationText
4104          ** @see #setAnnotationXShift setAnnotationXShift
4105          ** @see #setAnnotationYShift setAnnotationYShift
4106          ** @see #setAnnotationVisible setAnnotationVisible
4107          ** 
4108          **/
4109         public void setAnnotationLocation(AnnotationLocation
4110                                           annotationLocation) {
4111           getParent().invalidate();
4112           if (null == annotation) annotation = new Annotation();   
4113           annotation.setLocation(annotationLocation);
4114         }
4115    
4116         /**
4117          ** Specifies the text of this point's annotation
4118          ** (label). 
4119          ** <p>
4120          ** 
4121          ** <p>By default text is plain text, though
4122          ** you can change the size, weight, style, and color of
4123          ** the text via the <tt>setAnnotationFont*</tt>
4124          ** family of methods.
4125          ** 
4126          ** <p>
4127          ** 
4128          ** <b>To use HTML, <i>your text must begin with</i>
4129          ** <tt>&lt;html&gt</tt></b> (otherwise, GChart will treat
4130          ** it as plain text). Note that the leading
4131          ** <tt>&lt;html&gt</tt> is stripped off by GChart before
4132          ** your HTML gets to the browser. Since it's just a flag
4133          ** for GChart, not a real HTML tag, you should <i>not</i>
4134          ** use a closing <tt>&lt;/html&gt</tt> at the end.
4135          **
4136          ** <p> <small> The idea for adding HTML support (only plain
4137          ** text was supported originally) came from <a
4138          ** href="http://groups.google.com/group/Google-Web-Toolkit/msg/cb89003dad2416fe">
4139          ** this GWT forum post by Malcolm Gorman</a>. The current
4140          ** HTML support (and, it's natural extension, Widget
4141          ** support) in tick labels and annotations, which seems so
4142          ** obvious in hindsight, might never have been added had it
4143          ** not been for this post. Thanks!</small>
4144          ** 
4145          ** <p>
4146          **
4147          ** <small><b>How to use the width and height upperbounds:</b>
4148          ** </small>
4149          ** 
4150          ** <p>
4151          ** 
4152          ** <blockquote><small>
4153          **
4154          ** 
4155          ** In most cases, you can safely ignore these two
4156          ** parameters, simply calling the {@link
4157          ** #setAnnotationText(String) 1-arg convenience method}
4158          ** and getting GChart to estimate them for you.
4159          ** <p>
4160          **
4161          ** The width and height upper-bounds define an invisible
4162          ** bounding box (a 1x1 GWT Grid, actualy) that is used to
4163          ** properly align and center your annotation.
4164          ** <p>
4165          ** 
4166          ** <p> Annotations can
4167          ** become misaligned if, say, due to the user zooming up
4168          ** their font size, an annotation's size exceeds these
4169          ** upperbounds. This misalignment problem can be fixed by
4170          ** specifying a larger width and/or height upperbound.
4171          ** But, larger upperbounds slow chart updates a bit.
4172          ** The defaults try to balance the performance and
4173          ** alignment tradeoff.
4174          ** <p>
4175          **
4176          ** There is one annoying but generally harmless side effect of
4177          ** using very large upperbounds: most browsers will extend their
4178          ** scroll regions to the right (and presumably below) the real
4179          ** page content so as to include the invisible bounding box. When
4180          ** this happens, it looks to the user as if there is a bunch of
4181          ** blank space on, say, the right edge of the page. In practice,
4182          ** I've always been able to prevent this problem simply by
4183          ** choosing at-least-somewhat-reasonably-tight upper bounds,
4184          ** though a little blank space may be unavoidable in some special
4185          ** cases.
4186          ** 
4187          ** </blockquote></small>
4188          ** 
4189          ** @param annotationText the text or (<tt>&lt;html&gt</tt>
4190          ** prefixed) HTML of this point's
4191          ** annotation, or <tt>null</tt> to remove all annotation.
4192          ** 
4193          ** @param widthUpperBound an upper bound on the width of
4194          ** the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
4195          ** get GChart to estimate this width using a heuristic
4196          ** that works fine most of the time.
4197          ** 
4198          ** @param heightUpperBound an upper bound on the height of
4199          ** the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
4200          ** get GChart to estimate this height using a heuristic
4201          ** that works fine most of the time.
4202          **
4203          ** @see #getAnnotationText getAnnotationText
4204          ** @see #setAnnotationText(String) setAnnotationText(String)
4205          ** @see #setAnnotationLocation setAnnotationLocation
4206          ** @see #setAnnotationFontWeight setAnnotationFontWeight
4207          ** @see #setAnnotationFontColor setAnnotationFontColor
4208          ** @see #setAnnotationFontStyle setAnnotationFontStyle
4209          ** @see #setAnnotationFontSize setAnnotationFontSize
4210          ** @see #setAnnotationWidget setAnnotationWidget
4211          ** @see #setAnnotationXShift setAnnotationXShift
4212          ** @see #setAnnotationYShift setAnnotationYShift
4213          ** @see #setAnnotationVisible setAnnotationVisible
4214          ** @see GChart.Axis#addTick(double,String,int,int) addTick
4215          ** 
4216          **/
4217         public void setAnnotationText(String annotationText,
4218                                       int widthUpperBound,
4219                                       int heightUpperBound) {
4220            getParent().invalidate();
4221            if (null == annotation) annotation = new Annotation();   
4222            annotation.setText(annotationText,
4223                               widthUpperBound,
4224                               heightUpperBound);
4225         }
4226         /**
4227          * Sets the text of an annotation.
4228          * <p>
4229          * This is a convenience method equivalent to
4230          * <tt>setAnnotationText(annotationText, GChart.NAI, GChart.NAI)</tt>. See
4231          * that method for further details.
4232          * <p>
4233          *
4234          ** @param annotationText the text or
4235          ** (<tt>&lt;html&gt</tt>-prefixed) HTML of this point's
4236          ** annotation, or <tt>null</tt> to remove all annotation.
4237          *
4238          * @see #setAnnotationText(String, int, int) 
4239          *       setAnnotationText(String,int,int)
4240          *       
4241          */ 
4242         public void setAnnotationText(String annotationText) {
4243              setAnnotationText(annotationText, GChart.NAI, GChart.NAI);
4244         }
4245    
4246         /**
4247          ** Specifies a widget defining this point's annotation
4248          ** <p>
4249          ** This method is similar to <tt>setAnnotationText</tt>
4250          ** except that it uses a widget, rather than a string
4251          ** to define this point's annotation.
4252          ** Although the string based method is faster
4253          ** on first chart rendering, and uses less memory, the
4254          ** widget-based method allows you to change the annotation
4255          ** independently of the chart--potentially bypassing (or
4256          ** at least speeding up) expensive chart updates later on.
4257          ** <p>
4258          **
4259          ** You might use a widget-based annotation to pop-up a
4260          ** message whenever the user clicks on a button underneath
4261          ** a particular data point on the chart, to include a small
4262          ** GWT <tt>Grid</tt> as a table embedded in the upper left
4263          ** hand corner of the chart, to trigger mouse-over events
4264          ** when the user hovers over a transparent image-based
4265          ** annotation centered on a particular point, etc.
4266          ** <p>
4267          ** 
4268          ** <i>Tip:</i>If you need to instrument a chart using
4269          ** widgets precisely positioned on the chart, but not
4270          ** associated with any visible curve, add a curve just to
4271          ** hold these annotations, with one point per annotation,
4272          ** and set that curve's symbol type
4273          ** to <tt>SymbolType.NONE</tt>.
4274          ** <p>
4275          ** 
4276          ** <b><i>Warning:</i></b> If you use the exact same widget
4277          ** reference to define two different annotations, GChart
4278          ** will render only one of them, and <i>there is no easy
4279          ** rule</i> that lets you reliably determine which one. So,
4280          ** don't do that.  Instead, if you want to use the same
4281          ** widget for two different annotations, use two identical
4282          ** but distinct copies of that widget. Similarly, if you
4283          ** want to move a single widget annotation from one point
4284          ** to another, be sure to "<tt>null</tt> out" the first
4285          ** point's annotation (e.g.  via
4286          ** <tt>setAnnotationWidget(null)</tt>) or the widget may
4287          ** not be rendered where you expect. You should also
4288          ** <tt>null</tt> out the annotation widget reference before
4289          ** moving the annotation widget to a position in the DOM
4290          ** completely outside of the GChart. A little extra
4291          ** bookkeeping on your part makes it possible to
4292          ** significantly simplify and streamline GChart's rendering
4293          ** algorithms. 
4294          **
4295          *  @param annotationWidget the GWT Widget that defines this
4296          *    point's annotation. 
4297          *
4298          *  @param widthUpperBound an upper bound on the width of
4299          *  the Widget, in pixels. If this and the next
4300          *  parameter are omitted, GChart will use
4301          *  <tt>DEFAULT_WIDGET_WIDTH_UPPERBOUND</tt>.
4302          *  
4303          *  @param heightUpperBound an upper bound on the height of
4304          *  the Widget, in pixels. If this and the previous
4305          *  parameter are omitted, GChart will use <tt>
4306          *  DEFAULT_WIDGET_HEIGHT_UPPERBOUND</tt>
4307          *
4308          * 
4309          * @see #getAnnotationWidget getAnnotationWidget
4310          * @see #setAnnotationText(String, int, int) 
4311          *       setAnnotationText(String,int,int)
4312          * @see #setAnnotationWidget(Widget)
4313          * setAnnotationWidget(Widget)
4314          * @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
4315          * @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
4316          * @see SymbolType#NONE  SymbolType.NONE
4317          * 
4318          **/ 
4319         public void setAnnotationWidget(Widget annotationWidget,
4320                                       int widthUpperBound,
4321                                       int heightUpperBound) {
4322            getParent().invalidate();
4323            if (null == annotation) annotation = new Annotation();   
4324         // accept "Not an Integer" (because setAnnotationText does)
4325           if (widthUpperBound == GChart.NAI)
4326              widthUpperBound = DEFAULT_WIDGET_WIDTH_UPPERBOUND;
4327           if (heightUpperBound == GChart.NAI)
4328              heightUpperBound = DEFAULT_WIDGET_HEIGHT_UPPERBOUND;
4329           annotation.setWidget(annotationWidget,
4330                                widthUpperBound,
4331                                heightUpperBound);
4332         }
4333    
4334         /**
4335          * Specifies a widget defining this point's annotation.
4336          * <p>
4337          * A convenience method equivalent to
4338          * <tt>setAnnotationWidget(annotationWidget,
4339          * DEFAULT_WIDGET_WIDTH_UPPERBOUND,
4340          * DEFAULT_WIDGET_HEIGHT_UPPERBOUND)</tt>
4341          *
4342          *  @param annotationWidget the GWT Widget that defines this
4343          *    point's annotation. 
4344          *
4345          * @see #setAnnotationWidget(Widget,int,int)
4346          * setAnnotationWidget(Widget,int,int)
4347          * @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
4348          * @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
4349          *
4350          */ 
4351         public void setAnnotationWidget(Widget annotationWidget) {
4352            setAnnotationWidget(annotationWidget, DEFAULT_WIDGET_WIDTH_UPPERBOUND,
4353                              DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
4354         }
4355    
4356         
4357         /**
4358          ** Specifies if this point's annotation
4359          ** (label) is visible or not. 
4360          ** <p>
4361          ** 
4362          ** @param isVisible use true to make the annotation
4363          **   visible, or false to hide it.
4364          **
4365          ** @see #getAnnotationVisible getAnnotationVisible
4366          ** @see #setAnnotationLocation setAnnotationLocation
4367          ** @see #setAnnotationFontWeight setAnnotationFontWeight
4368          ** @see #setAnnotationFontColor setAnnotationFontColor
4369          ** @see #setAnnotationFontStyle setAnnotationFontStyle
4370          ** @see #setAnnotationFontSize setAnnotationFontSize
4371          ** @see #setAnnotationXShift setAnnotationXShift
4372          ** @see #setAnnotationYShift setAnnotationYShift
4373          ** @see #setAnnotationText setAnnotationText
4374          **/
4375          public void setAnnotationVisible(boolean isVisible) {
4376            getParent().invalidate();
4377            if (null == annotation) annotation = new Annotation();   
4378            annotation.setVisible(isVisible);
4379          }
4380    
4381          
4382    
4383         /**
4384          ** Specifies the number of pixels (along the x-axis) to
4385          ** move this point's annotation from its default,
4386          ** <tt>AnnotationLocation</tt>-defined, position.  Negative
4387          ** values move the annotation in the negative x direction.
4388          **
4389          ** <p> For example, with the default <tt>xShift</tt> of 0,
4390          ** annotations with an <tt>AnnotationLocation</tt> of
4391          ** <tt>EAST</tt> will have their left edges flush against
4392          ** the right edge of, say, a box symbol representing the
4393          ** annotated point.  You could use an <tt>xShift</tt>
4394          ** setting of 10 to move the annotation 10 pixels to the
4395          ** right and thus introduce some space between the
4396          ** annotation and the box.
4397          ** <p>
4398          **
4399          ** <i>Special convention for pie slices:</i>
4400          ** Points on curves whose symbols represent pie
4401          ** slices always have the positive x-axis associated with
4402          ** the shifts specified by this method aligned with the
4403          ** outward-pointing pie radius that bisects the pie slice. This
4404          ** convention makes it easy to move pie slice annotations
4405          ** radially outward (via <tt>xShift > 0</tt>) or
4406          ** radially inward (via <tt>xShift < 0</tt>). For those
4407          ** rare situations where you may need to move a pie
4408          ** annotation perpendicularly to this radius, use
4409          ** <tt>setAnnotationYShift</tt>.
4410          ** 
4411          ** @param xShift number of pixels to move annotation
4412          **   along the x axis from
4413          **   it's default, <tt>AnnotationLocation</tt>-defined,
4414          **   location. 
4415          **
4416          ** @see #setAnnotationYShift setAnnotationYShift
4417          ** @see #setAnnotationLocation setAnnotationLocation
4418          ** @see #setAnnotationFontWeight setAnnotationFontWeight
4419          ** @see #setAnnotationFontColor setAnnotationFontColor
4420          ** @see #setAnnotationFontStyle setAnnotationFontStyle
4421          ** @see #setAnnotationFontSize setAnnotationFontSize
4422          ** @see #setAnnotationText setAnnotationText
4423          ** @see #setAnnotationVisible setAnnotationVisible
4424          ** @see #getAnnotationXShift getAnnotationXShift 
4425          **/
4426          public void setAnnotationXShift(int xShift) {
4427            getParent().invalidate();
4428            if (null == annotation) annotation = new Annotation();   
4429            annotation.setXShift(xShift);
4430          }
4431         /**
4432           ** Specifies the number of pixels (along the y-axis) to
4433          ** move this point's annotation from its default,
4434          ** <tt>AnnotationLocation</tt>-defined, position.  Negative
4435          ** values move the annotation in the negative y direction.
4436          **
4437          ** <p> For example, with the default <tt>yShift</tt> of 0,
4438          ** annotations with an <tt>AnnotationLocation</tt> of
4439          ** <tt>SOUTH</tt> will have their top edges flush against
4440          ** the bottom edge of, say, a box symbol representing the
4441          ** annotated point.  You could use a <tt>yShift</tt>
4442          ** setting of -10 to move the annotation down 10 pixels and
4443          ** thus introduce some spacing between the annotation and
4444          ** the box.
4445          ** <p>
4446          **
4447          ** <i>Special convention for pie slices:</i> The positive
4448          ** y-axis for pie slices always points one 90 degree
4449          ** counter-clockwise rotation from the direction of the
4450          ** outward-pointing pie radius that bisects the pie slice.
4451          ** This convention means that <tt>yShift</tt> moves pie
4452          ** slice annotations along a line <i>perpendicular to</i>
4453          ** this bisecting pie radius. Use the companion method
4454          ** <tt>setAnnotationXShift</tt> for the more common
4455          ** operation of moving the annotation along this bisecting
4456          ** radius.
4457          ** 
4458          ** @param yShift number of pixels to move annotation along
4459          **   the y-axis from it's default,
4460          **   <tt>AnnotationLocation</tt>-defined, location. 
4461          **
4462          ** @see #setAnnotationXShift setAnnotationXShift
4463          ** @see #setAnnotationLocation setAnnotationLocation
4464          ** @see #setAnnotationFontWeight setAnnotationFontWeight
4465          ** @see #setAnnotationFontColor setAnnotationFontColor
4466          ** @see #setAnnotationFontStyle setAnnotationFontStyle
4467          ** @see #setAnnotationFontSize setAnnotationFontSize
4468          ** @see #setAnnotationText setAnnotationText
4469          ** @see #setAnnotationVisible setAnnotationVisible
4470          ** @see #getAnnotationXShift getAnnotationXShift 
4471          **/
4472          public void setAnnotationYShift(int yShift) {
4473            getParent().invalidate();
4474            if (null == annotation) annotation = new Annotation();   
4475            annotation.setYShift(yShift);
4476          }
4477    
4478         /**
4479          * Defines the x-coordinate of this point in "model units"
4480          * (arbitrary, application-specific, units mapped to the
4481          * horizontal dimension of the plot area).
4482          * <p>
4483          *
4484          * <tt>Double.NaN</tt>, <tt>Double.MAX_VALUE</tt>, and
4485          * <tt>-Double.MAX_VALUE</tt> have special meanings. See the
4486          * <tt>addPoint</tt> method for details.
4487          *
4488          *
4489          * @param x the x-coordinate of the point in model units.
4490          * 
4491          ** @see #getX getX
4492          ** @see #setY setY
4493          ** @see #getY getY
4494          ** @see #addPoint addPoint 
4495          */ 
4496         public void setX(double x) {
4497            getParent().invalidate();
4498            this.x = x;
4499         }
4500    
4501         /**
4502          * Defines the y-coordinate of this point in "model units"
4503          * (arbitrary, application-specific, units mapped to the
4504          * vertical dimension of the plot area).
4505          * <p>
4506          *
4507          * <tt>Double.NaN</tt>, <tt>Double.MAX_VALUE</tt>, and
4508          * <tt>-Double.MAX_VALUE</tt> have special meanings. See the
4509          * <tt>addPoint</tt> method for details.
4510          *
4511          * @param y the y-coordinate of the point in model units.
4512          * 
4513          ** @see #getX getX
4514          ** @see #setX setX
4515          ** @see #getY getY
4516          ** @see #addPoint addPoint 
4517          ** 
4518          */ 
4519         public void setY(double y) {
4520            getParent().invalidate();
4521            this.y = y;
4522         }
4523    
4524         Annotation getAnnotation() {
4525            if (annotation == null) annotation = new Annotation();
4526            return annotation;
4527         }
4528         /**
4529          * Retrieves the expanded hovertext associated with this
4530          * point.
4531          * <p>
4532          *
4533          * The expanded hovertext is obtained by replacing any
4534          * embedded parameters in the hovertext template with
4535          * their values as evaluated at this point. For example,
4536          * references to <tt>${x}</tt> and <tt>${y}</tt> in
4537          * the hovertext template are replaced with
4538          * appropriately formatted representations of this
4539          * point's x and y properties.
4540          * <p>
4541          *
4542          * By default, GChart will display this expanded hovertext
4543          * whenever the user "touches" a point with the
4544          * curve-specific, rectangular, mouse-centered, brush.
4545          * <p>
4546          *
4547          * <i>Tip:</i> To define your own custom parameter names
4548          * that can be embedded within hovertext templates and
4549          * will be interpreted/expanded relative to the touched point, use
4550          * the <tt>setHoverParameterInterpreter</tt> method.
4551          *
4552          * @return the expanded hover text associated with this point.
4553          *
4554          * @see Symbol#setHovertextTemplate setHovertextTemplate
4555          * @see #setHoverParameterInterpreter setHoverParameterInterpreter
4556          *
4557          */
4558         public String getHovertext() {
4559            String result = HovertextChunk.getHovertext(
4560               getParent().getSymbol().getHovertextChunks(), this);
4561            return result;
4562              }
4563         
4564      } // end of class GChart.Curve.Point
4565    
4566      /*
4567       * Declares that this curve's rendering panel (its DOM representation)
4568       * is inconsistent with current curve specifications.
4569       * 
4570       * <p>
4571       *
4572       * Sets the flag <tt>update</tt> uses to determine if a curve needs
4573       * to be re-rendered.
4574       *
4575       */
4576      void invalidate() {
4577         // The guard isn't just for speed; it keeps us out of trouble
4578         // when the system curves are being added/configured initially
4579         if (isValidated) { 
4580           isValidated = false;
4581         // for efficiency, all background curves use a single rendering
4582         // panel, so invalidating one background curve invalidates them all
4583           if (indexOf < N_PRE_SYSTEM_CURVES) {
4584              for (int i = 0; i < N_PRE_SYSTEM_CURVES; i++)
4585                 curves.get(i).isValidated = false;
4586           }
4587         }     
4588      }
4589    
4590      /*
4591       * Smallest rectangle containing curve's graphics (ignoring
4592       * annotations)
4593       * <p>
4594       * 
4595       * Each curve has it's own canvas to allow for fast, single
4596       * curve, updates (and we hope this rendering independence will
4597       * facilitate additional features in future releases). So, we
4598       * have to economize on canvas size.  Moreover, because we
4599       * allow rendering outside of the decorated chart region, we
4600       * can't just set the size of the canvas to the size of the
4601       * plot area or decorated chart (even if we could afford to do
4602       * that, memory-wise).
4603       * 
4604       */ 
4605      Rectangle getContainingRectangle(PlotPanel pp) {
4606         final Rectangle result = new Rectangle();
4607         if (getNPoints() == 0) {
4608            result.x = result.y = result.width = result.height = 0;
4609            return result;
4610         }
4611         
4612         double minX = Double.MAX_VALUE;
4613         double maxX = -Double.MAX_VALUE;
4614         double minY = Double.MAX_VALUE;
4615         double maxY = -Double.MAX_VALUE;
4616         boolean pointAtXAxisMin = false; // do keyword positioned points
4617         boolean pointAtXAxisMax = false; // exist on this curve?
4618         boolean pointAtYAxisMin = false;
4619         boolean pointAtYAxisMax = false;
4620         boolean isClippedToDecoratedChart = getClipToDecoratedChart();
4621         boolean isClippedToPlotArea = getActuallyClippedToPlotArea();
4622         // Find min, max for x,y and record each keyword position used
4623         int nPoints = getNPoints();
4624         for (int i = 0; i < nPoints; i++) {
4625            Point p = getPoint(i);
4626            double x = p.getX();
4627            double y = p.getY();
4628            if (Double.MAX_VALUE == x)
4629               pointAtXAxisMax = true; 
4630            else if (-Double.MAX_VALUE == x)
4631               pointAtXAxisMin = true;
4632            else {
4633              if (x < minX) minX = x;
4634              if (x > maxX) maxX = x;  
4635            }
4636            if (Double.MAX_VALUE == y)
4637               pointAtYAxisMax = true; 
4638            else if (-Double.MAX_VALUE == y)
4639               pointAtYAxisMin = true;
4640            else {
4641              if (y < minY) minY = y;
4642              if (y > maxY) maxY = y;  
4643            }
4644         }
4645    
4646         // apply "at min/max" keyword, clipping imposed limits
4647         if (pointAtXAxisMin) 
4648            minX = Math.min(minX, pp.getXMin());
4649         if (isClippedToPlotArea)
4650            minX = Math.max(minX, pp.getXMin());
4651         else if (isClippedToDecoratedChart)
4652            minX = Math.max(minX, getXAxis().pixelToModel(0));
4653         
4654         if (pointAtXAxisMax) 
4655           maxX = Math.max(maxX, pp.getXMax());
4656         if (isClippedToPlotArea)
4657           maxX = Math.min(maxX, pp.getXMax());
4658         else if (isClippedToDecoratedChart)
4659           maxX = Math.min(maxX, getXAxis().pixelToModel(
4660                       pp.getXChartSizeDecoratedQuickly()));
4661    
4662         boolean onY2 = onY2();
4663         if (onY2) {
4664           if (pointAtYAxisMin) 
4665             minY = Math.min(minY, pp.getY2Min());
4666           if (isClippedToPlotArea)
4667             minY = Math.max(minY, pp.getY2Min());
4668           else if (isClippedToDecoratedChart)
4669             minY = Math.max(minY, getY2Axis().pixelToModel(
4670                 pp.getYChartSizeDecoratedQuickly()));
4671           if (pointAtYAxisMax) 
4672             maxY = Math.max(maxY, pp.getY2Max());
4673           if (isClippedToPlotArea)
4674             maxY = Math.min(maxY, pp.getY2Max());
4675           else if (isClippedToDecoratedChart)
4676             maxY = Math.min(maxY, getY2Axis().pixelToModel(0));
4677         }
4678         else {
4679           if (pointAtYAxisMin) 
4680             minY = Math.min(minY, pp.getYMin());
4681           if (isClippedToPlotArea)
4682             minY = Math.max(minY, pp.getYMin());
4683           else if (isClippedToDecoratedChart)
4684             minY = Math.max(minY, GChart.this.getYAxis().pixelToModel(
4685                 pp.getYChartSizeDecoratedQuickly()));
4686           if (pointAtYAxisMax) 
4687             maxY = Math.max(maxY, pp.getYMax());
4688           if (isClippedToPlotArea)
4689             maxY = Math.min(maxY, pp.getYMax());
4690           else if (isClippedToDecoratedChart)
4691             maxY = Math.min(maxY, GChart.this.getYAxis().pixelToModel(0));
4692         }
4693    
4694    // finally, we need to convert to pixels while taking into account
4695    // the size of the rendered symbol itself (e.g. pies can stick
4696    // out from their x,y specified center point, etc.)     
4697         Symbol sym = getSymbol();
4698         SymbolType symType = sym.getSymbolType();
4699    // in obscure cases, canvas could clip without this extra wiggle room
4700         int extraSpace = sym.getFillThickness();
4701         extraSpace += Math.abs(sym.getBorderWidth());
4702         double left0 = symType.getEdgeLeft(pp, sym, minX, onY2);
4703         double left1 = symType.getEdgeLeft(pp, sym, maxX, onY2);
4704         double right0 = symType.getEdgeRight(pp, sym, minX, onY2);
4705         double right1 = symType.getEdgeRight(pp, sym, maxX, onY2);
4706         double bottom0  = symType.getEdgeBottom(pp, sym, minY, onY2);
4707         double bottom1  = symType.getEdgeBottom(pp, sym, maxY, onY2);
4708         double top0 = symType.getEdgeTop(pp, sym, minY, onY2);
4709         double top1 = symType.getEdgeTop(pp, sym, maxY, onY2);
4710    
4711    // baseline bars can flip order, so smallest x could be 'right', etc.
4712         double xPxMin = Math.min(Math.min(left0, left1),
4713                                  Math.min(right0, right1));
4714         double xPxMax = Math.max(Math.max(left0, left1),
4715                                  Math.max(right0, right1));
4716         double yPxMin = Math.min(Math.min(bottom0, bottom1),
4717                                  Math.min(top0, top1));
4718         double yPxMax = Math.max(Math.max(bottom0, bottom1),
4719                                  Math.max(top0, top1));
4720         result.x = xPxMin - extraSpace;
4721         result.y = yPxMin - extraSpace;
4722         result.width = xPxMax - xPxMin + 1 + 2*extraSpace;
4723         result.height = yPxMax - yPxMin + 1 + 2*extraSpace;       
4724    
4725         // result is (roughly) smallest rectangle that contains every
4726         // rendered symbol on this curve (ignoring annotations)
4727         
4728         return result;
4729    
4730      }
4731    
4732      // keeps track of if last rendering was canvas-based or not   
4733      private boolean wasCanvasRendered = false;
4734      void setWasCanvasRendered(boolean wasCanvasRendered) {
4735         this.wasCanvasRendered = wasCanvasRendered;
4736      }
4737      // is curve currently canvas rendered and up-to-date
4738      boolean isCanvasRendered() {
4739         return isValidated && wasCanvasRendered;
4740      }
4741      
4742    } // end of class GChart.Curve       
4743    
4744    
4745         // Allows hovertext templates to be parsed into "chunks"
4746         // so that they can be expanded into hovertext faster.
4747         static class HovertextChunk {
4748            final static int HOVERTEXT_PARAM_NONE = 0; // plain old text
4749            final static int HOVERTEXT_PARAM_X = 1;  // ${x}
4750            final static int HOVERTEXT_PARAM_Y = 2;  // ${y}
4751            final static int HOVERTEXT_PARAM_PIESLICESIZE = 3; // ${pieSlicePercent}
4752            final static int HOVERTEXT_PARAM_USERDEFINED = 4; // ${mySpecialParameter}
4753            int paramId;      // id of substitution parameter
4754            String paramName;  // name of substitution parameter
4755            String chunkText; // plain text that follows this parameter
4756            HovertextChunk(int id, String name, String text) {
4757               paramId = id;
4758               paramName = name;
4759               chunkText = text;
4760            }
4761            // returns array of "chunks" corresponding to the given
4762            // hovertext template
4763            static HovertextChunk[] parseHovertextTemplate(
4764               String htTemplate) {
4765               if (htTemplate.equals("")) return new HovertextChunk[0];
4766               // takes "x=${x}; y=${y}" into {"x=", "x}; y=", "y}"}
4767               // Thus, except for the first, chunks contain a
4768               // keyword like part, followed by a string literal.
4769               String[] sChunk = htTemplate.split("\\$\\{");
4770               HovertextChunk[] result = new HovertextChunk[sChunk.length];
4771    
4772               for (int i = 0; i < sChunk.length; i++) {
4773                  String sC = sChunk[i];
4774                  if (0 == i) 
4775                  // leading (non-parametric) plain text chunk 
4776                     result[i] = new HovertextChunk(HOVERTEXT_PARAM_NONE,
4777                                                    null, sC);                 
4778                  else if (sC.startsWith("x}")) 
4779                     result[i] = new HovertextChunk(
4780                                       HOVERTEXT_PARAM_X, "x",
4781                                       sC.substring("x}".length()));
4782                  else if (sC.startsWith("y}")) 
4783                     result[i] = new HovertextChunk(
4784                                       HOVERTEXT_PARAM_Y, "y",
4785                                       sC.substring("y}".length()));
4786                  else if (sC.startsWith("pieSliceSize}")) 
4787                     result[i] = new HovertextChunk(
4788                                      HOVERTEXT_PARAM_PIESLICESIZE,
4789                                      "pieSliceSize",
4790                                      sC.substring("pieSliceSize}".length()));
4791                  else if (sC.matches("[a-zA-Z][a-zA-Z0-9_]*\\}.*")) {
4792                     // fits pattern for a user defined parameter
4793                     int closeCurlyIndex = sC.indexOf("}"); 
4794                     result[i] = new HovertextChunk(
4795                                       HOVERTEXT_PARAM_USERDEFINED,
4796                                       sC.substring(0, closeCurlyIndex),             
4797                                       sC.substring(closeCurlyIndex+1));
4798                  }
4799                  else {
4800                  // leading "${" without "paramName}". Likely a
4801                  // typo, but output verbatim to give them a clue:
4802                     result[i] = new HovertextChunk(HOVERTEXT_PARAM_NONE,
4803                                                    null, "${" + sC);
4804                  }
4805                     
4806               }
4807               return result;
4808            }
4809    
4810         /* hovertext associated with parsed "chunks" for a given point */   
4811         static String getHovertext(HovertextChunk[] htc,
4812                                    Curve.Point p) {
4813           String result = "";
4814           String xS = null;
4815           String yS = null;
4816           String pieSlicePercentS = null;
4817           HoverParameterInterpreter hpi =
4818             p.getParent().getParent().getHoverParameterInterpreter(); 
4819           for (int i = 0; i < htc.length; i++) {
4820              switch (htc[i].paramId) {
4821                 case HovertextChunk.HOVERTEXT_PARAM_NONE:
4822                    break;
4823                 case HovertextChunk.HOVERTEXT_PARAM_X:
4824                    if (null == xS) {
4825                       String hoverParam = (null == hpi) ? null :
4826                                  hpi.getHoverParameter(htc[i].paramName, p);
4827                       if (null != hoverParam)
4828                          xS = hoverParam;
4829                       else {
4830                          Axis axis =
4831                            p.getParent().getParent().getXAxis();
4832                          xS = axis.formatAsTickLabel(p.getX());
4833                       }
4834                    }
4835                    result += xS;
4836                    break;
4837                 case HovertextChunk.HOVERTEXT_PARAM_Y:
4838                    if (null == yS) {
4839                       String hoverParam = (null == hpi) ? null :
4840                                  hpi.getHoverParameter(htc[i].paramName, p);
4841                       if (null != hoverParam)
4842                          yS = hoverParam;
4843                       else {
4844                          Axis axis = p.getParent().onY2() ?
4845                              p.getParent().getParent().getY2Axis() :
4846                              p.getParent().getParent().getYAxis();
4847                          yS = axis.formatAsTickLabel(p.getY());
4848                       }
4849                    }
4850                    result+=yS;
4851                    break;
4852                      
4853                 case HovertextChunk.HOVERTEXT_PARAM_PIESLICESIZE:
4854                    if (null == pieSlicePercentS) {
4855                       String hoverParam = (null == hpi) ? null :
4856                                  hpi.getHoverParameter(htc[i].paramName, p);
4857                       if (null != hoverParam)
4858                          pieSlicePercentS = hoverParam;
4859                       else {
4860                          double pieSliceSize = 
4861                             p.getParent().getSymbol().getPieSliceSize();
4862                          Axis axis = p.getParent().onY2() ?
4863                                  p.getParent().getParent().getY2Axis() :
4864                                  p.getParent().getParent().getYAxis();
4865                          pieSlicePercentS =
4866                            axis.formatAsTickLabel(100*pieSliceSize) + "%";
4867                       }
4868                    }
4869                    result+=pieSlicePercentS;
4870                    break;
4871    
4872                 case HovertextChunk.HOVERTEXT_PARAM_USERDEFINED:
4873                    
4874                    String hoverParam = (null == hpi) ? null :
4875                               hpi.getHoverParameter(htc[i].paramName, p);
4876                    if (null == hoverParam)
4877                    // null means "unrecognized parameter" - so
4878                    // regenerate the original, unparsed, param spec
4879                    // to clue them in that it was not processed.
4880                       result += "${" + htc[i].paramName + "}"; 
4881                    else
4882                       result += hoverParam;
4883                       
4884                    break;
4885                 default:
4886                    throw new IllegalStateException(
4887                      "An illegal HOVERTEXT_PARAM_* id: " + htc[i].paramId + 
4888                      " was encountered. A GChart bug is likely to blame.");   
4889              }
4890              result+=htc[i].chunkText;
4891           }
4892           return result;
4893         }
4894       }
4895    
4896      /**
4897        ** Defines a chart curve symbol. Each point on a curve
4898        ** is represented on the chart by an appropriate
4899        ** rendering of the curve's symbol.
4900        **
4901        ** @see Curve#getSymbol Curve.getSymbol
4902        ** @see SymbolType SymbolType
4903        ** 
4904        **/
4905    
4906      public class Symbol {
4907    
4908         private Annotation annotation = null;
4909         private String backgroundColor = DEFAULT_SYMBOL_BACKGROUND_COLOR;
4910    // same as backgroundColor, but with extended RGBA collapsed to plain RGA
4911         private String backgroundColorCSS = DEFAULT_SYMBOL_BACKGROUND_COLOR;
4912         private double baseline = Double.NaN;
4913         private String borderColor = "black";
4914         private String borderColorCSS = "black";
4915         private String  borderStyle = DEFAULT_SYMBOL_BORDER_STYLE;
4916         private int  borderWidth = DEFAULT_SYMBOL_BORDER_WIDTH;
4917         private int brushHeight = DEFAULT_BRUSH_HEIGHT;
4918         private AnnotationLocation brushLocation = AnnotationLocation.CENTER;
4919         private int brushWidth = DEFAULT_BRUSH_WIDTH;
4920         private boolean fillHasHovertext = true;
4921         private double fillSpacing = Double.NaN;
4922         private int fillThickness = GChart.NAI;
4923         private int height = DEFAULT_SYMBOL_HEIGHT;
4924         private String hovertextTemplate=null;
4925         // holds specification for the hover annotation. Actual
4926         // hover annotation is generated on the fly when they hover
4927         private Annotation hoverAnnotation = null;
4928         private boolean hoverAnnotationEnabled = true;
4929         // allows hover annotation to use a different symbol type
4930         // than the symbol being hovered over. Main use expected to
4931         // be to place hover feedback at a fixed location on the chart (via
4932         // ANCHOR_* family of symbol types), for example, a status
4933         // bar message that changes depending on what the mouse
4934         // is touching.
4935         private SymbolType hoverAnnotationSymbolType = null;
4936         // encloses each symbol in a 1 px gray selection rectangle: 
4937         private String hoverSelectionBackgroundColor = "transparent";
4938         private String hoverSelectionBorderColor = "gray";
4939         private String hoverSelectionBorderStyle = "solid";
4940         private int hoverSelectionBorderWidth = -1;
4941         private boolean hoverSelectionEnabled = true;
4942         private double hoverSelectionFillSpacing = Double.NaN;
4943         private int hoverSelectionFillThickness = GChart.NAI;
4944         private int hoverSelectionHeight = GChart.NAI;
4945         private String hoverSelectionImageURL = null;
4946         private int hoverSelectionWidth = GChart.NAI;
4947         private SymbolType hoverSelectionSymbolType = null;
4948    
4949         private HovertextChunk[] hovertextChunks = null;
4950         private String imageURL = null;
4951    // XXX: Symbols are used independently of Curves by the
4952    // realizeTick method. But it's probably better to render ticks
4953    // via specialized system curves. If/when that's implemented,
4954    // Symbol should become an inner class of Curve, and this
4955    // explicit parent pointer will no longer be required.  
4956         private Curve parent = null;
4957    // when specified, model width/height are in user-defined units.
4958         private double modelHeight = Double.NaN;
4959         private double modelWidth = Double.NaN;
4960    // NaN means "begin this slice where last slice left off, or at 
4961    // initialPieSliceOrientation if it is the first slice to be rendered"
4962         private double pieSliceOrientation = Double.NaN;
4963         private double defaultPieSliceOrientation = 0.0;
4964    // slices, by default, fill the entire pie (useful for drawing disks)
4965         private double pieSliceSize = 1;
4966    
4967         private SymbolType symbolType = DEFAULT_SYMBOL_TYPE;
4968         
4969         private int width = DEFAULT_SYMBOL_WIDTH;
4970         double xScaleFactor = 1.0;
4971         double yScaleFactor = 1.0;
4972         
4973         Symbol(Curve parent) {super(); this.parent = parent;}
4974    
4975    
4976         
4977         /** Returns the CSS background color of all the rectangular
4978          ** elements used in rendering the symbol. 
4979          **
4980          ** @return the CSS background color used to fill in the
4981          **  central (non-border) part of each rectangular element
4982          **  used to render a curve's symbol.
4983          **
4984          ** @see #setBackgroundColor(String) setBackgroundColor 
4985          **/ 
4986         public String getBackgroundColor() {
4987            return backgroundColor;
4988         }
4989         String getBackgroundColorCSS() {
4990            return backgroundColorCSS;
4991         }
4992         /** Returns the baseline value for this symbol,
4993          ** previously specified via <tt>setBaseline</tt>
4994          **
4995          **
4996          ** @return the previously specified baseline value for
4997          **   this symbol.
4998          **
4999          ** @see #setBaseline setBaseline 
5000          **/ 
5001         public double getBaseline() {
5002            return baseline;
5003         }
5004        /** Returns the CSS border color of all the rectangular
5005         ** elements used in rendering the symbol. 
5006         ** 
5007         ** <p>
5008         ** @return the color of the border of the rectangular elements
5009         **   used to render the symbol, in standard CSS format
5010         ** 
5011         ** @see #setBorderColor setBorderColor
5012         ** 
5013         **/ 
5014         public String getBorderColor() { 
5015           return borderColor;
5016         }
5017         String getBorderColorCSS() {
5018            return borderColorCSS;
5019         }
5020         /**
5021         ** Returns the border style of all of the rectangular
5022         ** elements from which this symbol is built.
5023         ** <p>
5024         ** @return the CSS borderStyle of this symbol's elements
5025         **         (dotted, dashed, solid, etc. )
5026         **  
5027         ** @see #setBorderStyle setBorderStyle
5028         **/
5029           public String getBorderStyle() {
5030              return borderStyle;
5031           }
5032    
5033         /**
5034         ** Returns the width of the border around each
5035         ** rectangular element used to render this symbol,
5036         ** in pixels.
5037         ** 
5038         ** <p> 
5039         ** @return the previously set border width (in pixels).
5040         **
5041         ** @see #setBorderWidth setBorderWidth
5042          */
5043           public int getBorderWidth() {
5044              return borderWidth;
5045           }
5046            /**
5047               *
5048               * Returns the height of the rectangular "brush" that defines
5049               * how close the mouse cursor must be to a rendered symbol for
5050               * the symbol to be considered to have been "touched" (which
5051               * causes the point's hover feedback to pop up).
5052               *
5053               * @return the height of the "brush", in pixels, associated
5054               * with this symbol/curve.
5055               *
5056               * @see #setBrushHeight setBrushHeight
5057               *
5058               */
5059              public int getBrushHeight() {
5060                      return brushHeight;
5061              }
5062    
5063         /**
5064          * 
5065          * Returns the location of the rectangular brush relative to
5066          * the current x,y coordinates of the mouse cursor.
5067          * <p>
5068          *
5069          * @return the location of the rectangular brush relative to
5070          * the x,y coordinates of the mouse cursor.
5071          * 
5072          * @see #setBrushLocation setBrushLocation
5073          */
5074              public AnnotationLocation getBrushLocation() {
5075                 return brushLocation;
5076              }
5077    
5078    
5079            /**
5080               *
5081               * Returns the width of the rectangular "brush" that defines
5082               * how close the mouse cursor must be to a rendered symbol for
5083               * the symbol to be considered to have been "touched" (which
5084               * causes the point's hover feedback to pop up).  <p>
5085               *
5086               * @return the width of the "brush", in pixels, associated
5087               *   with this symbol/curve.
5088               *
5089               * @see #setBrushWidth setBrushWidth
5090               * 
5091               */
5092              public int getBrushWidth() {
5093                      return brushWidth;
5094              }
5095    
5096         /**
5097          ** @deprecated
5098          **
5099          ** Returns the value previously set by setFillHasHovertext.
5100          ** 
5101          ** @see #setFillHasHovertext setFillHasHovertext
5102          **
5103          **/
5104           public boolean getFillHasHovertext() {
5105             return fillHasHovertext;
5106           }
5107           
5108           
5109         /**
5110         ** Returns the spacing between successive rectangular
5111         ** elements used to emulate any required non-rectangular
5112         ** features of the symbol.  <p>
5113         **
5114         ** 
5115         ** 
5116         ** @return the previously set (or the default, if the
5117         ** fillSpacing has been set to <tt>Double.NaN</tt>) fill spacing
5118         ** (in pixels).
5119         **
5120         ** @see #setFillSpacing setFillSpacing
5121         ** @see #setFillThickness setFillThickness
5122         ** 
5123          */
5124           public double getFillSpacing() {
5125             if ((fillSpacing!=fillSpacing)) // x!=x is a faster isNaN
5126               return symbolType.defaultFillSpacing();
5127             else
5128               return fillSpacing;
5129           }
5130    
5131         /**
5132         ** Returns the "thickness" of rectangular elements used to
5133         ** emulate any required non-rectangular features of the symbol.
5134         ** <p>
5135         ** 
5136         ** 
5137         ** @return the previously set (or the default, if the
5138         ** fillThickness has been set to <tt>GChart.NAI</tt>) fill
5139         ** thickness (in pixels).
5140         **
5141         ** @see #setFillThickness setFillThickness
5142         ** @see #setFillSpacing setFillSpacing
5143          */
5144           public int getFillThickness() {
5145             if (fillThickness==GChart.NAI)
5146               return symbolType.defaultFillThickness();
5147             else
5148               return fillThickness;
5149           }
5150    
5151           /* Retrieves the annotation that defines the properties of
5152            * the internally generated annotations used to display
5153            * hover feedback. */
5154           Annotation getHoverAnnotation() {
5155             if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5156             return hoverAnnotation;
5157           }
5158         /**
5159          * Retrieves a boolean that indicates if point-specific
5160          * annotations popup whenever you hover over a point on the
5161          * curve associated with this symbol.<p>
5162          * 
5163          * @return true if hover-induced annotations popup, false otherwise.
5164          *
5165          * @see #setHoverAnnotationEnabled setHoverAnnotationEnabled
5166          * 
5167          */
5168         public boolean getHoverAnnotationEnabled() {
5169           return hoverAnnotationEnabled;
5170         }
5171         /**
5172          ** Retrieves the weight of the font that will be used
5173          ** with this symbol's hover annotations.
5174          ** <p>
5175          ** 
5176          ** @return the standard CSS font-weight
5177          **    specification such as normal, bold, bolder, lighter,
5178          **    100, 200, ... 900, or inherit used by hover
5179          **    annotations
5180          **
5181          ** @see #setHoverFontWeight setHoverFontWeight  
5182          **
5183          ** 
5184          **/ 
5185        public String getHoverFontWeight() {
5186          if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5187          String result = hoverAnnotation.getFontWeight();
5188          return result;
5189        }
5190         /**
5191          ** Retrieves the font color of this symbol's hover
5192          ** annotations.
5193          ** 
5194          ** @return color of the font used to display this
5195          **    symbol's hover annotations
5196          **
5197          ** @see #setHoverFontColor setHoverFontColor  
5198          **/ 
5199         public String getHoverFontColor() {
5200           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5201           String result = hoverAnnotation.getFontColor();
5202           return result;
5203         }
5204    
5205         
5206         /**
5207          ** Retrieves the CSS font-style used with this symbol's
5208          ** hover annotations.
5209          **
5210          ** @return the CSS font-style, namely,
5211          **   normal, italic, oblique, or inherit of text displayed
5212          **   in the hover annotations associated with this symbol
5213          **
5214          ** @see #setHoverFontStyle setHoverFontStyle  
5215          **/ 
5216         public String getHoverFontStyle() {
5217           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5218           String result = hoverAnnotation.getFontStyle();
5219           return result;
5220         }
5221         /**
5222          ** Retrieves the CSS font size used with this symbol's hover
5223          ** annotations, in pixels.
5224          **
5225          ** @return the font size used in the text displayed
5226          ** in the hover annotations associated with this symbol.
5227          **
5228          ** @see #setHoverFontSize setHoverFontSize
5229          ** 
5230          **/ 
5231         public int getHoverFontSize() {
5232           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5233           int result = hoverAnnotation.getFontSize();
5234           return result;
5235         }
5236    
5237        /**
5238         * Retrieves point-relative location of this symbol's hover
5239         * annotations.  <p>
5240         * 
5241         * @return the relative location of the hover annotations for
5242         * all points on the curve associated with this symbol.
5243         *
5244         * @see #setHoverLocation setHoverLocation
5245         * @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
5246         *
5247         */
5248        public AnnotationLocation getHoverLocation() {
5249           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5250           AnnotationLocation result = hoverAnnotation.getLocation();
5251           if (null == result) result = getSymbolType().defaultHoverLocation(); 
5252           return result;
5253        }
5254        /**
5255         * Retrieves the symbol type that will determine how the
5256         * hover annotations for this symbol gets positioned.
5257         * <p>
5258         *
5259         * @return <tt>SymbolType</tt> used to position hover
5260         * annotations, or <tt>null</tt> if the symbol type of the
5261         * hovered over point is being used.
5262         *
5263         * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
5264         *
5265         */
5266        public SymbolType getHoverAnnotationSymbolType() {
5267           return hoverAnnotationSymbolType;   
5268        }
5269         /**
5270          * Retrieves the background color used to indicate that the mouse is
5271          * "touching" (hovering over) a point.
5272          * 
5273          * @return a CSS color specification string that represents
5274          * the background color used to indicate "hover-selection".
5275          *
5276          * @see #setHoverSelectionBackgroundColor
5277          * setHoverSelectionBackgroundColor
5278          */
5279         public String getHoverSelectionBackgroundColor() {
5280           return hoverSelectionBackgroundColor;
5281         }
5282         /**
5283          * Retrieves the border color used to indicate that the mouse is
5284          * "touching" (hovering over) a point.
5285          * 
5286          * @return a CSS color specification string that represents
5287          * the border color used to indicate "hover-selection".
5288          *
5289          * @see #setHoverSelectionBorderColor
5290          * setHoverSelectionBorderColor
5291          */
5292         public String getHoverSelectionBorderColor() {
5293           return hoverSelectionBorderColor;
5294         }
5295         /**
5296          * Retrieves the border style used to indicate that the mouse is
5297          * "touching" (hovering over) a point.
5298          * 
5299          * @return a CSS border style specification string that represents
5300          * the border style used to indicate "hover-selection".
5301          *
5302          * @see #setHoverSelectionBorderStyle
5303          * setHoverSelectionBorderStyle
5304          */
5305         public String getHoverSelectionBorderStyle() {
5306           return hoverSelectionBorderStyle;
5307         }
5308         /**
5309          * Retrieves the width of the border around the perimeter of
5310          * rectangles used to indicate that the mouse is
5311          * "touching" (hovering over) a point.
5312          * <p>
5313          *
5314          * 
5315          * @return the width of the border drawn around the perimeter of
5316          * the selected symbol's rectangles to indicate that it has
5317          * been "touched: by the mouse.
5318          *
5319          * @see #setHoverSelectionBorderWidth
5320          * setHoverSelectionBorderWidth
5321          */
5322         public int getHoverSelectionBorderWidth() {
5323           return hoverSelectionBorderWidth;
5324         }
5325         /**
5326          * Retrieves a boolean that indicates if hover selection
5327          * feedback will be provided for this curve.  <p>
5328          * 
5329          * @return if true, hover selection feedback is enabled,
5330          *   if false, hovering over a point does not change its
5331          *   color.
5332          *
5333          * @see #setHoverSelectionEnabled setHoverSelectionEnabled
5334          * 
5335          */
5336         public boolean getHoverSelectionEnabled() {
5337           return hoverSelectionEnabled;
5338         }
5339    
5340         /**
5341          * Returns the fill spacing that will be used when
5342          * rendering this curve's hover selection feedback.
5343          * <p>
5344          *
5345          * @return fill spacing used by hover selection feedback,
5346          * or <tt>GChart.NAI</tt> if the fill spacing setting
5347          * of the hovered-over curve is to be used.
5348          * 
5349          * @see #setHoverSelectionFillSpacing
5350          *   setHoverSelectionFillSpacing
5351          * 
5352          */
5353         public double getHoverSelectionFillSpacing() {
5354           return hoverSelectionFillSpacing;
5355         }
5356         /**
5357          * Returns the fill thickness that will be used when
5358          * rendering this curve's hover selection feedback.
5359          * <p>
5360          *
5361          * @return fill thickness used by hover selection feedback,
5362          * or <tt>GChart.NAI</tt> if the fill thickness setting
5363          * of the hovered-over curve is to be used.
5364          * 
5365          * @see #setHoverSelectionFillThickness
5366          *       setHoverSelectionFillThickness
5367          * 
5368          */
5369         public int getHoverSelectionFillThickness() {
5370           return hoverSelectionFillThickness;
5371         }
5372         
5373         /**
5374          * Returns the height of the symbol used to indicate
5375          * when a given point is being "hovered over" with the
5376          * mouse.
5377          * <p>
5378          *
5379          * @return the height of the symbol used to
5380          * indicate that that a point has been selected, or
5381          * <tt>GChart.NAI</tt> if the the height of the
5382          * symbol representing the selected point is being used.
5383          * 
5384          *
5385          * @see #setHoverSelectionHeight setHoverSelectionHeight
5386          * 
5387          */
5388          public int getHoverSelectionHeight() {
5389             return hoverSelectionHeight;
5390          }
5391    
5392         /**
5393          * Returns the URL that will be used for all of the
5394          * images used in rendering this symbol's selection feedback. 
5395          * <p>
5396          *
5397          * @see #setHoverSelectionImageURL
5398          *
5399          * @return the url that defines the <tt>src</tt> property of all
5400          * images used to draw this the selection feedback associated
5401          * with this symbol.
5402          * 
5403          */ 
5404    
5405          public String getHoverSelectionImageURL() {
5406             String result = (null == hoverSelectionImageURL) ?
5407                             getBlankImageURL() :
5408                             hoverSelectionImageURL;
5409             return result;                        
5410          }
5411    
5412    
5413        /**
5414         *
5415         * Returns the symbol type that GChart will use when generating
5416         * selection feedback. GChart indicates that a point is
5417         * selected by re-rendering the point as if it had this symbol
5418         * type.
5419         * 
5420         *
5421         * @return the symbol type that in
5422         * part determines how selection feedback for a hovered over
5423         * point is drawn, or <tt>null</tt> if defaulting to the
5424         * symbol type of the hovered over point.
5425         *   
5426         * @see #setHoverSelectionSymbolType setHoverSelectionSymbolType
5427         * 
5428         */
5429         public SymbolType getHoverSelectionSymbolType() {
5430           return hoverSelectionSymbolType;
5431         }
5432         /**
5433          * Returns the width of the symbol used to indicate
5434          * when a given point is being "hovered over" with the
5435          * mouse.
5436          * <p>
5437          *
5438          * @return the width of the symbol used to indicate that
5439          * that a point has been selected, or <tt>GChart.NAI</tt> if
5440          * using to the width of the symbol representing the
5441          * selected point.
5442          * 
5443          *
5444          * @see #setHoverSelectionWidth setHoverSelectionWidth
5445          *
5446          * 
5447          */
5448          public int getHoverSelectionWidth() {
5449             return hoverSelectionWidth;
5450          }
5451         
5452      /**
5453       ** Returns the hovertextTemplate of this symbol.
5454       ** <p>
5455       **
5456       ** @return hovertextTemplate of the symbol
5457       **
5458       ** 
5459       ** @see #setHovertextTemplate(String) setHovertextTemplate
5460       **
5461       **/ 
5462           public String getHovertextTemplate() {
5463             if (null == hovertextTemplate)
5464               return symbolType.defaultHovertextTemplate();
5465             else
5466               return hovertextTemplate;
5467           }
5468    
5469         /**
5470          * When widget-based hover annotations are being used
5471          * by the curve associated with this symbol, this method returns
5472          * the <tt>HoverUpdateable</tt> widget used within
5473          * those annotations. When simple text or HTML hover
5474          * annotations are being used, it returns null.
5475          * 
5476          * @return the widget used to provide widget-based hover
5477          * annotations or null if hover annotations are not
5478          * widget-based.
5479          * 
5480          * @see #setHoverWidget setHoverWidget
5481          * 
5482          */
5483         public HoverUpdateable getHoverWidget() {
5484           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5485           return (HoverUpdateable) hoverAnnotation.getWidget();
5486         }
5487    
5488    
5489    
5490        
5491        /**
5492         * Retrieves the number of pixels (along the x-axis) that
5493         * this point's hover-annotation will be moved from its default,
5494         * <tt>setHoverLocation</tt>-defined, point-relative location.  
5495         * <p>
5496         *
5497         * @return x-shift, in pixels, of the hover annotation
5498         *  
5499         * @see #setHoverXShift getHoverXShift
5500         * 
5501         */
5502        public int getHoverXShift() {
5503           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5504           int result = hoverAnnotation.getXShift();
5505           return result;
5506        }
5507    
5508        /**
5509         * Retrieves the number of pixels (along the y-axis) that
5510         * this point's hover annotation will be moved from its default,
5511         * <tt>setHoverLocation</tt>-defined, point-relative location.  
5512         * <p>
5513         *
5514         * @return y-shift, in pixels, of the hover annotation
5515         *
5516         * @see #setHoverYShift setHoverYShift
5517         * 
5518         */
5519        public int getHoverYShift() {
5520           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5521           int result = hoverAnnotation.getYShift();
5522           return result;
5523        }
5524        
5525         /**
5526          * Returns the URL that will be used for all of the
5527          * images used in rendering this symbol. 
5528          * <p>
5529          *
5530          * @see #setImageURL setImageURL
5531          * @see #setBlankImageURL setBlankImageURL
5532          *
5533          * @return the url that defines the <tt>src</tt> property of all
5534          * images used to draw this symbol on the chart.
5535          */ 
5536         public String getImageURL() {
5537            String result = (null == imageURL) ?
5538                            getBlankImageURL() :
5539                            imageURL;
5540            return result;
5541         }
5542           // returns an internal, parsed form of the hovertext template
5543           HovertextChunk[] getHovertextChunks() {
5544              if (null == hovertextChunks)
5545                 hovertextChunks =  HovertextChunk.parseHovertextTemplate(
5546                    getHovertextTemplate());
5547    
5548              return hovertextChunks;
5549           }
5550           
5551          /** Returns the <tt>Curve</tt> that contains this
5552           ** <tt>Symbol</tt>.
5553           **
5554           ** @return a reference to the <tt>Curve</tt> that
5555           **   contains this <tt>Symbol</tt> (its "parent")
5556           **
5557           **/   
5558           public Curve getParent() {return parent;}
5559           
5560        /**
5561         ** Returns the value, previously specified via
5562         ** <tt>setPieSliceOrientation</tt>, that defines the angular
5563         ** orientation of any pie slices associated with this
5564         ** symbol.  <p>
5565         **
5566         ** 
5567         ** @return the value, either <tt>Double.NaN</tt> or a value
5568         **         between 0 and 1 previously set via
5569         **         <tt>setPieSliceOrientation</tt>, that determines the
5570         **         angular orientation of any pie slice associated
5571         **         with this symbol. 
5572         **
5573         ** @see #setPieSliceOrientation setPieSliceOrientation
5574         ** @see #setPieSliceSize setPieSliceSize
5575         ** 
5576          */
5577           public double getPieSliceOrientation() {
5578             return pieSliceOrientation;
5579           }
5580           // Used internally to translate <tt>Double.NaN</tt> into
5581           // an appropriate default slice orientation that, when pie
5582           // slice orientation isn't explicitly specified, results
5583           // in a series of adjacent slices that will form a pie
5584           // when the sum of the slice sizes equals 1.0
5585           
5586           double getDecodedPieSliceOrientation() {
5587             double result = pieSliceOrientation;
5588             if ((result!=result)) // x!=x is a faster isNaN
5589               result = defaultPieSliceOrientation;
5590             return result;
5591           }
5592    
5593           void setDefaultPieSliceOrientation(double defaultOrientation) {
5594              defaultPieSliceOrientation = defaultOrientation;
5595           }
5596           double getDefaultPieSliceOrientation() {
5597              return defaultPieSliceOrientation;
5598           }
5599           
5600        /**
5601         ** Returns the value, previously specified via
5602         ** <tt>setPieSliceSize</tt>, that defines the size of
5603         ** the angle subtended by any pie slice associated with this
5604         ** symbol.  <p>
5605         ** 
5606         ** @return the value, between 0 and 1 and previously set via
5607         **         <tt>setPieSliceSize</tt>, that defines the
5608         **         size of the "wedge of pie" as a fraction of
5609         **         the total pie, for any pie slice associated
5610         **         with this symbol.
5611         **
5612         ** @see #setPieSliceOrientation setPieSliceOrientation
5613         ** @see #setPieSliceSize setPieSliceSize
5614         ** 
5615          */
5616           public double getPieSliceSize() {
5617              return pieSliceSize;
5618           }
5619    
5620    
5621        /*
5622         * Returns the radius of the pie from which this
5623         * symbol's pie slice was extracted.
5624         *
5625         */
5626         double getPieSliceRadius(PlotPanel pp, boolean onY2) {
5627             double w = getWidth(pp);      // needed to decode model 
5628             double h = getHeight(pp,onY2);// width,height into pixels
5629             double result = Math.sqrt(w*w + h*h)/2.;
5630      // Tweak radius to assure it is an even multiple of the fill
5631      // spacing. Makes it possible to assure regular band spacing
5632      // across pie at the expense of less precise control of pie
5633      // size (regular band spacing makes it look much better). 
5634             double spacing = getFillSpacing();
5635             if (0 == spacing) spacing = 1;
5636             int nBands = (int) Math.round(result/spacing);
5637             result = nBands * spacing;
5638             return result;
5639           }
5640    
5641           // defines first, second edge angle in standard radian units
5642           double getPieSliceTheta0() {
5643             double result;
5644             result = (0.75 - getDecodedPieSliceOrientation())*2*Math.PI;
5645             return result;
5646           }
5647           double getPieSliceTheta1() {
5648             return getPieSliceTheta0() - 2.*Math.PI*getPieSliceSize(); 
5649           }
5650    
5651        /**
5652         ** Returns this symbol's height, as previously set by
5653         ** <tt>setHeight</tt>.
5654         ** 
5655         ** @return the previously set symbol height, in pixels.
5656         **
5657         ** @see #setHeight setHeight
5658          */
5659           public int getHeight() {
5660              return height;
5661           }
5662      /**
5663       ** Returns this symbol's height as previously set by
5664       ** <tt>setModelHeight</tt>.
5665       ** 
5666       ** @return the previously set symbol height, in model units
5667       **
5668       ** @see #setModelHeight setModelHeight
5669       ** @see #setModelWidth setWidth
5670       ** @see #setHeight setHeight
5671       ** @see #setWidth setWidth
5672       ** 
5673        */
5674         public double getModelHeight() {
5675            return modelHeight;
5676         }
5677    
5678         
5679         /**
5680         ** Returns this symbol's width as previously set by
5681         ** <tt>setModelWidth</tt>.
5682         ** 
5683         ** @return the previously set symbol width, in model units.
5684         **
5685         ** @see #setModelWidth setModelWidth
5686         ** @see #setModelHeight setModelHeight
5687         ** @see #setWidth setWidth
5688         ** @see #setHeight setHeight
5689         ** 
5690          */
5691           public double getModelWidth() {
5692              return modelWidth;
5693           }
5694      /** Returns this symbol's type. 
5695        **
5696        ** @return the type of this symbol.
5697        ** @see #setSymbolType setSymbolType
5698        **
5699        **/ 
5700       public SymbolType getSymbolType() {
5701          return symbolType;
5702       }
5703      /**
5704       ** Returns this symbol's width
5705       ** as previously set by <tt>setWidth</tt>.
5706       ** <p>
5707       ** 
5708       ** <i>Warning:</i> This method won't return the correct
5709       ** pixel width associated with a <tt>setModelWidth</tt>
5710       ** setting, as you might have expected.  It only returns
5711       ** the pixel width you last explicitly specified via
5712       ** <tt>setWidth</tt>.
5713       ** 
5714       ** <p>
5715       ** 
5716       ** @return the previously set symbol width, in pixels
5717       **
5718       ** @see #setWidth setWidth
5719       ** @see #setModelWidth setModelWidth
5720        */
5721         public int getWidth() {
5722            return width;
5723         }
5724    
5725         /*
5726          * Do points on the curve associated with this symbol
5727          * use a horizontal (or vertical) binning strategy for
5728          * "what point is the mouse over" hit testing?
5729          *
5730          */
5731         boolean isHorizontallyBanded() {
5732            boolean result;
5733            if (null == symbolType.isHorizontallyBanded)
5734               // not fixed by symbol type: use brush shape determined banding
5735               // (we are guessing point distribution based on brush shape)
5736               result = brushHeight < brushWidth;           
5737            else 
5738               result = symbolType.isHorizontallyBanded.booleanValue();
5739    
5740            return result;
5741         }
5742    
5743         
5744         /*
5745          * If passed an rgba-like string (rgba(255,255,128,0.5))
5746          * returns the collapsed-to-rgb version (rgb(255,255,128)).
5747          * Else returns the original string. Throws an exception
5748          * if string begins with rgba( but lacks required
5749          * format after that.
5750          * 
5751          */ 
5752         private String collapseRGBAToRGB(String rgba) {
5753    // an int in the range 0..255 for the "R,G,B" parts
5754            final String RGB =
5755               "([0-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9])|(25[0-5]))";
5756    // a double in the range 0..1 for the "A" part
5757            final String A = "(0|1|(1[.]0*)|(0[.][0-9]*)|([.][0-9]+))";
5758    // full RGBA pattern
5759            final String RGBA_PATTERN = 
5760               "rgba[(]" + RGB + "[,]" + RGB + "[,]" + RGB + "[,]" + A + "[)]";
5761            String result = rgba;
5762            if (null != rgba && rgba.startsWith("rgba(")) {
5763               if (rgba.matches(RGBA_PATTERN)) {
5764                 final int FIRST_PAREN = 4;
5765                 int lastComma = rgba.lastIndexOf(",");
5766                 result = "rgb" +
5767                          rgba.substring(FIRST_PAREN, lastComma) + ")";
5768               }
5769               else
5770                  throw new IllegalArgumentException(
5771    "Your RGBA color specification: '" + rgba + "'" +
5772    " was not in the GChart-required form: rgba(Red,Green,Blue,Alpha)" +
5773    " where Red, Green and Blue are integers in the range 0 to 255 and" +
5774    " Alpha is a double in the range 0.0 to 1.0");
5775            }
5776            // else special keyword or else some (unchecked) CSS color format
5777            return result;
5778         }
5779         
5780      /**
5781       ** Specifies the background or fill color of this symbol.
5782       ** <p>
5783       ** 
5784       ** For example, this would define the color of the interior,
5785       ** non-border, part of bars in a bar-chart, the color of the
5786       ** interior of each shading bar in a banded-fill pie slice, or
5787       ** the canvas "fill" color of solid fill, canvas-rendered, pie
5788       ** slices. <p>
5789       ** 
5790       ** You can use one of the 16 standard HTML/CSS color literals,
5791       ** illustrated below, to quickly specify common colors: 
5792       **
5793       ** <p><ul>
5794       ** <img
5795       ** src="{@docRoot}/com/googlecode/gchart/client/doc-files/gchartexample10.png">
5796       ** </ul>
5797       ** 
5798       ** <p> For more variety, use a standard CSS RGB (<b>r</b>ed,
5799       ** <b>g</b>reen, and <b>b</b>lue) color format such as "#FF0000"
5800       ** (same as "red"), "#00FF00" (same as "green"), "#0000FF" (same as
5801       ** "blue"), "#FFFFFF" (same as "white") or "#000000" (same as
5802       ** "black").  <p>
5803       **
5804       ** If you are using an external canvas library that supports
5805       ** them, feel free to use RGBA color specifications
5806       ** (e.g, <tt>rgba(255,255,255,0.5))</tt> for semi-transparent
5807       ** white).  GChart will automatically collapse these
5808       ** specifications down to CSS standard RGB (e.g.
5809       ** <tt>rgb(255,255,255)</tt>) as needed for any
5810       ** non-canvas-rendered parts of the curve.
5811       ** <p>
5812       ** 
5813       ** <small> To maintain <tt>GWTCanvas</tt> consistency, GChart requires that
5814       ** you use integers in the range <tt>0..255</tt> in the first three
5815       ** comma delimited RGBA slots, and a double in the range
5816       ** <tt>0..1</tt> in the fourth. An runtime exception will be raised
5817       ** if you use any other format. <p>
5818       ** </small>
5819       ** 
5820       ** <i>Why GChart sometimes deliberately drops your
5821       ** alpha-transparency specs (and how to workaround this limitation):</i>
5822       ** 
5823       ** <p>
5824       ** <blockquote>
5825       ** <small>
5826       ** <p>
5827       ** 
5828       ** The external <tt>GWTCanvas</tt> vector graphics library lets you
5829       ** use the RGBA format, which adds a fourth,
5830       ** <tt><b>a</b>lpha</tt> parameter in addition to the standard
5831       ** <tt><b>r</b>ed</tt>, <tt><b>g</b>reen</tt> and
5832       ** <tt><b>b</b>lue</tt>. This <tt>alpha</tt> lets you define the
5833       ** degree of transparency: from 0 (transparent) to 1 (opaque).<p>
5834       ** 
5835       ** Problem is, if you use this <tt>rgba(r,g,b,a)</tt> syntax to specify, say,
5836       ** the color of an HTML element's border, IE won't display anything
5837       ** but an error.  So, whenever GChart uses an HTML element to render
5838       ** a symbol's background or border (instead of your external vector
5839       ** graphics library) it collapses the RGBA into the more
5840       ** widely accepted RGB format, by dropping the fourth,
5841       ** <tt>alpha</tt>, parameter and changing the leading <tt>rgba</tt>
5842       ** identifier to <tt>rgb</tt>.<p>
5843       ** 
5844       ** GChart will render any "continuously filled" (you can use
5845       ** <tt>setFillSpacing(0)</tt> to request continuous filling)
5846       ** aspects of your symbols using whatever external vector
5847       ** graphics library you specified via
5848       ** <tt>setCanvasFactory</tt>. So, you can rely on the full
5849       ** RGBA syntax working for these aspects of your curve,
5850       ** provided that, like <tt>GWTCanvas</tt>, your external graphics
5851       ** library supports the <tt>rgba(r,g,b,a)</tt> syntax.
5852       ** Specifically, solid fill pie slices and their borders,
5853       ** solid fill area charts and their borders, and solid
5854       ** point-to-point connecting lines on a line chart all support
5855       ** RGBA-specified alpha-transparency.  <p>
5856       ** 
5857       ** On the other hand, due to a limitation of GChart's
5858       ** implementation, any rectangular aspects of your symbols,
5859       ** namely bar chart symbols and their borders, rectangular
5860       ** point markers and their borders, and banded-filled pie
5861       ** slices, are rendered via HTML and will collapse your
5862       ** <tt>rgba(r,g,b,a)</tt> specs into <tt>rgb(r,g,b)</tt>.<p>
5863       ** 
5864       ** But what if you need a semi-transparent bar chart,
5865       ** banded-fill pie slice, or rectangular point marker?
5866       ** Fortunately, there is a simple workaround: just pass the
5867       ** URL of an appropriately-sized semi-transparent image to the
5868       ** <tt>setImageURL</tt> method of the curve in question.
5869       ** 
5870       ** </small>
5871       ** </blockquote>
5872       ** <p>
5873       **  
5874       ** <p>
5875       ** The default symbol background color is
5876       ** <tt>DEFAULT_SYMBOL_BACKGROUND_COLOR</tt>
5877       **
5878       ** 
5879       ** @param backgroundColor a standard CSS or canvas-library
5880       ** supported RGBA background color specification string.
5881       ** 
5882       **
5883       ** @see #getBackgroundColor getBackgroundColor
5884       ** @see #setBorderColor setBorderColor
5885       ** @see #DEFAULT_SYMBOL_BACKGROUND_COLOR DEFAULT_SYMBOL_BACKGROUND_COLOR
5886       ** @see #setImageURL setImageURL
5887       ** 
5888       **/
5889         public void setBackgroundColor(String backgroundColor) {
5890            getParent().invalidate();
5891            this.backgroundColor = backgroundColor;
5892    // don't want to keep collapsing whenever we render, so save it:
5893            backgroundColorCSS = collapseRGBAToRGB(backgroundColor);
5894         }
5895    
5896         /** Specifies the baseline value for this symbol. Use a
5897          ** baseline value when you need to create bar charts whose
5898          ** bars extend up/down to a specified y baseline value (for
5899          ** vertical bar charts) or left/right to a specified x baseline
5900          ** value (for horizontal bar charts).
5901          ** 
5902          ** <p>
5903          **
5904          ** In greater detail:
5905          ** <p>
5906          ** <ul>
5907          ** 
5908          ** <li>For curves that employ symbol types with names of
5909          ** the form <tt>VBAR_BASELINE_*</tt>, a vertical bar is
5910          ** drawn that connects the x,y position of each data point
5911          ** to the horizontal line defined by the equation
5912          ** <tt>y=baseline</tt>.  For the default baseline setting
5913          ** of <tt>Double.NaN</tt>, the defining equation is
5914          ** <tt>y=(yMin+yMax)/2</tt> (i.e., a midpoint baseline).
5915          ** 
5916          ** <p> <li>For curves that employ symbol types with names
5917          ** of the form <tt>HBAR_BASELINE_*</tt>, a horizontal bar
5918          ** is drawn from the x,y position associated with each data
5919          ** point to the vertical line defined by the equation
5920          ** <tt>x=baseline</tt>. For the default baseline setting of
5921          ** <tt>Double.NaN</tt>, the defining equation is
5922          ** <tt>x=(xMin+xMax)/2</tt>.
5923          ** </ul>
5924          **
5925          ** <p>
5926          ** 
5927          ** @param baseline the y (or x) that defines the horizontal
5928          **   (or vertical) line to which any baseline-based vertical
5929          **   (or horizontal) bars are extended.
5930          **   
5931          ** @see #getBaseline getBaseline
5932          ** @see SymbolType#HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
5933          ** @see SymbolType#HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
5934          ** @see SymbolType#HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
5935          ** @see SymbolType#VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
5936          ** @see SymbolType#VBAR_BASELINE_EAST VBAR_BASELINE_EAST
5937          ** @see SymbolType#VBAR_BASELINE_WEST VBAR_BASELINE_WEST
5938          ** 
5939          **/ 
5940         public void setBaseline(double baseline) {
5941            getParent().invalidate();
5942            this.baseline = baseline;
5943         }
5944    
5945         
5946      /**
5947       ** Specifies the border color, as a CSS or RGBA color
5948       ** specification string.
5949       ** 
5950       ** <p>
5951       ** 
5952       ** Both standard CSS and the all-but-IE-supported
5953       ** RGBA extension (with the fourth paramter defining
5954       ** alpha-transparency, for example,
5955       ** <tt>rgba(255,255,255,0.5)</tt> to define a semi-transparent
5956       ** white) are allowed. However, GChart may collapse
5957       ** these extended specifications into ordinary RGB in
5958       ** some cases. See {@link Symbol#setBackgroundColor
5959       ** setBackgroundColor} for full details.
5960       **
5961       ** <p>
5962       **
5963       ** For example, for a square symbol, this would set the color
5964       ** of the line that indicates the outter perimeter of that
5965       ** square.  For a banded-fill pie slice, this would set the
5966       ** color of the outter perimeter of every shading bar used to
5967       ** fill in the pie slice. For a solid fill pie slice, with an
5968       ** external canvas library such as <tt>GWTCanvas</tt> plugged
5969       ** into GChart via <tt>setCanvasFactory</tt>, this method
5970       ** would instead set the color of the canvas-stroked line
5971       ** around the slice's perimeter.
5972       ** 
5973       ** <p>
5974       **
5975       ** This color also defines the color of the point-to-point
5976       ** connecting lines of a continuously connected line chart.
5977       ** <p>
5978       **
5979       **
5980       ** <i>Using <tt>TRANSPARENT_BORDER_COLOR</tt>:</i>
5981       ** <p>
5982       ** 
5983       ** <blockquote>
5984       ** <small>
5985       ** <p>Although you can use the special CSS keyword
5986       ** "transparent", due to a well-known bug, IE6 will usually render
5987       ** that as black. Plus, different browsers define the background
5988       ** color that shines through the transparent border
5989       ** differently, and external canvas libraries such as
5990       ** <tt>GWTCanvas</tt> may not support the use of "transparent".  So,
5991       ** GChart provides a browser-independent keyword
5992       ** <tt>TRANSPARENT_BORDER_COLOR</tt> that you can use instead, that
5993       ** emulates transparency by removing the border entirely, and then
5994       ** shifting and resizing the transparently bordered element so as to
5995       ** create the illusion that its transparent border is still there.
5996       ** That's not exactly the same thing as having a transparent border
5997       ** (the emulated borders can't "see" mouse moves over them, for
5998       ** example) but it's close enough for most charting purposes. Another
5999       ** simple way to workaround these transparent border inconsistencies is
6000       ** to use an explicitly specified border color equal to the chart's
6001       ** background color.
6002       ** </small>
6003       ** </blockquote>
6004       **
6005       **
6006       ** @param borderColor the color of the borders of this curve's rendered
6007       ** symbols, and of any point-to-point connecting lines. Use any
6008       ** valid CSS color specification string (including the
6009       ** RGBA extension), or the special
6010       ** GChart keyword <tt>TRANSPARENT_BORDER_COLOR</tt>.
6011       **
6012       ** For more information on standard CSS color specifications
6013       ** including
6014       ** how GChart handles the RGBA extended format, see
6015       ** {@link Symbol#setBackgroundColor setBackgroundColor}.
6016       **
6017       ** @see #TRANSPARENT_BORDER_COLOR TRANSPARENT_BORDER_COLOR
6018       ** @see #getBorderColor getBorderColor
6019       ** @see #setBackgroundColor setBackgroundColor
6020       ** @see #setCanvasFactory setCanvasFactory
6021       ** 
6022       **/
6023         public void setBorderColor(String borderColor) {
6024            getParent().invalidate();
6025            this.borderColor = borderColor;
6026            borderColorCSS = collapseRGBAToRGB(borderColor);
6027         }
6028         
6029      /**
6030       ** Sets the border style of the rectangular elements used
6031       ** to render this symbol.
6032       **
6033       ** <p>
6034       ** 
6035       ** <p>
6036       ** @param borderStyle a CSS border style such as
6037       ** "solid", "dotted", "dashed", etc. 
6038       **
6039       ** @see #getBorderStyle getBorderStyle
6040       ** @see #setBackgroundColor setBackgroundColor
6041       ** @see #setBorderColor setBorderColor
6042       **/
6043         public void setBorderStyle(String borderStyle) {
6044           getParent().invalidate();
6045           this.borderStyle = borderStyle;
6046         }
6047      /**
6048       ** Sets the width of the border around the graphical
6049       ** element(s) used to render this curve, in pixels.
6050       ** <p>
6051       ** 
6052       ** If positive, the border is drawn inside each graphical
6053       ** element. If negative, the border is drawn outside of
6054       ** those elements. Note that, negative, external, borders do not
6055       ** increase the mouse hit-testing region associated with
6056       ** the rendered symbols.
6057       ** <p>
6058       **
6059       ** <blockquote><small> <i>Tip:</i> To get the hit test region
6060       ** around a symbol to, <i>in effect</i>, include the external border
6061       ** associated with a negative border width, simply increase the width
6062       ** and the height of the symbol's point selection brush by
6063       ** <tt>2*Math.abs(getBorderWidth())</tt>.  </small> </blockquote>
6064       ** 
6065       ** 
6066       ** <p> If a rectangular symbol's width or height ever become
6067       ** less than twice the specified positive border width, the
6068       ** border will be shrunk down until it just fills up the
6069       ** entire rectangular area of the symbol.
6070       ** <p>
6071       **
6072       ** On backgrounds vs borders in bar charts when printing
6073       ** <p>
6074       ** 
6075       ** <blockquote><small> Because borders are usually printed more reliably
6076       ** than background colors cross-browser, using such oversized
6077       ** borders is a simple way to assure that on-screen and
6078       ** printed bar chart renderings are consistent. Consistency
6079       ** isn't everything: the removal of background colors can save
6080       ** ink. So, you might prefer to just design bar charts that
6081       ** look good with either solid-filled (on-screen) or outlined
6082       ** (printed w.  'print background colors' unchecked in FF, for
6083       ** example) bars.  </small></blockquote>
6084       ** 
6085       ** @param borderWidth the width of the symbol's border, in pixels
6086       ** @see #getBorderWidth getBorderWidth
6087       **/
6088         public void setBorderWidth(int borderWidth) {
6089            getParent().invalidate();
6090            this.borderWidth = borderWidth;
6091         }
6092    
6093            /**
6094               *
6095               * Sets the height of the rectangular point-selection
6096               * "brush". This brush defines how close the mouse
6097               * must get to a point on the chart in order to "touch" it. 
6098               * <p>
6099               * 
6100               * Whenever a point is touched, GChart displays that point's
6101               * hover feedback. By default, the selected point is
6102               * highlighted, the associated symbol's hovertext template
6103               * is expanded relative to the touched point, and the
6104               * resulting plain text or HTML is placed onto the chart at
6105               * an appropriate, point-relative, location. You can
6106               * override this default by passing a
6107               * <tt>HoverUpdateable</tt> <tt>Widget</tt> to the the
6108               * <tt>setHoverWidget</tt> method. In that case, instead of
6109               * expanding your hovertext template, the
6110               * <tt>HoverUpdateable</tt>
6111               * <tt>Widget</tt> is instead positioned properly relative to the
6112               * touched point, and its <tt>hoverUpdate</tt> method is
6113               * invoked with a reference to the touched point passed in
6114               * as its first argument.  <p>
6115               *
6116               * The rules for determining if the brush is "touching" a symbol
6117               * are as follows:
6118               *
6119               * <ol>
6120               *
6121               *   <li>For all symbols <i>except</i> pie slices, the symbol
6122               *   is considered to have been touched if the rectangle
6123               *   containing the rendered symbol, and a rectangle with
6124               *   the specified brush width and height, and centered
6125               *   on
6126               *   the current mouse position, intersect.
6127               *   <p>
6128               *
6129               *   
6130               *   <small><i>Note:</i> The brush is centered on the mouse by
6131               *   default. You can place the brush above, below, etc.
6132               *   the mouse position via the
6133               *   <tt>setBrushLocation</tt> method.
6134               *   </small>
6135               *   <p>
6136               *   
6137               *   For example, with a 10 x 10 pixel brush, a bar
6138               *   chart's bar gets "touched" whenever the mouse is either
6139               *   within, or within a 5 pixel border around, the bar.  Note
6140               *   that on line charts, only the individual (rectangular)
6141               *   point markers, not connecting lines between them, are
6142               *   touchable.  <p>
6143               *
6144               *   <li>For pie slices, the slice is considered to have been
6145               *   touched if the mouse cursor is within the angle subtended
6146               *   by the slice, and within <tt>sliceRadius + max(brushWidth,
6147               *   brushHeight)/2.0</tt> of the center of the pie containing
6148               *   the slice. Intuitively, the brush in effect aligns its
6149               *   longest dimension, compass-like, so that it points to the
6150               *   center of the pie containing the slice. 
6151               *   <p>
6152               *   <small><i>Note:</i> Unlike other symbol types, pie
6153               *   slice hit testing works the same regardless
6154               *   of the brush location (c.f. <tt>setBrushLocation</tt>)
6155               *   setting.  </small>
6156               *   
6157               *  </ol>
6158               *
6159               * <p>
6160               * Even though each curve's symbol can have an
6161               * independently sized brush--which gives you a lot of
6162               * control over which curve's points are easiest to
6163               * select--using the same sized brush for all curves
6164               * produces results more consistent with the simple concept
6165               * of a single physical brush "touching" symbols on the
6166               * chart. 
6167               *
6168               * <p>
6169               * 
6170               * Finally, if the brush touches more than one symbol, the
6171               * symbol whose center is closest to the mouse cursor (by
6172               * default, that's also the brush center point) is
6173               * considered to have been touched.  (In these calculations,
6174               * the ordinary, Euclidean, definition of distance is used
6175               * by default; the <tt>setDistanceMetric</tt> method allows
6176               * for other definitions). For pie slices, the symbol's
6177               * center point is defined (to simplify the calculations) as
6178               * the point at the center of the pie that contains the
6179               * slice. In case of any remaining ties, the point later on
6180               * the point list (the one "on top") is selected.  <p>
6181               *
6182               * 
6183               *
6184               * <small>
6185               * <i>Fine-print:</i> Known brush size limitations when
6186          * selecting off-chart points: 
6187               *
6188               * <blockquote>
6189               *   GChart allows you to render points that fall outside
6190               *   of the GChart's containing rectangle.
6191               *   <p>
6192               *   
6193               *   But, GChart can't see mouse moves around such off-chart
6194               *   elements--the mouse has to be right on top of these
6195               *   elements before it's motion is detected by GChart (the
6196               *   browser sends mouse moves in these regions to whatever
6197               *   non-GChart related browser elements occupy this space).
6198               *   
6199               *   <p> Consequently, even though GChart still uses your
6200               *   brush settings for hit testing with such points,
6201               *   because it can't see mouse activity until the center of
6202               *   the mouse "touches" such elements, it will often act
6203               *   <i>as if</i> you were using a <tt>1x1</tt> pixel brush for such
6204               *   externally rendered points.  <p>
6205               *
6206               *   Unfortunately, this is a basic limitation of how GChart
6207               *   handles mouse events. But there are a number of
6208               *   possible workarounds, such as adding a transparent
6209               *   curve with similar, but larger,
6210               *   dimensions/point locations to the off-chart curve's
6211               *   points (so as to capture external mouse moves around
6212               *   external points) or adding a transparent
6213               *   annotation around external points (again, to create a
6214               *   mouse capture region around these points). Perhaps the
6215               *   easiest approach is to simply make the outter
6216               *   decoration regions around the chart big enough (via
6217               *   methods such as <tt>setAxisLabelThickness</tt> and
6218               *   similarly named <tt>set*Thickness</tt> methods) so that
6219               *   there is enough empty space around the plot area's
6220               *   perimeter so that points never fall outside the chart's
6221               *   bounding rectangle.
6222               *   <p>
6223               *
6224               *   Because most applications place their points on the
6225               *   chart, this is only an issue for advanced applications
6226               *   that deliberately draw outside of the chart's rectangle so
6227               *   as to overlay the chart onto the host page. 
6228               *
6229               * 
6230               * </blockquote></small>
6231               * 
6232               * 
6233               *
6234               * @param height the height of the rectangular point
6235               * selection brush used by points on the curve associated
6236               * with this symbol (in pixels).
6237               *
6238               * @see #getBrushHeight getBrushHeight
6239               * @see #setBrushWidth setBrushWidth
6240               * @see #setBrushSize setBrushSize
6241               * @see #setBrushLocation setBrushLocation
6242               * @see #setDistanceMetric setDistanceMetric
6243               * @see Symbol#setHoverWidget setHoverWidget
6244               * @see HoverUpdateable HoverUpdateable
6245               * @see #DEFAULT_BRUSH_WIDTH DEFAULT_BRUSH_WIDTH
6246               * @see #DEFAULT_BRUSH_HEIGHT DEFAULT_BRUSH_HEIGHT
6247               * @see #getTouchedPoint getTouchedPoint
6248               * @see #touch touch
6249               * @see GChart#setHoverTouchingEnabled setHoverTouchingEnabled
6250               * 
6251               */
6252              public void setBrushHeight(int height) {
6253                 brushHeight = height;
6254         }
6255    
6256         /**
6257          * Sets the location of the brush relative to the mouse
6258          * x,y coordinates.
6259          * <p>
6260          *
6261          * With the default setting of
6262          * <tt>AnnotationLocation.CENTER</tt>, the brush is centered
6263          * on the mouse cursor, which is usually acceptable.
6264          * <p>
6265          *
6266          * The most useful non-default settings, which facilitate
6267          * "single sided" point selection, are tabulated below:
6268          *
6269          * <table border>
6270          *  <tr><th>Location</th><th>Impact on point
6271          *  selection</th></tr>
6272          *  <tr><td><tt>AnnotationLocation.NORTH</tt></td> <td>The
6273          *  mouse
6274          *  only selects points when it is on or below them.</td></tr>
6275          *  <tr><td><tt>AnnotationLocation.SOUTH</tt></td> <td>The
6276          *  mouse
6277          *  only selects points when it is on or above them.</td></tr>
6278          *  <tr><td><tt>AnnotationLocation.WEST</tt></td> <td>The
6279          *  mouse
6280          *  only selects points when it is on them or to their right.</td></tr>
6281          *  <tr><td><tt>AnnotationLocation.EAST</tt></td> <td>The
6282          *  mouse
6283          *  only selects points when it is on them or to their left.</td></tr>
6284          *
6285          * </table>
6286          * <p>
6287          * 
6288          * <i>Tip:</i> When a chart has two curves, setting one
6289          * curve to use <tt>NORTH</tt> as its brush location, and
6290          * the other to use <tt>SOUTH</tt>, and using a brush height
6291          * equal to the height of the decorated chart
6292          * (returned via <tt>getYChartSizeDecorated()</tt>) allows points
6293          * on one curve to be selected when the user is near the top
6294          * of the chart, and points on the other curve to be
6295          * selected when the user is near the bottom of the chart.
6296          * <p>
6297          *   
6298          * @see #setBrushHeight setBrushHeight
6299          * @see #setBrushWidth setBrushWidth
6300          * @see GChart#getYChartSizeDecorated getYChartSizeDecorated
6301          *
6302          * @param location the location of the rectangular brush,
6303          * relative to the x,y position of the mouse.
6304          *
6305          */
6306              public void setBrushLocation(AnnotationLocation location) {
6307                 brushLocation = location;
6308              }
6309    
6310    
6311    
6312            /**
6313               *
6314               * Convenience method equivalent to:
6315               * <p>
6316               * 
6317               * <pre>
6318               *   setBrushWidth(width);
6319               *   setBrushHeight(height);
6320               * </pre>
6321               * 
6322               * <p>
6323               * For a full discussion of how GChart uses it's "brush" to
6324               * determine when hover feedback for a point gets displayed,
6325               * see <tt>setBrushHeight</tt>.
6326               * <p>
6327               * 
6328               * @param width the width of this chart's brush, in pixels
6329               * @param height the height of this chart's brush, in pixels
6330               * 
6331               * 
6332               * @see #setBrushHeight setBrushHeight
6333               * @see #setBrushWidth setBrushWidth
6334               * @see #DEFAULT_BRUSH_WIDTH DEFAULT_BRUSH_WIDTH
6335               * @see #DEFAULT_BRUSH_HEIGHT DEFAULT_BRUSH_HEIGHT
6336               *
6337               * <p>
6338               *
6339               * 
6340               */
6341              public void setBrushSize(int width, int height) {
6342                 setBrushWidth(width);
6343                 setBrushHeight(height);
6344              }
6345    
6346    
6347    
6348            /**
6349               *
6350               * Sets the width of the rectangular "brush" that defines how
6351               * close the mouse position must be to a rendered symbol for
6352               * that symbol to have been "touched".
6353               * <p>
6354               *
6355               * For a full discussion of how GChart uses it's "brush" to
6356               * determine when hover feedback for a point gets displayed,
6357               * see <tt>setBrushHeight</tt>.
6358               *
6359               * @param width width of the point selection brush, in pixels.
6360               * 
6361               * @see #setBrushHeight setBrushHeight
6362               * @see #setBrushSize setBrushSize
6363               * @see #DEFAULT_BRUSH_WIDTH DEFAULT_BRUSH_WIDTH
6364               * @see #DEFAULT_BRUSH_HEIGHT DEFAULT_BRUSH_HEIGHT
6365               * 
6366               *
6367               */ 
6368              public void setBrushWidth(int width) {
6369                 brushWidth = width;
6370              }
6371    
6372    
6373         /**
6374          *
6375          * Allows you to change the x,y scale factors that define
6376          * the distance between the mouse cursor and each
6377          * rendered point; these distances determine which point is
6378          * "closest" to the mouse during hit testing.  <p>
6379          *
6380          * Whenever the mouse selection brush "touches" more than
6381          * one point, the point whose center is closest to the mouse
6382          * cursor is the one selected. For a point centered at (all
6383          * coordinates are in pixels) <tt>(xCenter, yCenter)</tt>
6384          * the distance to the mouse cursor at <tt>(xMouse,
6385          * yMouse)</tt> is given by: <p>
6386          * 
6387          * <pre>
6388          *   dx = xScaleFactor*(xCenter-xMouse);
6389          *   dy = yScaleFactor*(yCenter-yMouse);
6390          *   distance = Math.sqrt(dx*dx + dy*dy); 
6391          * </pre>
6392          * <p>
6393          *
6394          * In the above, <tt>(xCenter, yCenter)</tt> is the position
6395          * at the center of the rectangle associated with the
6396          * rendered point. For pie slices, it is the position at the
6397          * center of the pie containing the slice.
6398          * 
6399          * <p>
6400          * 
6401          *
6402          * <i>Tip:</i> Here are the most commonly used x-y scale factors,
6403          * and how they are typically used:
6404          * <p>
6405          * <ol>
6406          * 
6407          *   <li>To select points based on the ordinary (Euclidean)
6408          *   distance use <tt>xScaleFactor = 1, yScaleFactor =
6409          *   1</tt> (this is the default).  <p>
6410          *   
6411          *   <li>To select points based only on how close the mouse
6412          *   x-coordinate is to the x-coordinate at the point's
6413          *   center (often a good choice for a time series chart) use
6414          *   <tt>xScaleFactor=1, yScaleFactor=0</tt>
6415          *   <p>
6416          *
6417          *   <li>To select points based only on how close the mouse
6418          *   y-coordinate is to the y-coordinate at the point's
6419          *   center (a good choice for a horizontally oriented bar
6420          *   chart) use <tt>xScaleFactor=0, yScaleFactor=1</tt>
6421          *   <p>
6422          *   
6423          *   <li>To ignore the distance to the point, and instead
6424          *   always select, from among those points touching the
6425          *   brush, the point rendered last, use <tt>xScaleFactor=0,
6426          *   yScaleFactor=0</tt>. This choice guarantees that points
6427          *   that are completely occluded by other points can never get
6428          *   selected.  But, because distances are ignored, this choice
6429          *   can lead to points further from the mouse being
6430          *   selected in preference to points closer to the mouse.
6431          *   You can minimize this effect by using a very small
6432          *   brush size.
6433          *   
6434          * </ol>
6435          * <p>
6436          *
6437          * <i>Warning:</i> Mixed metrics, like mixed metaphors, can
6438          * be confusing:
6439          * <blockquote>
6440          *
6441          * Since each curve gets it's own distance
6442          * metric, it's possible to preferentially select one curve
6443          * over another by giving it relatively smaller scale
6444          * factors.  This can produce very counter-intuitive
6445          * selection behaviors (selecting a point that is farther
6446          * away from the mouse than another point, for example).
6447          * Generally, <i><b>it's best to use the same distance metric
6448          * for all curves</i></b> except in special cases.
6449          * <p>
6450          *
6451          * For example, suppose you had a vertical bar chart on
6452          * curve 0 that was restricted to the top half of your
6453          * chart, and a horizontal bar chart on curve 1 restricted
6454          * to the bottom half. Then you might use
6455          * <tt>xScaleFactor=1, yScaleFactor=0</tt> for curve 0 and
6456          * <tt>xScaleFactor=0, yScaleFactor=1</tt> for curve 1. This
6457          * "mixed metric" would not create confusion, because the
6458          * user would view the region-specific selection behaviour
6459          * as sensibly coordinated with the orientation of the bars
6460          * in each region.  <p>
6461          *
6462          * </blockquote>
6463          *
6464          * Though using a relatively larger <tt>xScaleFactor</tt>,
6465          * <tt>yScaleFactor</tt> makes a curve's points relatively
6466          * harder to select during hit testing, to completely ignore
6467          * a curve's points during hit testing, you need to use
6468          * <tt>setHoverSelectionEnabled(false)</tt> and
6469          * <tt>setHoverAnnotationEnabled(false)</tt>. 
6470          * 
6471          *
6472          * @param xScaleFactor multiplies the x-pixel distance
6473          * between the mouse cursor and the point center (see
6474          * distance formula above). 
6475          * @param yScaleFactor multiplies the y-pixel distance
6476          * between the mouse cursor and the point center (see
6477          * distance formula above). 
6478          *
6479          * @see #setBrushSize setBrushSize
6480          * @see #setBrushLocation setBrushLocation
6481          * @see #setHoverSelectionEnabled setHoverSelectionEnabled
6482          * @see #setHoverAnnotationEnabled setHoverAnnotationEnabled
6483          *
6484          */
6485         public void setDistanceMetric(double xScaleFactor,
6486                                        double yScaleFactor) {
6487            this.xScaleFactor = xScaleFactor;
6488            this.yScaleFactor = yScaleFactor;
6489         }
6490    
6491         /**
6492          ** @deprecated
6493          **
6494          ** As of GChart 2.4, hover feedback has been completely
6495          ** redesigned. Though these changes are mostly positive,
6496          ** one downside is that, to simplify its hit-testing
6497          ** algorithms, GChart only provides hover feedback for the
6498          ** explicitly specified data points on a line chart; it can
6499          ** no longer provide feedback for the "filled in" points
6500          ** connecting successive data points.  If you need hover
6501          ** feedback on such interpolated points you will have to
6502          ** explicitly add individual data points to the curve
6503          ** representing the interpolated values.  <p>
6504          ** 
6505          ** Another difference is that you can no longer turn off
6506          ** hover feedback for a pie slice via this method. If you
6507          ** need to turn hover feedback off for a pie slice (or for
6508          ** any other symbol, for that matter) you can use the
6509          ** (new with 2.4) <tt>setHoverAnnotationEnabled</tt> and
6510          ** <tt>setHoverSelectionEnabled</tt> methods.
6511          ** 
6512          **
6513          ** @see #getFillHasHovertext getFillHasHovertext
6514          ** @see #setHovertextTemplate setHovertextTemplate
6515          ** @see #setBrushSize setBrushSize
6516          ** @see #setHoverAnnotationEnabled setHoverAnnotationEnabled
6517          ** @see #setHoverSelectionEnabled setHoverSelectionEnabled
6518          **
6519          **/
6520           public void setFillHasHovertext(boolean fillHasHovertext) {
6521              this.fillHasHovertext = fillHasHovertext;
6522           }
6523         
6524         /**
6525         ** Specifies the spacing between successive rectangular
6526         ** elements used to render any required non-rectangular
6527         ** features of the symbol.  <p>
6528         ** 
6529         ** The exact meaning of this spacing
6530         ** setting depends on the symbol type, and on if an external
6531         ** canvas factory has been specified via
6532         ** <tt>setCanvasFactory</tt>:
6533         **
6534         ** <p>
6535         ** 
6536         ** <table border>
6537         **   <tr><th>SymbolType</th><th>How spacing is interpreted</th><th>Default value</th>
6538         **   </tr>
6539         **   <tr><td>BOX_*</td>
6540         **   
6541         **   <td>The distance between the centers of the "dots" used to
6542         **    draw the dotted connecting lines between successive
6543         **    x,y data points on a curve. 
6544         **    <p>
6545         **    
6546         **    If <tt>fillSpacing == 0</tt> ("continuously filled"),
6547         **    <tt>fillThickness > 0</tt>, and a canvas
6548         **    factory has been specified via <tt>setCanvasFactory</tt>, a
6549         **    continuous line connecting the centers of successive boxes is
6550         **    produced in exactly the same way as is done for the
6551         **    <tt>LINE</tt> symbol type. Without a canvas factory (the
6552         **    default) <tt>fillSpacing == 0</tt> works the same as
6553         **    <tt>fillSpacing == 1</tt>.
6554         **    
6555         **    </td>
6556         **   <td>4</td>
6557         **   </tr>
6558         **   
6559         **   <tr><td>LINE</td>
6560         **
6561         **   
6562         **   
6563         **   <td>The horizontal distance between the centers of the
6564         **   successive vertical bars, or the vertical distance
6565         **   between the centers of the successive horizontal bars,
6566         **   that GChart uses to render the point-to-point
6567         **   connecting lines of the LINE symbol type.
6568         **   <p>
6569         **   
6570         **   The defaults (no canvas factory specified, 0px spacing)
6571         **   provide the smoothest lines possible without using canvas, but
6572         **   also the longest chart update times. Spacing values larger
6573         **   than 1px will provide proportionally faster rendering of
6574         **   connecting lines (provided the connecting line segments are
6575         **   significantly longer than the specified spacing), but they
6576         **   give the lines a grainy, "stair-step" look.  <p>
6577         **   
6578         **   If <tt>fillSpacing == 0</tt> ("continuously filled"),
6579         **   <tt>fillThickness > 0</tt>, and
6580         **   a canvas
6581         **   factory has been specified via <tt>setCanvasFactory</tt>, a
6582         **   continuous, crisp (sans stair-steps) line connecting the
6583         **   centers of successive boxes is produced--quickly. Without any
6584         **   canvas factory specified, <tt>fillSpacing == 0</tt> will work the same
6585         **   as <tt>fillSpacing == 1</tt>.
6586         **   
6587         **    <p>
6588         **    
6589         **   <i>Tip:</i> To assure an unbroken connecting line, use a
6590         **   non-zero fill thickness setting greater than or equal to your
6591         **   fill spacing setting.</td>
6592         **   
6593         **   <td>0</td>
6594         **   </tr>
6595         **   
6596         **   <tr><td>PIE_SLICE_*</td>
6597         **   
6598         **   <td>The vertical or horizontal distance between
6599         **   the centers of the vertical, and/or horizontal,
6600         **   shading bars used to fill in the pie slice. With
6601         **   the default setting, this produces a banded-fill
6602         **   look.
6603         **   
6604         **     <p> If <tt>fillSpacing == 0</tt> ("continuously
6605         **    filled"), <tt>fillThickness > 0</tt>, and a canvas
6606         **    factory has been specified via
6607         **    <tt>setCanvasFactory</tt>, the pie slices are crisp,
6608         **    quickly-rendered, and solid-filled.  Without a canvas
6609         **    factory specified (the default) <tt>fillSpacing ==
6610         **    0</tt> works the same as <tt>fillSpacing == 1</tt>.
6611         **    
6612         **   </td>
6613         **   <td>4</td> </tr>
6614         **
6615         **   <tr> 
6616          **   <td>VBAR_*</td>
6617         **
6618         **   <td>The horizontal distance between corresponding edges
6619         **      of the vertical bars used to fill in the trapezoidal
6620         **      areas linearly interpolated between successive
6621         **      vertical bars on a curve.  
6622         ** 
6623         **     <p> If <tt>fillSpacing == 0</tt> ("continuously
6624         **    filled"), <tt>fillThickness > 0</tt>, and a canvas
6625         **    factory has been specified via
6626         **    <tt>setCanvasFactory</tt>, a filled polygon whose
6627         **    perimeter connects the x,y points of the curve with a
6628         **    corresponding interval on the x-axis, x2-axis, or
6629         **    horizontal baseline is rendered, so as to create a
6630         **    vertical, solid-filled, area chart.  <p>
6631         **    
6632         **    Without a canvas factory, <tt>fillSpacing == 0</tt>
6633         **    works the same as <tt>fillSpacing == 1</tt>.
6634         **    
6635         **    </td>
6636         **    <td>0</td>
6637         **    
6638         **   <tr>
6639         **   <td>HBAR_*</td>
6640         **
6641         **   <td>The vertical distance between corresponding edges
6642         **      of the horizontal bars used to fill in the
6643         **      trapezoidal areas linearly interpolated between
6644         **      successive horizontal bars on a curve.
6645         **     <p>  
6646         ** 
6647         **    If <tt>fillSpacing == 0</tt> ("continuously filled"), <tt>fillThickness >
6648         **    0</tt>, and a canvas factory has been specified via
6649         **    <tt>setCanvasFactory</tt>, a filled polygon whose
6650         **    perimeter connects the x,y points of the curve with a
6651         **    corresponding interval on the y-axis, y2-axis, or
6652         **    vertical baseline will be rendered, so as to create a
6653         **    horizontal, solid-filled, area chart.
6654         **    
6655         **    <p>
6656         **    
6657         **    Without a canvas factory, <tt>fillSpacing == 0</tt> works the same as
6658         **    <tt>fillSpacing == 1</tt>.
6659         **
6660         **      </td>
6661         **    
6662         **    <td>0</td>
6663         **   </tr> 
6664         **   <tr>
6665         **   <td>XGRIDLINE</td>
6666         **
6667         **   <td>The horizontal distance between corresponding edges
6668         **      of the vertical bars used to fill in the
6669         **      trapezoidal areas linearly interpolated between
6670         **      successive x-gridlines on a curve.
6671         **     <p>  
6672         ** 
6673         **    If <tt>fillSpacing == 0</tt> ("continuously filled"), <tt>fillThickness >
6674         **    0</tt>, and a canvas factory has been specified via
6675         **    <tt>setCanvasFactory</tt>, a filled polygon is drawn whose
6676         **    perimeter connects the x,y points of the curve with a
6677         **    corresponding interval on either the x-axis or x2-axis,
6678         **    depending on whichever axis is closest to the first
6679         **    point on the curve.
6680         **    
6681         **    <p>
6682         **
6683         **    Without a canvas factory, <tt>fillSpacing == 0</tt> works the same as
6684         **    <tt>fillSpacing == 1</tt>. <p>
6685         **
6686         **    <i>Tip:<i> To make a canvas-filled x-gridline curve fill
6687         **    in a rectangular region, set the y of each point to
6688         **    <tt>Double.MAX_VALUE</tt>.
6689         **    
6690         **      </td>
6691         **    
6692         **    <td>4</td>
6693         **   </tr> 
6694         **   <tr>
6695         **   <td>YGRIDLINE</td>
6696         **
6697         **   <td>The vertical distance between corresponding edges
6698         **      of the horizontal bars used to fill in the
6699         **      trapezoidal areas linearly interpolated between
6700         **      successive y-gridlines on a curve.
6701         **     <p>  
6702         ** 
6703         **    If <tt>fillSpacing == 0</tt> ("continuously filled"), <tt>fillThickness >
6704         **    0</tt>, and a canvas factory has been specified via
6705         **    <tt>setCanvasFactory</tt>, a filled polygon is drawn whose
6706         **    perimeter connects the x,y points of the curve with a
6707         **    corresponding interval on either the y-axis or y2-axis,
6708         **    depending on whichever axis is closest to the first
6709         **    point on the curve.
6710         **    
6711         **    <p>
6712         **
6713         **    Without a canvas factory, <tt>fillSpacing == 0</tt> works the same as
6714         **    <tt>fillSpacing == 1</tt>. <p>
6715         **
6716         **    <i>Tip:<i> To make a canvas-filled y-gridline curve fill
6717         **    in a rectangular region, set the x of each point to
6718         **    <tt>Double.MAX_VALUE</tt>.
6719         **    
6720         **      </td>
6721         **    
6722         **    <td>4</td>
6723         **   </tr> 
6724         ** </table>   
6725         **  
6726         ** <p>
6727         **
6728         ** As of version 2.5 GChart provides support for
6729         ** canvas-based, crisp, quickly drawn, lines, 2-D pie
6730         ** slices, and area charts if a <tt>fillSpacing</tt> of
6731         ** <tt>0</tt> is specified ("continuously filled") along
6732         ** with a <tt>fillThickness > 0</tt>. However, you must
6733         ** bolt-on an external canvas library (plain vanilla GWT
6734         ** does not currently come pre-loaded with a browser
6735         ** independent canvas Widget. However, the fact that the GWT
6736         ** incubator project contains one implies that the GWT team
6737         ** is considering adding one). See the
6738         ** <tt>setCanvasFactory</tt> method for details.
6739         ** 
6740         ** <p>
6741         **
6742         ** By default, GChart does not use an external canvas library,
6743         ** and thus depends only on the standard GWT distribution and
6744         ** its own 3,000-odd semi-colon terminated lines of GWT Java. If you decide to stick with
6745         ** this default, the following tips may help you workaround
6746         ** GChart's rectangle-element-based limitations when
6747         ** used in this mode.
6748         ** <p>
6749         ** 
6750         ** First, if your goal is to produce a solid connecting
6751         ** line between points always use the <tt>LINE</tt> symbol
6752         ** type rather than the <tt>BOX_CENTER</tt> symbol type with a fill
6753         ** spacing of 1px. The <tt>LINE</tt> symbol type knows how
6754         ** to merge adjacent pixels into larger rectangular
6755         ** elements, and is therefore usually much more efficient,
6756         ** especially with curves that involve many near-vertical or
6757         ** near-horizontal connecting lines. For best performance,
6758         ** use the <tt>BOX_CENTER</tt> symbol only to produce
6759         ** dotted connecting lines that have widely spaced dots. 
6760         ** <p>
6761         ** 
6762         ** In general, since the number of elements required is
6763         ** often inversely proportional to fill spacing, using a
6764         ** very small fill spacing like 1px, while allowed, could
6765         ** degrade performance unacceptably, especially for very
6766         ** large-sized charts. On the other hand, too large a fill
6767         ** spacing/thickness can degrade graphical quality
6768         ** unacceptably (e.g. due to too few "dots" on dotted
6769         ** connecting lines, "stair-step" solid connecting lines,
6770         ** or "grainy filled" pie slices).  <p>
6771         ** 
6772         ** <blockquote><small>
6773         ** 
6774         ** <i>Tip:</i> For pie slices as well as for dotted or solid
6775         ** connecting lines, scaling down the size of the chart via
6776         ** <tt>setXPixelSize</tt> and <tt>setYPixelSize</tt> can
6777         ** also speed up chart display, and thus will often provide
6778         ** a better-looking alternative to increasing the fill
6779         ** spacing.  <p>
6780         ** 
6781         **  In particular, for a typical curve whose x-values always
6782         **  increase with point index (i.e. no "doubling back")
6783         **  <tt>LINE</tt> symbol type curves often have a number of
6784         **  elements, and thus an update time, that is approximately
6785         **  equal to:
6786         **  
6787         **   <p>
6788         **   
6789         **   <pre>
6790         **
6791         **     "Some Constant" * (xMaxInPixels-xMinInPixels)/fillSpacing
6792         **
6793         **   </pre>  
6794         **   <p>
6795         **
6796         **   So, for <tt>LINE</tt> curves, halving the x-axis range
6797         **   via <tt>setXPixelSize</tt> will provide approximately
6798         **   the same speedup as doubling the fill spacing setting,
6799         **   and, because the lines will be less "stair-steppy",
6800         **   will often provide a more acceptable visual result.
6801         **   
6802         **   </small>
6803         **   </blockquote>
6804         ** 
6805         ** Experience suggests that many applications will be able
6806         ** to find a combination of chart size and spacing/thickness
6807         ** settings that provide an acceptable level of both
6808         ** graphical quality and performance--particularly if your
6809         ** charting needs are more utilitarian than aesthetic. When
6810         ** that's not good enough, it's time to use
6811         ** <tt>setCanvasFactory</tt> to super-charge GChart's
6812         ** rendering with the power of a cross-browser vector
6813         ** graphics facility, such as <tt>GWTCanvas</tt>.
6814         ** 
6815         ** <p>
6816         **
6817         ** @param fillSpacing spacing between successive rectangular
6818         **   elements used to fill in non-rectangular symbols, in
6819         **   pixels. If a canvas factory has been specified,
6820         **   you can use a setting of <tt>0</tt> to produces
6821         **   "continuously filled" elements.
6822         **   
6823         **
6824         ** @see #getFillSpacing getFillSpacing
6825         ** @see #setFillThickness setFillThickness
6826         ** @see #setCanvasFactory setCanvasFactory
6827         ** 
6828          */
6829         public void setFillSpacing(double fillSpacing) {
6830            getParent().invalidate();
6831           // x!=x is a faster isNaN
6832            if (!(fillSpacing!=fillSpacing) &&
6833                fillSpacing != 0 && fillSpacing < 1)
6834             throw new IllegalArgumentException(
6835              "fillSpacing="+fillSpacing+"; "+
6836              "fillSpacing must either be >= 1, or else " +
6837              "equal to either 0 or Double.NaN.");
6838           this.fillSpacing = fillSpacing;
6839         }
6840    
6841         /**
6842         ** Sets the "thickness" of the rectangular elements used to
6843         ** render any required non-rectangular features of this symbol.
6844         ** <p>
6845         ** 
6846         ** The exact meaning of this thickness setting, as well as
6847         ** the default used whenever the thickness is set to the
6848         ** special undefined integer value recognized by GChart
6849         ** (<tt>GChart.NAI</tt>), depends on the symbol type, and
6850         ** if an external canvas factory has been specified via
6851         ** <tt>setCanvasFactory</tt>:
6852         ** <p>
6853         ** 
6854         ** <table border>
6855         **   <tr><th>SymbolType</th><th>How thickness is interpreted</th><th>Default value</th>
6856         **   </tr>
6857         **   <tr><td>BOX_*</td>
6858         **   
6859         **   <td>The height and width of rectangular "dots" used to
6860         **    draw the dotted connecting lines between successive
6861         **    x,y data points on a curve. 
6862         **    <p>
6863         **
6864         **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>
6865         **    and a canvas factory has been specified via
6866         **    <tt>setCanvasFactory</tt>,
6867         **    <tt>fillThickness</tt> is the width of the continuous
6868         **    connecting lines between successive points.
6869         **    </td> <p>
6870         **
6871         **   <td>0 (implies no interpolated dots or connecting lines)</td>
6872         **   </tr>
6873         **   
6874         **   <tr><td>LINE</td>
6875         **   
6876         **   <td>The width of the vertical line segments placed
6877         **   end-to-end to render any "more-nearly-vertical"
6878         **   connecting lines of the curve, and the height of the
6879         **   horizontal line segments placed end-to-end to render
6880         **   any "more-nearly-horizontal" connecting lines on this
6881         **   curve.  Note that if you use a fill thickness less than
6882         **   the fill spacing, your line will not be continuously
6883         **   connected.
6884         **   <p>
6885         **
6886         **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>
6887         **    and a canvas factory has been specified via
6888         **    <tt>setCanvasFactory</tt>,
6889         **    <tt>fillThickness</tt> is the width of the continuous
6890         **    connecting lines drawn between successive points.
6891         **
6892         **   </td>
6893         **   
6894         **  <td>1</td>
6895         **   </tr>
6896         **   
6897         **   <tr><td>PIE_SLICE_*</td>
6898         **   
6899         **   <td>The width of vertical, and/or the height of
6900         **   horizontal, shading bars used to fill in the pie slice
6901         **   <p>
6902         **   
6903         **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>
6904         **    and a canvas factory has been specified via
6905         **    <tt>setCanvasFactory</tt>, pie slices are solidly
6906         **    and continously filled for any <tt>fillThickness &gt; 0</tt>.
6907         **
6908         **   </td>
6909         **   <td>2</td>
6910         **   
6911         **   </tr>
6912         **
6913         **   <tr>
6914          **   <td>VBAR_*</td>
6915         **
6916         **   <td>The width of vertical bars
6917         **      used to fill in the trapezoidal areas linearly
6918         **      interpolated between successive vertical
6919         **      bars on a curve.
6920         **    <p>
6921         **     
6922         **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>
6923         **    and a canvas factory has been specified via
6924         **    <tt>setCanvasFactory</tt>, these trapezoidal areas are
6925         **    merged together and solidly
6926         **    and continously filled for any <tt>fillThickness &gt; 0</tt>. 
6927         **
6928         **      </td>
6929         **    
6930         **    <td>0 (implies no "area filling" between bars)
6931         **    
6932         **   <tr>
6933         **   <td>HBAR_*</td>
6934         **
6935         **   <td>The height of horizontal bars used to fill in the
6936         **      trapezoidal areas linearly interpolated between
6937         **      successive horizontal bars on a curve.
6938         **    <p>
6939         **     
6940         **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>
6941         **    and a canvas factory has been specified via
6942         **    <tt>setCanvasFactory</tt>, these trapezoidal areas are
6943         **    merged together and solidly
6944         **    and continously filled for any <tt>fillThickness &gt; 0</tt>. 
6945         **    
6946         **      </td>
6947         **    
6948         **    <td>0 (implies no "area filling" between bars)
6949         **   </tr> 
6950         **   <tr>
6951         **   <td>XGRIDLINE</td>
6952         **
6953         **   <td>The width of vertical bars used to fill in the
6954         **      trapezoidal areas linearly interpolated between
6955         **      successive x-gridlilnes on a curve.
6956         **    <p>
6957         **     
6958         **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>,
6959         **    <tt>fillThickness &gt; 0</tt>, and a canvas factory has been
6960         **    specified via <tt>setCanvasFactory</tt>, a continuously
6961         **    filled area-chart, as described in <tt>setFillSpacing</tt>,
6962         **    will be produced.
6963         **    
6964         **      </td>
6965         **    
6966         **    <td>0 (implies no "area filling" between gridlines)
6967         **   </tr> 
6968         **   <tr>
6969         **   <td>YGRIDLINE</td>
6970         **
6971         **   <td>The height of horizontal bars used to fill in the
6972         **      trapezoidal areas linearly interpolated between
6973         **      successive y-gridlilnes on a curve.
6974         **    <p>
6975         **     
6976         **    If the fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>,
6977         **    <tt>fillThickness &gt; 0</tt>, and a canvas factory has been
6978         **    specified via <tt>setCanvasFactory</tt>, a continuously
6979         **    filled area-chart, as described in <tt>setFillSpacing</tt>,
6980         **    will be produced.
6981         **    
6982         **      </td>
6983         **    
6984         **    <td>0 (implies no "area filling" between gridlines)
6985         **   </tr> 
6986         ** </table>   
6987         ** 
6988         ** 
6989         **
6990         ** <p> This fill thickness setting and the associated fill
6991         ** spacing setting (c.f. <tt>setFillSpacing</tt>) work
6992         ** together to define the look and efficiency of pie slice
6993         ** shading, connecting lines, etc. 
6994         **
6995         ** @param fillThickness the fill thickness, in pixels
6996         **
6997         ** @see #setCanvasFactory setCanvasFactory
6998         ** @see #getFillThickness getFillThickness
6999         ** @see #setFillSpacing setFillSpacing
7000          */
7001           public void setFillThickness(int fillThickness) {
7002            getParent().invalidate();
7003             if (fillThickness!=GChart.NAI && fillThickness < 0)
7004               throw new IllegalArgumentException(
7005                "fillThickness="+fillThickness+"; "+
7006                "fillThickness must either be >= 0, or else " +
7007                "equal to GChart.NAI.");
7008              this.fillThickness = fillThickness;
7009           }
7010    
7011         /**
7012          * Sets a boolean that determines if point-specific
7013          * annotations will popup whenever you hover over a point on
7014          * the curve associated with this symbol.<p>
7015          *
7016          * By default, these hover-induced popups are enabled.
7017          * <p>
7018          * 
7019          * Note that the point selection feedback on the
7020          * hovered-over point is controlled separately, via the
7021          * <tt>setHoverSelectionEnabled</tt> method.
7022          * 
7023          * @param hoverAnnotationEnabled true if hover-induced annotations popup on this
7024          * curve, false otherwise.
7025          *
7026          * @see #getHoverAnnotationEnabled getHoverAnnotationEnabled
7027          * @see #setHoverSelectionEnabled setHoverSelectionEnabled
7028          * @see #setHovertextTemplate setHovertextTemplate
7029          * @see #setHoverWidget setHoverWidget
7030          * @see #setHoverLocation setHoverLocation
7031          * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
7032          * @see #setHoverXShift setHoverXShift
7033          * @see #setHoverYShift setHoverYShift
7034          * 
7035          */
7036         public void setHoverAnnotationEnabled(boolean hoverAnnotationEnabled) {
7037           this.hoverAnnotationEnabled = hoverAnnotationEnabled;
7038         }
7039         /**
7040          ** Specifies the weight of the font that will be used
7041          ** to render the text of this point's hover annotations.
7042          ** <p>
7043          ** 
7044          ** @param cssWeight A standard CSS font-weight
7045          **    specification such as normal, bold, bolder, lighter,
7046          **    100, 200, ... 900, or inherit
7047          **
7048          ** @see #getHoverFontWeight getHoverFontWeight  
7049          ** @see #setHoverFontColor setHoverFontColor
7050          ** @see #setHoverFontStyle setHoverFontStyle
7051          ** @see #setHoverFontSize setHoverFontSize
7052          ** @see #setHoverLocation setHoverLocation
7053          ** @see #setHoverWidget setHoverWidget
7054          ** @see #setHoverXShift setHoverXShift
7055          ** @see #setHoverYShift setHoverYShift
7056          **
7057          **
7058          ** 
7059          **/ 
7060        public void setHoverFontWeight(String cssWeight) {
7061           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
7062           hoverAnnotation.setFontWeight(cssWeight);      
7063        }
7064         /**
7065          ** Specifies the color of the hover annotations' font.
7066          **
7067          ** 
7068          ** <p>
7069          ** For more information on standard CSS color
7070          ** specifications see the discussion in
7071          ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
7072          ** <p>
7073          **        
7074          ** @param cssColor color of the font used to display this
7075          **    symbol's hover annotations.
7076          **
7077          ** @see #getHoverFontColor getHoverFontColor  
7078          ** @see #setHoverFontWeight setHoverFontWeight  
7079          ** @see #setHoverFontStyle setHoverFontStyle
7080          ** @see #setHoverFontSize setHoverFontSize
7081          ** @see #setHoverLocation setHoverLocation
7082          ** @see #setHoverWidget setHoverWidget
7083          ** @see #setHoverXShift setHoverXShift
7084          ** @see #setHoverYShift setHoverYShift
7085          **/ 
7086         public void setHoverFontColor(String cssColor) {
7087           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
7088           hoverAnnotation.setFontColor(cssColor);      
7089         }
7090    
7091         
7092         /**
7093          ** Specifies the CSS font-style used by this symbol's hover
7094          ** annotations.
7095          **
7096          ** @param cssStyle any valid CSS font-style, namely,
7097          **   normal, italic, oblique, or inherit.
7098          **
7099          ** @see #getHoverFontStyle getHoverFontStyle  
7100          ** @see #setHoverFontWeight setHoverFontWeight  
7101          ** @see #setHoverFontColor setHoverFontColor
7102          ** @see #setHoverFontSize setHoverFontSize
7103          ** @see #setHoverLocation setHoverLocation
7104          ** @see #setHoverWidget setHoverWidget
7105          ** @see #setHoverXShift setHoverXShift
7106          ** @see #setHoverYShift setHoverYShift
7107          **/ 
7108         public void setHoverFontStyle(String cssStyle) {
7109           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
7110           hoverAnnotation.setFontStyle(cssStyle);      
7111         }
7112         /**
7113          ** Specifies the CSS font size used in this symbol's hover
7114          ** annotations, in pixels.
7115          **
7116          ** @param fontSize the CSS font size used in the
7117          ** hover annotations associated with this symbol, in pixels.
7118          **
7119          ** @see #getHoverFontSize getHoverFontSize  
7120          ** @see #setHoverFontWeight setHoverFontWeight  
7121          ** @see #setHoverFontColor setHoverFontColor
7122          ** @see #setHoverFontStyle setHoverFontStyle
7123          ** @see #setHoverLocation setHoverLocation
7124          ** @see #setHoverWidget setHoverWidget
7125          ** @see #setHoverXShift setHoverXShift
7126          ** @see #setHoverYShift setHoverYShift
7127          **/ 
7128         public void setHoverFontSize(int fontSize) {
7129           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
7130           hoverAnnotation.setFontSize(fontSize);      
7131         }
7132        /**
7133         *
7134         * Specifies the location of this point's hover annotations. Set
7135         * this property to <tt>null</tt> (the default) to use GChart's
7136         * default hover location, which varies with the hover annotation's
7137         * symbol type, as tabulated below. (The hover annotation symbol type defaults to the
7138         * symbol type of the hovered over curve, and can be specified
7139         * explicitly via the <tt>setHoverAnnotationSymbolType</tt> method).
7140         * <p>
7141         *
7142         * <table border>
7143         *   <tr><th>SymbolType used to<br>position hover annotation</th>
7144         *   <th>Default Hover<br>AnnotationLocation</th></tr>
7145         *   <tr><td>HBAR_BASELINE_*</td><td>FARTHEST_FROM_VERTICAL_BASELINE</td></tr>
7146         *   <tr><td>HBAR_*WEST</td><td>EAST</td></tr>
7147         *   <tr><td>HBAR_*EAST</td><td>WEST</td></tr>
7148         *   <tr><td>PIE_SLICE_*</td><td>OUTSIDE_PIE_ARC</td></tr>
7149         *   <tr><td>VBAR_SOUTH*</td><td>NORTH</td></tr>
7150         *   <tr><td>VBAR_BASELINE_*</td><td>FARTHEST_FROM_HORIZONTAL_BASELINE</td></tr>
7151         *   <tr><td>VBAR_NORTH*</td><td>SOUTH</td></tr>
7152         *   <tr><td>All others</td><td>NORTHWEST</td></tr>
7153         * </table>
7154         * 
7155         * <p>
7156         * 
7157         * You can further adjust the position of a point's
7158         * hover annotations by specifying non-zero positional shifts via
7159         * the <tt>setHoverXShift</tt> and
7160         * <tt>setHoverYShift</tt> methods, and via the
7161         * <tt>setHoverAnnotationSymbolType</tt> method.
7162         * <p>
7163         *
7164         * <i>Tip:</i> To position hover annotations at a fixed location
7165         * on the chart, (such as a status bar that displays
7166         * information about the hovered over point, an inset chart
7167         * that shows a detailed view, etc.)  pass one of the
7168         * <tt>ANCHOR_*</tt> symbol types to the
7169         * <tt>setHoverAnnotationSymbolType</tt> method.  <p>
7170         * 
7171         * @param hoverLocation the relative location of
7172         * the hover annotations, or <tt>null</tt> to use a
7173         * symbol-type-specific default.
7174         *
7175         * @see #getHoverLocation getHoverLocation
7176         * @see #setHoverFontWeight setHoverFontWeight  
7177         * @see #setHoverFontColor setHoverFontColor
7178         * @see #setHoverFontStyle setHoverFontStyle
7179         * @see #setHoverFontSize setHoverFontSize
7180         * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
7181         * @see #setHoverWidget setHoverWidget
7182         * @see #setHoverXShift setHoverXShift
7183         * @see #setHoverYShift setHoverYShift
7184         * @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
7185         *
7186         */
7187        public void setHoverLocation(AnnotationLocation hoverLocation) {
7188           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
7189           hoverAnnotation.setLocation(hoverLocation);      
7190        }
7191    
7192        /**
7193         *
7194         * Sets the symbol type that GChart will use when positioning
7195         * hover annotations. GChart positions each hover annotation
7196         * as if it were associated with a point with the
7197         * same x,y as the hovered over point, and mapped to the same
7198         * y-axis, but that appears on a curve with the symbol type
7199         * specified by this method.
7200         * 
7201         * <p> If <tt>null</tt> is used (this is the default) GChart
7202         * will use the symbol type associated with the curve
7203         * containing the hovered over point.  Since normally you
7204         * will want hover annotations to be positioned as if they were
7205         * annotations of the hovered over points, this default is
7206         * usually appropriate.  <p>
7207         *
7208         * However, sometimes you would like the hover annotations to be
7209         * positioned differently. For example, you might prefer the
7210         * hover annotations to always appear within a status bar at the
7211         * bottom of the chart. To achieve this, you could set this
7212         * property to <tt>ANCHOR_SOUTHWEST</tt>.  Or suppose you
7213         * always wanted a pie chart's hover annotations to appear in
7214         * the center of the pie instead of along the outer
7215         * perimeter. Then you could use <tt>BOX_CENTER</tt>. Or, if you
7216         * wanted the hover annotations to be positioned relative to the
7217         * position that the mouse was at when the symbol was first
7218         * "touched", you could use <tt>ANCHOR_MOUSE</tt>.
7219         * <p>
7220         *
7221         * <i>Tip:</i> Pre v2.4 versions of GChart supported a much
7222         * simpler, "at-the-mouse", <tt>setTitle</tt>-based, hover
7223         * feedback that you can emulate using code such as: <p>
7224         * 
7225         * <pre>
7226         *     Symbol sym = getCurve().getSymbol();
7227         *     sym.setHoverAnnotationSymbolType(SymbolType.ANCHOR_MOUSE);
7228         *     sym.setHoverLocation(AnnotationLocation.SOUTHEAST);
7229         *     sym.setHoverYShift(-20); // push 20px below mouse
7230         *                              // (kind of like setTitle does it).
7231         *
7232         *     // Convenience/transition-helper method
7233         *     // formatAsHovertext wraps plain text in appropriate
7234         *     // HTML so it looks kind of like setTitle-based hovertext.
7235         *     
7236         *     sym.setHovertextTemplate(
7237         *       GChart.formatAsHovertext("x=${x}, y=${y}")); 
7238         * </pre>  
7239         * <p>
7240         *
7241         * @param hoverAnnotationSymbolType the symbol type that in part
7242         * determines how the hover annotations get positioned, or
7243         * <tt>null</tt> (the default) to use the symbol type of the
7244         * hovered over point.
7245         *   
7246         * @see #getHoverAnnotationSymbolType getHoverAnnotationSymbolType
7247         * @see #setHoverLocation setHoverLocation
7248         * @see #setHovertextTemplate setHovertextTemplate 
7249         * @see #setHoverXShift setHoverXShift
7250         * @see #setHoverYShift setHoverYShift
7251         * @see GChart#formatAsHovertext GChart.formatAsHovertext
7252         */
7253         public void setHoverAnnotationSymbolType(
7254                       SymbolType hoverAnnotationSymbolType) {
7255           this.hoverAnnotationSymbolType = hoverAnnotationSymbolType;  
7256        }
7257         /**
7258          * Specifies the background color used to indicate that the mouse is
7259          * "touching" (hovering over) a point.
7260          * <p>
7261          * 
7262          * Whenever the user "touches" a point on this curve with
7263          * the curve's mouse-centered "brush", GChart displays the hover
7264          * feedback for that point, and indicates that the point is
7265          * the one the hover feedback refers to changing its
7266          * background color to this color.
7267          * <p>
7268          * 
7269          * The default hover selection background color is
7270          * "transparent". This allows the original symbol to appear
7271          * within selection rectangles that can be defined via the
7272          * <tt>setHoverSelectionBorderWidth</tt> and
7273          * <tt>setHoverSelectionBorderColor</tt> methods (1px thick
7274          * external gray selection rectangles are used by default).
7275          * <p>
7276          * 
7277          * <i>Tip:</i> Because the background selection color
7278          * will often cover the original symbol, it's usually
7279          * best to choose a selection background color closely
7280          * related to the original symbol's background color.
7281          * For example, if the original symbol were blue, you
7282          * might use a lighter shade of blue.
7283          * 
7284          * @param hoverSelectionBackgroundColor a CSS color
7285          * specification string that specifies the background color used to
7286          * indicate "hover-selection".
7287          *
7288          * @see #getHoverSelectionBackgroundColor
7289          * getHoverSelectionBackgroundColor
7290          * @see #setHoverSelectionBorderColor
7291          * setHoverSelectionBorderColor
7292          * @see #setHoverSelectionBorderStyle
7293          * setHoverSelectionBorderStyle
7294          * @see #setHoverSelectionBorderWidth
7295          * setHoverSelectionBorderWidth
7296          * @see #setBrushHeight setBrushHeight
7297          * 
7298          */
7299         public void setHoverSelectionBackgroundColor(
7300            String hoverSelectionBackgroundColor) {
7301           this.hoverSelectionBackgroundColor = hoverSelectionBackgroundColor;
7302         }
7303         /**
7304          * Specifies the border color used to indicate that the mouse is
7305          * "touching" (hovering over) a point.
7306          * <p>
7307          * 
7308          * Whenever the user "touches" a point on this curve with
7309          * the mouse-centered "brush", GChart displays the hover
7310          * feedback for that point, and indicates that the point is
7311          * the one the hover feedback refers to by drawing a border
7312          * around it with the given color.
7313          * <p>
7314          *
7315          * The width of this border, and if the is drawn outside or
7316          * just inside the rectangles associated with the symbol,
7317          * can be specified via
7318          * <tt>setHoverSelectionBorderWidth</tt>.
7319          * 
7320          * The default hover selection border color is <tt>gray</tt>. 
7321          * 
7322          * @param hoverSelectionBorderColor a CSS color specification string that specifies
7323          * the color used to indicate "hover-selection", or the special
7324          * keyword TRANSPARENT_BORDER_COLOR for a cross-browser consistent
7325          * transparent border.
7326          *
7327          * @see #getHoverSelectionBorderColor
7328          * getHoverSelectionBorderColor
7329          * @see #setHoverSelectionBorderStyle
7330          * setHoverSelectionBorderStyle
7331          * @see #setHoverSelectionBorderWidth
7332          * setHoverSelectionBorderWidth
7333          * @see #setBrushHeight setBrushHeight
7334          * 
7335          */
7336         public void setHoverSelectionBorderColor(String hoverSelectionBorderColor) {
7337           this.hoverSelectionBorderColor = hoverSelectionBorderColor;
7338         }
7339    
7340         /**
7341          * Specifies the border style used to indicate that the mouse is
7342          * "touching" (hovering over) a point.
7343          * <p>
7344          * 
7345          * Whenever the user "touches" a point on this curve with
7346          * the mouse-centered "brush", GChart displays the hover
7347          * feedback for that point, and indicates that the point is
7348          * the one the hover feedback refers to by drawing a border
7349          * around it with the given style.
7350          * <p>
7351          *
7352          * The width of this border, and if the is drawn outside or
7353          * just inside the rectangles associated with the symbol,
7354          * can be specified via
7355          * <tt>setHoverSelectionBorderWidth</tt>.
7356          * 
7357          * The default hover selection border style is <tt>solid</tt>. 
7358          * 
7359          * @param hoverSelectionBorderStyle a CSS border style
7360          * specification string that indicates the style of border used to
7361          * indicate "hover-selection".
7362          *
7363          * @see #getHoverSelectionBorderStyle
7364          * getHoverSelectionBorderStyle
7365          * @see #setHoverSelectionBorderWidth
7366          * setHoverSelectionBorderWidth
7367          * @see #setHoverSelectionBorderColor
7368          * setHoverSelectionBorderColor
7369          * @see #setBrushHeight setBrushHeight
7370          * 
7371          */
7372         public void setHoverSelectionBorderStyle(String hoverSelectionBorderStyle) {
7373           this.hoverSelectionBorderStyle = hoverSelectionBorderStyle;
7374         }
7375    
7376         
7377         /**
7378          * Sets the width of the border around the perimeter of
7379          * rectangles used to indicate that the mouse is
7380          * "touching" (hovering over) a point.
7381          * <p>
7382          *
7383          * If positive, the border is drawn inside each rendered
7384          * rectangle of the selected symbol. If negative, the border
7385          * is drawn outside of those rectangles.
7386          * <p>
7387          * 
7388          * <i>Tip:</i> To create the illusion that symbols
7389          * increase in size when they are "touched", use a hover
7390          * selection border color that matches the symbol's color
7391          * along with a negative hover selection border width.  <p>
7392          * 
7393          * @param borderWidth the width of the border drawn around
7394          * the perimeter of the selected symbol's rectangles to
7395          * indicate that the symbol is being "touched: by the mouse.  A
7396          * negative value adds that border around the outside of the
7397          * existing rectangles, in effect increasing the selected
7398          * symbol's size (in pixels).
7399          *
7400          * @see #getHoverSelectionBorderWidth
7401          * getHoverSelectionBorderWidth
7402          * @see #setHoverSelectionBorderColor setHoverSelectionBorderColor
7403          * @see #setHoverSelectionBackgroundColor setHoverSelectionBackgroundColor
7404          * @see #setHoverSelectionBorderStyle
7405          * setHoverSelectionBorderStyle
7406          *
7407          * 
7408          */
7409          public void setHoverSelectionBorderWidth(int borderWidth) {
7410             hoverSelectionBorderWidth = borderWidth;
7411          }
7412    
7413    
7414    
7415          
7416         /**
7417          * Specifies if hover selection feedback will be provided
7418          * for this curve.
7419          * <p>
7420          * 
7421          * When enabled, whenever the user "touches" a point on this
7422          * curve with the mouse-centered "brush", GChart indicates
7423          * the hover-selected point by adding a selection border
7424          * around the point, etc.
7425          * <p>
7426          * 
7427          * By default, hover selection feedback is enabled.
7428          * <p>
7429          * 
7430          * Note that the pop-up hover annotation itself is
7431          * controlled separately, via the
7432          * <tt>setHoverAnnotationEnabled</tt> method.
7433          * <p>
7434          * 
7435          * @param hoverSelectionEnabled a if true, hover selection feedback is enabled,
7436          *   if false, hovering over a point does not change its
7437          *   color.
7438          *
7439          * @see #getHoverSelectionEnabled getHoverSelectionEnabled
7440          * @see #setHoverAnnotationEnabled setHoverAnnotationEnabled
7441          * @see #setHoverSelectionBackgroundColor setHoverSelectionBackgroundColor
7442          * @see #setHoverSelectionBorderColor setHoverSelectionBorderColor
7443          * @see #setHoverSelectionBorderStyle
7444          * setHoverSelectionBorderStyle
7445          * @see #setHoverSelectionBorderWidth setHoverSelectionBorderWidth
7446          * @see #setHoverSelectionSymbolType setHoverSelectionSymbolType
7447          * 
7448          */
7449         public void setHoverSelectionEnabled(boolean hoverSelectionEnabled) {
7450           this.hoverSelectionEnabled = hoverSelectionEnabled;
7451         }
7452    
7453         /**
7454          * Specifies the fill spacing that will be used when
7455          * rendering this curve's hover selection feedback.
7456          * <p>
7457          *
7458          * For more on fill spacing, see
7459          * <tt>setFillSpacing</tt>.
7460          * <p>
7461          * 
7462          * @param selectionFillSpacing fill spacing, in pixels, used
7463          * when rendering this curve's hover selection feedback or
7464          * <tt>Double.NaN</tt> (the default) to adopt the curve's
7465          * fill spacing.
7466          *
7467          * @see #getHoverSelectionFillSpacing getHoverSelectionFillSpacing
7468          * @see #setFillSpacing setFillSpacing
7469          * 
7470          */
7471         
7472          public void setHoverSelectionFillSpacing(double selectionFillSpacing) {
7473             hoverSelectionFillSpacing = selectionFillSpacing;
7474          }
7475         /**
7476          * Specifies the fill thickness that will be used when
7477          * rendering this curve's hover selection feedback.
7478          * <p>
7479          *
7480          * For more on fill thickness, see
7481          * <tt>setFillThickness</tt>.
7482          * <p>
7483          * 
7484          * @param selectionFillThickness fill thickness, in pixels, used
7485          * when rendering this curve's hover selection feedback or
7486          * <tt>GChart.NAI</tt> (the default) to adopt the curve's
7487          * fill thickness.
7488          *
7489          * @see #getHoverSelectionFillThickness getHoverSelectionFillThickness
7490          * @see #setFillThickness setFillThickness
7491           * 
7492          */
7493          public void setHoverSelectionFillThickness(int selectionFillThickness) {
7494             hoverSelectionFillThickness = selectionFillThickness;
7495          }
7496    
7497         
7498         /**
7499          * Sets the height of the symbol used to indicate
7500          * when a given point is being "hovered over" with the
7501          * mouse.
7502          * <p>
7503          *
7504          * With the default setting of <tt>GChart.NAI</tt>, GChart
7505          * simply gives the hover selection symbol the same height as
7506          * the symbol representing the point itself.  Though this
7507          * default is usually appropriate, you might want the
7508          * selection symbol to have a larger size so as to increase
7509          * the visibility of the selected point, etc.
7510          * <p>
7511          * 
7512          * @param selectionHeight the height of the symbol used
7513          * to indicate that that a point has been selected, in
7514          * pixels, or <tt>GChart.NAI</tt> (the default) to use
7515          * the height of the symbol representing the selected
7516          * point.
7517          * 
7518          *
7519          * @see #getHoverSelectionHeight getHoverSelectionHeight
7520          * @see #setHoverSelectionWidth setHoverSelectionWidth
7521          * @see #setHoverSelectionBorderColor setHoverSelectionBorderColor
7522          * @see #setHoverSelectionBackgroundColor setHoverSelectionBackgroundColor
7523          *
7524          * 
7525          */
7526          public void setHoverSelectionHeight(int selectionHeight) {
7527             hoverSelectionHeight = selectionHeight;
7528          }
7529    
7530         /**
7531          * Specifies the URL that will define the image
7532          * used to render selection feedback for points on
7533          * the curve associated with this symbol.
7534          * 
7535          * <p>
7536          *
7537          * Specify <tt>null</tt> to use the URL returned by
7538          * <tt>getBlankImageURL</tt> (this is the default, and gives you a
7539          * blank 1x1 pixel GIF). Since the image is transparent, the
7540          * <tt>setHoverSelectionBackgroundColor</tt> method can be used to
7541          * define the background color of the selection feedback.  <p>
7542          *
7543          * Though most applications will do just fine with this default,
7544          * you can use this method for special selection effects, such
7545          * creating a semi-transparent "screen" (say, by using an image
7546          * with alternating transparent and gray pixels) that overlays the
7547          * selected points.
7548          * <p>
7549          *
7550          * The image is applied in the same way as the symbol's own image
7551          * URL, but to the internal, system, curve GChart uses to render
7552          * the selection feedback. See <tt>setImageURL</tt> for additional
7553          * information.
7554          * 
7555          * @see #getHoverSelectionImageURL getHoverSelectionImageURL
7556          * @see #setImageURL setImageURL
7557          * @see #setBlankImageURL setBlankImageURL
7558    
7559          * @param imageURL the url that defines the image used to generate
7560          * selection feedback for points rendered with this symbol, or
7561          * <tt>null</tt> to to use GChart's default selection image URL (a
7562          * 1x1 transparent blank GIF).
7563          * 
7564          */ 
7565         public void setHoverSelectionImageURL(String imageURL) {
7566            hoverSelectionImageURL = imageURL;
7567         }
7568          
7569        /**
7570         *
7571         * Sets the symbol type that GChart will use when
7572         * generating selection feedback. GChart indicates that
7573         * a point is selected by re-rendering the point <i>as
7574         * if</i> it had this symbol type (this re-rendering
7575         * overlays, but need not completely cover, the
7576         * original rendering). 
7577         * 
7578         * <p> If <tt>null</tt> is used (this is the default) GChart
7579         * will use the symbol type associated with the original
7580         * point. This default, which overlays the selection feedback
7581         * on top of the rendered symbol, is usually appropriate.
7582         * <p>
7583         *
7584         * However, sometimes you would like the selection feedback
7585         * to use a different symbol type. For example, you might
7586         * prefer to indicate that a point is selected by drawing a
7587         * vertical gridline through the point. To achieve this, you
7588         * could use the <tt>XGRIDLINE</tt> symbol type. Or, you
7589         * might wish to indicate selection by dropping a vertical
7590         * line from the center of the selected point to the x-axis.
7591         * In this case, you could use <tt>VBAR_SOUTH</tt> as the
7592         * hover selection symbol type.
7593         * <p>
7594         * 
7595         * <i>Note:</i> The special mouse related symbol types (those with
7596         * names matching <tt>ANCHOR_MOUSE*</tt>) are intended for use
7597         * in positioning hover popup annotations (via
7598         * <tt>setHoverAnnotationSymbolType</tt>). They are not expected to be
7599         * useful, and could potentially cause confusion, if used as the
7600         * symbol type passed to this method.
7601         * 
7602         * <p>
7603         * 
7604         * @param hoverSelectionSymbolType the symbol type that in
7605         * part determines how selection feedback for a hovered over
7606         * point is drawn, or <tt>null</tt> (the default) to use the
7607         * symbol type of the hovered over point.
7608         *   
7609         * @see #getHoverSelectionSymbolType getHoverSelectionSymbolType
7610         * @see Symbol#setHoverSelectionBackgroundColor
7611         * setHoverSelectionBackgroundColor
7612         * @see Symbol#setHoverSelectionBorderColor setHoverSelectionBorderColor
7613         * @see Symbol#setHoverSelectionBorderWidth setHoverSelectionBorderWidth
7614         * @see Symbol#setHoverSelectionHeight setHoverSelectionHeight
7615         * @see Symbol#setHoverSelectionWidth setHoverSelectionWidth
7616         * @see Symbol#setHoverAnnotationSymbolType setHoverAnnotationSymbolType
7617         * 
7618         */
7619         public void setHoverSelectionSymbolType(
7620                       SymbolType hoverSelectionSymbolType) {
7621    //       throwExceptionOnAnchorMouse(hoverSelectionSymbolType);  
7622           this.hoverSelectionSymbolType = hoverSelectionSymbolType;  
7623        }
7624         /**
7625          * Sets the width of the symbol used to indicate
7626          * when a given point is being "hovered over" with the
7627          * mouse.
7628          * <p>
7629          *
7630          * With the default setting of <tt>GChart.NAI</tt>, GChart
7631          * simply gives the hover selection symbol the same width as
7632          * the symbol representing the point itself.  Though this
7633          * default is usually appropriate, you might want the
7634          * selection symbol to have a larger size so as to increase
7635          * the visibility of the selected point, etc.
7636          * <p>
7637          * 
7638          * @param selectionWidth the width of the symbol used to
7639          * indicate that that a point has been selected, in
7640          * pixels, or
7641          * <tt>GChart.NAI</tt> (the default) to use the width of the
7642          * symbol representing the selected point.
7643          * 
7644          *
7645          * @see #getHoverSelectionWidth getHoverSelectionWidth
7646          * @see #setHoverSelectionHeight setHoverSelectionHeight
7647          * @see #setHoverSelectionBorderColor setHoverSelectionBorderColor
7648          * @see #setHoverSelectionBackgroundColor setHoverSelectionBackgroundColor
7649          *
7650          * 
7651          */
7652          public void setHoverSelectionWidth(int selectionWidth) {
7653             hoverSelectionWidth = selectionWidth;
7654          }
7655    
7656    
7657         
7658      /**
7659       ** Defines the "hover-text" that appears whenever the user
7660       ** points their mouse at a point on the curve.
7661       ** <p>
7662       **
7663       ** HTML is supported within hover-text. As with
7664       ** <tt>setAnnotationText</tt>, you must prefix HTML template
7665       ** strings with <tt>&lt;html&gt;</tt> or they will be
7666       ** treated as plain text. <p>
7667       ** 
7668       ** <p> Three built-in parameters, <tt>${x}</tt>, <tt>${y}</tt>, and
7669       ** <tt>${pieSliceSize}</tt>
7670       ** are recognized within these hover text templates. Any
7671       ** occurrences of <tt>${x}</tt> in the string will be replaced with the
7672       ** x-coordinate of the point, formatted as per the specified
7673       ** tick label format of the x-axis. Any occurrences of
7674       ** <tt>${y}</tt> within the string will be replaced with the
7675       ** y-coordinate of the point, formatted either using the
7676       ** y-axis or y2-axis tick label format, depending on the axis
7677       ** on which the curve is displayed. Any occurrences of
7678       ** <tt>${pieSliceSize}</tt> within the string will be replaced with
7679       ** 100 times the specified <tt>pieSliceSize</tt> of the point,
7680       ** formatted the same way as <tt>${y}</tt>, except that a "%" is
7681       ** tacked onto the end.
7682       ** <p>
7683       **
7684       ** In addition to these built-in parameters, user-defined
7685       ** parameters are also supported. All parameter names must
7686       ** begin with a letter (<tt>a,b,...,z</tt> or
7687       ** <tt>A,B,...,Z</tt>) and be
7688       ** followed by a series of letters, digits (<tt>0,1,...,9</tt>), and
7689       ** underscores (<tt>_</tt>). For example, <tt>${myParam3}</tt> or
7690       ** <tt>${xyz_123}</tt>. Note that parameter names are
7691       ** case-sensitive.<p>
7692       **
7693       ** You define the rules for expanding these user-defined
7694       ** parameters relative to the hovered-over point, by
7695       ** instantiating a <tt>HoverParameterInterpreter</tt> and
7696       ** passing it to GChart's
7697       ** <tt>setHoverParameterInterpreter</tt> method. See the
7698       ** <tt>HoverParameterInterpreter</tt> interface's javadocs for
7699       ** full details.<p>
7700       **
7701       ** <blockquote>
7702       ** 
7703       ** <i>Tip:</i> If the <tt>${</tt> is not followed by a valid
7704       ** parameter name and then by <tt>}</tt>, the "invalid name",
7705       ** along with the original delimiters, passes through
7706       ** literally into the final hovertext (no exception is
7707       ** thrown). So, if you see keywords in your hovertext, it
7708       ** probably means you misspelled a keyword (e.g.  you entered
7709       ** <tt>${piesliceSize}</tt> instead of
7710       ** <tt>${pieSliceSize}</tt>), forgot the closing <tt>}</tt>,
7711       ** began a user-defined parameter name with a digit, and so on.
7712       ** 
7713       ** </blockquote>
7714       ** 
7715       ** 
7716       ** <p>
7717       **
7718       ** The default hovertext template, used automatically if
7719       ** hovertext template is <tt>null</tt>, is
7720       ** <tt>DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE</tt> for pie slice
7721       ** type symbols and <tt>DEFAULT_HOVERTEXT_TEMPLATE</tt> for
7722       ** all other symbol types.
7723       ** 
7724       ** <blockquote>
7725       ** <i>Tip:</i>If you notice unexplained whitespace to the right
7726       ** or below your pages, that comes and goes as you hover
7727       ** over the chart, it could be due to an overly large
7728       ** default estimate for the hidden "bounding box" GChart uses
7729       ** to properly center hover annotations. You can use the
7730       ** {@link #setHoverWidget setHoverWidget} method, with a <tt>null</tt>
7731       ** first widget parameter, to override these defaults
7732       ** and correct this problem. 
7733       ** </blockquote>
7734       ** <p>
7735       ** 
7736       ** @param hovertextTemplate defines the hoverText to display when the mouse
7737       **   moves over a point on this curve, with <tt>${x}</tt>,
7738       **   <tt>${y}</tt> and
7739       **   <tt>${pieSliceSize}</tt> parameters replaced as described above, and
7740       **   custom parameters replaced as defined by the parent
7741       **   GChart's <tt>HoverParameterInterpreter</tt>.
7742       **
7743       ** @see #getHovertextTemplate getHovertextTemplate
7744       ** @see Curve.Point#getHovertext getHovertext  
7745       ** @see HoverParameterInterpreter HoverParameterInterpreter
7746       ** @see GChart#setHoverParameterInterpreter setHoverParameterInterpreter
7747       ** @see HoverUpdateable HoverUpdateable
7748       ** @see GChart.Curve.Point#setAnnotationText setAnnotationText
7749       ** @see #DEFAULT_HOVERTEXT_TEMPLATE DEFAULT_HOVERTEXT_TEMPLATE
7750       ** @see #DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
7751       **   DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
7752        **/ 
7753           public void setHovertextTemplate(String hovertextTemplate) {
7754              if (this.hovertextTemplate != hovertextTemplate)
7755                 hovertextChunks = null; // invalidates prev chunk-parse
7756              this.hovertextTemplate = hovertextTemplate;
7757           }
7758     
7759        /**
7760         * Specifies a <tt>HoverUpdateable</tt> widget that will be
7761         * used to display the hover annotations associated with this
7762         * symbol. If <tt>null</tt>, GChart's built-in,
7763         * <tt>setHovertextTemplate</tt>-based, text or HTML
7764         * based hover annotations will instead be used.  <p>
7765         *
7766         * Whenever the rectangular "brush" centered on the current
7767         * mouse position "touches" a point on this symbol's parent
7768         * curve, GChart will first call the <tt>hoverUpdate</tt>
7769         * method of this "hover-widget", and then position it
7770         * appropriately relative to the touched point. Most
7771         * applications will want to implement <tt>hoverUpdate</tt> so as to
7772         * populate the hover widget with detailed information about
7773         * the touched point. For example, to emulate GChart's
7774         * default hover feedback, you could extend an <tt>HTML</tt>
7775         * widget and, within the <tt>hoverUpdate</tt> method, use
7776         * the <tt>setHTML</tt> method to set the widget's HTML to
7777         * the expanded hover text returned by
7778         * <tt>hoveredOverPoint.getHovertext()</tt>.  <p>
7779         *
7780         * The exact position of the hover widget relative to the
7781         * touched point is defined by the companion methods,
7782         * <tt>setHoverLocation</tt>,
7783         * <tt>setHoverAnnotationSymbolType</tt>,
7784         * <tt>setHoverXShift</tt>, and <tt>setHoverYShift</tt>. 
7785         *
7786         * 
7787         * @param hoverWidget a <tt>Widget</tt> that
7788         * implements the <tt>HoverUpdateable</tt> interface that GChart will
7789         * use when generating this symbol's widget-based hover annotations, or
7790         * <tt>null</tt> to use GChart's text or HTML based hover
7791         * annotations (the other two parameters can still be used
7792         * to specify upper-bounds on the width and height of this
7793         * default hover text).
7794         *
7795         *  @param widthUpperBound an upper bound on the width of
7796         *  the widget (or default hover annotation) in pixels.
7797         *  Use GChart.NAI to get the GChart-determined default. 
7798         *  
7799         *  @param heightUpperBound an upper bound on the height of the
7800         *  widget (or default hover annotation) in pixels. Use GChart.NAI
7801         *  to get the GChart-determined default.
7802         *
7803         * @see #getHoverWidget getHoverWidget
7804         * @see Curve.Point#getHovertext getHovertext
7805         * @see HoverUpdateable HoverUpdateable
7806         * @see #setHoverFontWeight setHoverFontWeight  
7807         * @see #setHoverFontColor setHoverFontColor
7808         * @see #setHoverFontStyle setHoverFontStyle
7809         * @see #setHoverFontSize setHoverFontSize
7810         * @see #setHoverLocation setHoverLocation
7811         * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
7812         * @see #setHovertextTemplate setHovertextTemplate
7813         * @see #setHoverXShift setHoverXShift
7814         * @see #setHoverYShift setHoverYShift
7815         * 
7816         */
7817           public void setHoverWidget(HoverUpdateable hoverWidget,
7818                                      int widthUpperBound,
7819                                      int heightUpperBound) {
7820           if (null != hoverWidget && !(hoverWidget instanceof Widget))
7821              throw new IllegalArgumentException(
7822                             "hoverWidget must either be null or a Widget.");
7823    
7824           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
7825           hoverAnnotation.setWidget((Widget) hoverWidget,
7826                                     widthUpperBound, heightUpperBound);       
7827    
7828        }
7829    
7830         /**
7831          * Specifies a <tt>HoverUpdateable</tt> widget that will be
7832          * used to display all hover annotations associated with this
7833          * symbol. If <tt>null</tt>, GChart's built-in,
7834          * <tt>setHovertextTemplate</tt>-based, text or HTML
7835          * based hover annotations will instead be used.  <p>
7836          * 
7837          * <p>
7838          * A convenience method equivalent to
7839          * <tt>setHoverWidget(hoverWidget, GChart.NAI, GChart.NAI)</tt>
7840          *
7841          *
7842          *  @param annotationWidget the GWT Widget that defines this
7843          *    point's hover-induced annotation, or <tt>null</tt> to use the
7844          *    default hover annotation, which is based on expanding the
7845          *    hover text template relative to the hovered over point.
7846          *
7847          * @see #setHoverWidget(HoverUpdateable,int,int)
7848          * setHoverWidget(HoverUpdateable,int,int)
7849          * @see #setHovertextTemplate setHovertextTemplate
7850          * @see Curve.Point#getHovertext getHovertext
7851          * @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
7852          * @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
7853          *
7854          */ 
7855         public void setHoverWidget(HoverUpdateable annotationWidget) {
7856            setHoverWidget(annotationWidget, DEFAULT_WIDGET_WIDTH_UPPERBOUND,
7857                              DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
7858         }
7859    
7860        /**
7861         * Specifies the number of pixels (along the x-axis) to
7862         * move this symbol's hover annotations from their default,
7863         * <tt>AnnotationLocation</tt>-defined, point-relative
7864         * locations.  
7865         * <p>
7866         *
7867         * Actual positional shifts are defined via the same
7868         * conventions as are used by <tt>setAnnotationXShift</tt>.
7869         * See that method for further details.
7870         *
7871         * @see #getHoverXShift getHoverXShift
7872         * @see GChart.Curve.Point#setAnnotationXShift setAnnotationXShift
7873         * @see #setHoverFontWeight setHoverFontWeight  
7874         * @see #setHoverFontColor setHoverFontColor
7875         * @see #setHoverFontStyle setHoverFontStyle
7876         * @see #setHoverFontSize setHoverFontSize
7877         * @see #setHoverLocation setHoverLocation
7878         * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
7879         * @see #setHovertextTemplate setHovertextTemplate
7880         * @see #setHoverWidget setHoverWidget
7881         * @see #setHoverYShift setHoverYShift
7882         *
7883         */
7884        public void setHoverXShift(int xShift) {
7885           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
7886           hoverAnnotation.setXShift(xShift);
7887        }
7888    
7889        /**
7890         * Specifies the number of pixels (along the y-axis) to
7891         * move this symbol's hover annotations from their default,
7892         * <tt>AnnotationLocation</tt>-defined, point-relative
7893         * locations.  
7894         * <p>
7895         *
7896         * Actual positional shifts are defined via the same
7897         * conventions as are used by <tt>setAnnotationYShift</tt>.
7898         * See that method for further details.
7899         *
7900         * @see #getHoverYShift getHoverYShift
7901         * @see GChart.Curve.Point#setAnnotationYShift setAnnotationYShift
7902         * @see #setHoverFontWeight setHoverFontWeight  
7903         * @see #setHoverFontColor setHoverFontColor
7904         * @see #setHoverFontStyle setHoverFontStyle
7905         * @see #setHoverFontSize setHoverFontSize
7906         * @see #setHoverLocation setHoverLocation
7907         * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
7908         * @see #setHovertextTemplate setHovertextTemplate
7909         * @see #setHoverWidget setHoverWidget
7910         * @see #setHoverXShift setHoverXShift
7911         *
7912         */
7913        public void setHoverYShift(int yShift) {
7914           if (hoverAnnotation == null) hoverAnnotation = new Annotation();
7915           hoverAnnotation.setYShift(yShift);
7916        }
7917        
7918    
7919         /**
7920          * Specifies the URL that will define the image
7921          * used to represent the points on this curve.
7922          * 
7923          * <p>
7924          *
7925          * Specify <tt>null</tt> to use the URL returned by
7926          * <tt>getBlankImageURL</tt> (this is the default,
7927          * and gives you a blank 1x1 pixel GIF).
7928          * <p>
7929          *
7930          * Most applications will do just fine with the default.
7931          * However, this method lets you replace the default,
7932          * rectangular, chart symbols with custom images (e.g. a
7933          * five pointed star) or even a Google Chart API url to use
7934          * tiny 3-D pie charts for each point symbol (it
7935          * looks a bit strange, and your chart will no longer
7936          * be strictly client-side any more, but it does work).
7937          * <p>
7938          *
7939          * Note that if the symbol's width and height are bigger or
7940          * smaller than the specified image, the image will be
7941          * stretched to fit the symbol's size. Except for single
7942          * pixel images and such, this does not usually look that
7943          * great, so exactly matching up the symbol and image size
7944          * is often best.
7945          * <p>
7946          *
7947          * <small> <i>Tip:</i> By using a single pixel high or wide image
7948          * whose other dimension exactly matches the width or height of the
7949          * symbol, the image will stretch to produce a regular pattern of
7950          * horizontal or vertical lines. For example, this technique can be
7951          * used to produce a vertical or horizontal gradient effect in a
7952          * horizontal or vertical bar chart.  </small>
7953          * 
7954          * <p>
7955          * 
7956          * Be aware that GChart was originally designed with only
7957          * blank image URL's in mind, so it may take some effort to
7958          * adjust other settings (such as symbol type, width,
7959          * height, background color, border color, various legend
7960          * related settings, and curve order) so that the overall
7961          * chart looks right with your custom images for the curve
7962          * symbols. In particular, the legend icons are just
7963          * scaled-down versions of the image, which often doesn't
7964          * look that great.
7965          * <p>
7966          *
7967          * A alternative that gives you more control (but is less
7968          * efficient) is to use <tt>SymbolType.NONE</tt> with
7969          * <tt>setAnnotationWidget</tt> (or
7970          * <tt>setAnnotationText</tt>) and
7971          * <tt>setAnnotationLocation(AnnotationLocation.CENTER)</tt>
7972          * to use separate, centered, widget-based (or HTML based)
7973          * annotations in lieu of each point's image-based symbol.
7974          * 
7975          * 
7976          * @see #getImageURL getImageURL
7977          * @see #setBlankImageURL setBlankImageURL
7978          * @see GChart#setPlotAreaImageURL setPlotAreaImageURL
7979          * @see Curve.Point#setAnnotationWidget setAnnotationWidget
7980          * @see Curve.Point#setAnnotationText setAnnotationText
7981          * @see Curve.Point#setAnnotationLocation setAnnotationLocation
7982          * @see Symbol#setSymbolType setSymbolType
7983          * @see SymbolType#NONE SymbolType.NONE
7984          * 
7985          * @param imageURL the url that defines the
7986          * image within all the rectangular elements used to draw
7987          * this symbol on the chart, or 
7988          * <tt>null</tt> to revert to GChart's default (a 1x1 transparent
7989          * blank GIF).
7990          * 
7991          */ 
7992         public void setImageURL(String imageURL) {
7993            this.imageURL = imageURL;
7994         }
7995         
7996      /**
7997       ** Sets the height of this symbol (including any specified border)
7998       ** in pixels.
7999       ** <p>
8000       ** 
8001       ** Symbols for drawing vertical bars and symbols defining
8002       ** vertical lines between points or across the entire chart,
8003       ** compute their heights automatically based on curve data,
8004       ** axes limits, specified baselines, etc. These symbols, namely
8005       ** <tt>XGRIDLINE</tt> and all those whose names begin with
8006       ** <tt>VBAR_</tt> will ignore this height setting.
8007       ** 
8008       ** <p>
8009       ** @param height height of this symbol, in pixels.
8010       ** 
8011       ** @see #getHeight getHeight
8012       **/
8013         public void setHeight(int height) {
8014            getParent().invalidate();
8015            this.height = height;
8016            this.modelHeight = Double.NaN;
8017         }
8018      /**
8019       ** Sets the height of this symbol (including any specified border)
8020       ** in model units (arbitrary, user-defined, units). Model
8021       ** units are the same units in which the points on the
8022       ** chart are specified and charted.
8023       ** <p>
8024       ** 
8025       ** Specification of the modelHeight undefines (that is, sets
8026       ** to <tt>GChart.NAI</tt>) any previous pixel-based
8027       ** specification made via <tt>setHeight</tt>.
8028       ** 
8029       ** <p> Symbols for drawing vertical bars and symbols defining
8030       ** vertical lines between points or across the entire chart,
8031       ** compute their heights automatically based on curve data,
8032       ** axes limits, specified baselines, etc. These symbols, namely
8033       ** <tt>XGRIDLINE</tt> and all those whose names begin with
8034       ** <tt>VBAR_</tt> will ignore this height setting.
8035       ** 
8036       ** <p>
8037       ** @param modelHeight height of this symbol, in model units
8038       ** 
8039       ** @see #getModelHeight getModelHeight
8040       ** @see #setHeight setHeight
8041       ** @see #setModelWidth setModelWidth
8042       ** @see #setWidth setWidth
8043       **/
8044         public void setModelHeight(double modelHeight) {
8045            getParent().invalidate();
8046            this.modelHeight = modelHeight;
8047         }
8048      /**
8049       ** Sets the width of this symbol (including any specified border)
8050       ** in model units. Model units are an arbitrary, user-defined
8051       ** units system associated with the x,y coordinates of 
8052       ** points displayed on the chart.
8053       ** 
8054       ** <p> Specification of a symbol's model width undefines (that
8055       ** is, sets to <tt>GChart.NAI</tt>) any previous, pixel-based,
8056       ** width specification made via <tt>setWidth</tt>.  <p>
8057       ** 
8058       ** Symbols for drawing horizontal bars, and symbols defining
8059       ** horizontal lines between points or across the entire chart,
8060       ** compute their widths automatically based on curve data,
8061       ** axes limits, specified baseline, etc. These symbols,
8062       ** namely <tt>YGRIDLINE</tt> and all those whose
8063       ** names begin with <tt>HBAR_</tt> will ignore this width
8064       ** setting.
8065       ** 
8066       ** <p>
8067       ** @param modelWidth width of this symbol, in model units.
8068       ** 
8069       ** @see #setModelHeight setModelHeight
8070       ** @see #setWidth setWidth
8071       ** @see #setHeight setHeight
8072       ** 
8073       **/
8074         public void setModelWidth(double modelWidth) {
8075            getParent().invalidate();
8076            this.modelWidth = modelWidth;
8077         }
8078    
8079        /**
8080         ** Specifies a value that defines the angular orientation of
8081         ** the first edge of the pie slice associated with this
8082         ** symbol.  (An additional clockwise rotation as defined by
8083         ** <tt>setPieSliceSize</tt> defines the angular orientation
8084         ** of the second edge of the pie slice).
8085         **
8086         ** <p> When specified explicitly, the value must be a
8087         ** fraction >= 0 and < 1, with 0 representing due south,
8088         ** 0.25 an additional clockwise angular rotation (starting
8089         ** at due south) that is 25% of the full, 360 degree
8090         ** rotation (and thus, if you can follow these gyrations, is
8091         ** due west), 0.5 representing a 50% clockwise angular
8092         ** rotation from due south (thus, due north), .75 a 75%
8093         ** clockwise rotation (and thus, due east), etc.
8094         **
8095         ** <p> If the specially recognized value,
8096         ** <tt>Double.NaN</tt>, is specified, orientation is
8097         ** chosen so as to make this slice appear adjacent to
8098         ** the previous slice, (assuming it has the same x,y
8099         ** as the previous slice and is thus part of the same
8100         ** pie figure). If this symbol/point represents the
8101         ** very first pie slice, <tt>Double.NaN</tt>
8102         ** causes the slice to be oriented as specified via
8103         ** the <tt>setInitialPieSliceOrientation</tt> method
8104         ** (by default, that's due south).
8105         **
8106         ** Note that though this value can be set regardless of the
8107         ** symbol's <tt>SymbolType</tt>, it only has an impact on
8108         ** how the symbol is rendered if the symbol has one of the
8109         ** pie slice symbol types (e.g.
8110         ** <tt>PIE_SLICE_VERTICAL_SHADING</tt>).
8111         ** 
8112         ** @param pieSliceOrientation angle at which first edge of pie
8113         ** slice appears, expressed as a fraction of a full
8114         ** 360 degree (2*Pi radians) clockwise rotation from an initial due
8115         ** south position (the 6 o'clock position) required to reach the first
8116         ** edge of the pie slice. 
8117         **
8118         ** 
8119         **
8120         ** @see #getPieSliceOrientation getPieSliceOrientation
8121         ** @see #setPieSliceSize setPieSliceSize
8122         ** @see GChart#setInitialPieSliceOrientation setInitialPieSliceOrientation
8123         ** 
8124          */
8125         public void setPieSliceOrientation(
8126              double pieSliceOrientation) {
8127           invalidateDependentSlices(getCurveIndex(getParent())); 
8128           if (pieSliceOrientation!=Double.NaN &&
8129               (pieSliceOrientation < 0 || pieSliceOrientation >=1))
8130             throw new IllegalArgumentException(
8131              "pieSliceOrientation="+pieSliceOrientation+"; "+
8132              "pieSliceOrientation must be >=0 and < 1, or else " +
8133              "equal to Double.NaN.");
8134           this.pieSliceOrientation = pieSliceOrientation;
8135         }
8136    
8137        /**
8138         ** Specifies a value that defines the angular size of
8139         ** any pie slice associated with this symbol.
8140         **
8141         ** <p> This must be value between 0 and 1.  0.25 represents
8142         ** a quarter pie slice, 0.5 a half pie, 1 a full pie, etc.
8143         **
8144         ** <p><i>Note:</i> To create a complete pie, you must arrange
8145         ** things so that the sum of all of your pie slice sizes adds up to
8146         ** exactly 1.0. If they sum to more than 1, some slices will cover
8147         ** up others; it they sum to less, your pie will have missing
8148         ** slices. You can assure you get a full pie, regardless of the
8149         ** original slice sizes by normalizing your slice sizes.
8150         ** Specifically, divide each original slice size by the sum over
8151         ** all of the original slice sizes.  For example, if the original
8152         ** slice sizes were 1, 2, and 2 you could divide them by their sum
8153         ** (1 + 2 + 2 = 5) to obtain normalized slice sizes of 0.2, 0.4,
8154         ** and 0.4.  <p>
8155         ** 
8156         ** Note that though this value can be set regardless of the
8157         ** symbol's current <tt>SymbolType</tt>, it only has an
8158         ** impact on how the symbol is rendered if the symbol has
8159         ** one of the pie slice symbol types (e.g.
8160         ** <tt>PIE_SLICE_VERTICAL_SHADING</tt>).
8161         ** 
8162         ** @param pieSliceSize Fraction of a full pie subtended by
8163         **  this particular pie slice. Must be between 0 and 1,
8164         **  inclusive.
8165         **
8166         ** @see #getPieSliceSize getPieSliceSize
8167         ** @see #setPieSliceOrientation setPieSliceOrientation
8168         ** 
8169          */
8170         public void setPieSliceSize(
8171              double pieSliceSize) {
8172           invalidateDependentSlices(getCurveIndex(getParent())); 
8173           if (!withinRange(pieSliceSize,0,1))
8174             throw new IllegalArgumentException(
8175              "pieSliceSize="+pieSliceSize+"; the requirement: "+
8176              "0.0 <= pieSliceSize <= 1.0 must be satisfied.");
8177           this.pieSliceSize = pieSliceSize;
8178         }
8179    
8180      /**
8181       ** Sets the type of this symbol.
8182       ** <p>
8183       **
8184       ** <i>Note:</i> The special mouse related symbol types (those with
8185       ** names that begin with <tt>ANCHOR_MOUSE</tt>) are intended for use
8186       ** in positioning hover popup annotations (via
8187       ** <tt>setHoverAnnotationSymbolType</tt>). They are not expected to be
8188       ** useful, and could potentially cause confusion, if used as the
8189       ** symbol types of ordinary curves.
8190       **
8191       ** @param symbolType the new symbol type for this symbol.
8192       ** @see SymbolType SymbolType
8193       ** @see SymbolType#ANCHOR_MOUSE ANCHOR_MOUSE
8194       ** @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
8195       ** 
8196       */
8197         public void setSymbolType(SymbolType symbolType) {
8198    //      throwExceptionOnAnchorMouse(symbolType);  
8199          getParent().invalidate();
8200          // will invalidate dependent slices if it was previously a pie slice  
8201          invalidateDependentSlices(getCurveIndex(getParent()));  
8202          this.symbolType = symbolType;
8203          // will invalidate dependent slices if it is now a pie slice 
8204          invalidateDependentSlices(getCurveIndex(getParent()));  
8205       }
8206    
8207     
8208      /**
8209       ** Sets the width of this symbol (including any specified border)
8210       ** in pixels.
8211       ** <p>
8212       ** 
8213       ** Symbols for drawing horizontal bars, and symbols defining
8214       ** horizontal lines between points or across the entire chart,
8215       ** compute their widths automatically based on curve data,
8216       ** axes limits, specified baseline, etc. These symbols, namely
8217       ** <tt>YGRIDLINE</tt> and all those whose names begin with
8218       ** <tt>HBAR_</tt> will ignore this width setting.
8219       ** 
8220       ** <p>
8221       ** @param width width of this symbol, in pixels
8222       ** 
8223       ** @see #setHeight setHeight
8224       ** 
8225       **/
8226         public void setWidth(int width) {
8227           getParent().invalidate();
8228           this.width = width;
8229           this.modelWidth = Double.NaN;
8230         }
8231    
8232         
8233         /*
8234          * Copies properties of the "from" symbol to this symbol.
8235          *
8236          * This isn't a generic copy, but is used only when copying
8237          * the properties of the hovered-over curve into the
8238          * system curves used to render the selection feedback and
8239          * hover annotations (it contains some special logic needed
8240          * only in that context).
8241          *
8242          */
8243         void copy(Symbol from) {
8244           setBackgroundColor(from.getBackgroundColor());
8245           setBaseline(from.getBaseline());
8246           setBorderColor(from.getBorderColor());
8247           setBorderStyle(from.getBorderStyle());
8248           setBorderWidth(from.getBorderWidth());
8249           setFillSpacing(from.getFillSpacing());
8250           setFillThickness(from.getFillThickness());
8251    //       setHoverAnnotationEnabled(from.getHoverAnnotationEnabled());
8252           setHoverFontColor(from.getHoverFontColor());
8253           setHoverFontSize(from.getHoverFontSize());
8254           setHoverFontStyle(from.getHoverFontStyle());
8255           setHoverFontWeight(from.getHoverFontWeight());
8256           setHoverLocation(from.getHoverLocation());
8257           setHoverAnnotationSymbolType(from.getHoverAnnotationSymbolType());
8258           setHoverSelectionBackgroundColor(from.getHoverSelectionBackgroundColor()); 
8259           setHoverSelectionBorderColor(from.getHoverSelectionBorderColor()); 
8260           setHoverSelectionBorderStyle(from.getHoverSelectionBorderStyle()); 
8261           setHoverSelectionBorderWidth(from.getHoverSelectionBorderWidth());
8262    //       setHoverSelectionEnabled(from.getHoverSelectionEnabled());
8263           setHovertextTemplate(from.getHovertextTemplate());
8264           setHoverWidget(from.getHoverWidget());
8265           setHoverXShift(from.getHoverXShift());
8266           setHoverYShift(from.getHoverYShift());
8267           setImageURL(from.getImageURL());
8268           // Model and pixel variants of width/height actually
8269           // represent a single underlying property (setting one,
8270           // unsets the other, etc.). Logic below reflects this.
8271           if (!Double.isNaN(from.getModelHeight()))
8272              setModelHeight(from.getModelHeight());
8273           else 
8274              setHeight(from.getHeight());
8275           if (!Double.isNaN(from.getModelWidth()))
8276              setModelWidth(from.getModelWidth());
8277           else 
8278              setWidth(from.getWidth());
8279    
8280           setPieSliceOrientation(from.getPieSliceOrientation());
8281           setDefaultPieSliceOrientation(from.getDefaultPieSliceOrientation());
8282           setPieSliceSize(from.getPieSliceSize());
8283           setSymbolType(from.getSymbolType());
8284    
8285         }
8286         Annotation getAnnotation() {
8287           if (annotation == null) annotation = new Annotation();
8288           return annotation;
8289         }
8290    
8291         // Pixel height of symbol when rendered on given plot panel
8292         double getHeight(PlotPanel pp, boolean onY2) {
8293            double result;
8294            double mH = getModelHeight();
8295            if ((mH!=mH)) // x!=x is a faster isNaN
8296                result = getHeight();
8297            else 
8298               result = pp.dyToPixel(mH,onY2);
8299    
8300            return result;
8301         }
8302         // Pixel width of symbol when rendered on given plot panel
8303         double getWidth(PlotPanel pp) {
8304            double result;
8305            double mW = getModelWidth();
8306            if ((mW!=mW)) // x!=x is a faster isNaN
8307                result = getWidth();
8308            else 
8309               result = pp.dxToPixel(mW);
8310    
8311            return result;
8312         }
8313    
8314         
8315         /* Renders the symbol at the specified position within the
8316            plot panel, by creating appropriately positioned Image
8317            and Grid (for any Annotation associated with this symbol
8318            rendering) objects within the panel.  So-rendered symbols
8319            are used to represent: each point on a curve with any
8320            associated point annotations, axes, gridlines, ticks and
8321            their tick-labels. */
8322         
8323         void realizeSymbol(PlotPanel pp,
8324                            GraphicsRenderingPanel grp,
8325                            AnnotationRenderingPanel arp,
8326                            Annotation annotation,
8327                            boolean onY2,
8328                            boolean clipPlotArea,
8329                            boolean clipDecoratedChart,
8330                            boolean drawMainSymbol,
8331                            double x, double y, 
8332                            double prevX, double prevY,
8333                            double nextX, double nextY) {
8334           getSymbolType().realizeSymbol(pp, grp, arp, this, annotation,
8335                                         onY2,
8336                                         clipPlotArea,
8337                                         clipDecoratedChart,
8338                                         drawMainSymbol,
8339                                         x, y,
8340                                         prevX, prevY, nextX, nextY);
8341         
8342         }
8343    
8344    
8345    
8346    
8347         
8348       } // end of class Symbol
8349       
8350      private static double lastPieSliceOrientation;
8351      static double getLastPieSliceOrientation() {
8352        return lastPieSliceOrientation;
8353      }
8354      static void setLastPieSliceOrientation(double lastOrientation) {
8355         lastPieSliceOrientation = lastOrientation%1.0;
8356      }
8357      private double initialPieSliceOrientation;
8358    
8359      /** Sets the default initial orientation for pie slices.
8360       **
8361       ** The default initial orientation is used as the first pie
8362       ** slice's first edge's orientation setting only if the symbol associated
8363       ** with that pie slice has the default, undefined, orientation
8364       ** setting of <tt>Double.NaN</tt>. 
8365       ** <p>
8366       ** The default value of this setting is 0, which corresponds
8367       ** to due south (6 o'clock). The value specifies the
8368       ** fraction of a complete clockwise rotation, beginning
8369       ** at due south required to reach the first edge of the
8370       ** pie slice.
8371       **
8372       ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
8373       ** 
8374       ** @param orientation the orientation to use for the first
8375       **   edge of the first pie slice in this GChart, in cases
8376       **   in which that first pie slice's orientation is undefined
8377       **   (<tt>Double.NaN</tt>).
8378       **/
8379      
8380      public void setInitialPieSliceOrientation(double orientation) {
8381         if (orientation < 0 || orientation >=1)
8382             throw new IllegalArgumentException(
8383              "orientation="+orientation+"; "+
8384              "orientation must be >=0 and < 1.");
8385         this.initialPieSliceOrientation = orientation;
8386         invalidateAllSlices(); 
8387      }
8388    
8389      /**
8390       ** Returns a previously specified initial pie slice orientation.
8391       **
8392       ** @return the fraction of a clockwise rotation, beginning
8393       **   from the 6 o'clock postion, needed to reach the default
8394       **   initial pie slice orientation.
8395       **
8396       ** @see #setInitialPieSliceOrientation
8397       **   setInitialPieSliceOrientation
8398       **/
8399      public double getInitialPieSliceOrientation() {
8400        return initialPieSliceOrientation;
8401      }
8402    
8403       /**
8404        ** Specifies the type of symbol used by a curve. GChart
8405        ** includes a <tt>LINE</tt> symbol type (suitable for solidly
8406        ** connected line charts), various "box" symbol types
8407        ** (suitable for scatter and dotted-line charts),
8408        ** horizontal and vertical bars that extend to axis limits
8409        ** or a specified baseline (suitable for bar and area charts),
8410        ** and pie slices (suitable for pie charts) in these
8411        ** symbol types. Thus, choosing a curve's symbol type has a
8412        ** bigger impact on the kind of chart you create than in
8413        ** other charting APIs you may have used.
8414        **
8415        ** <p> One advantage of this symbol type based approach: you
8416        ** can place multiple pies, lines and/or bars on a single
8417        ** chart simply by creating multiple curves whose associated
8418        ** symbols have appropriately different symbol types.
8419        ** 
8420        ** <p> Note that, for line, area, or pie charts, the exact
8421        ** look of the non-rectangular aspects (connecting lines,
8422        ** filled-in areas, etc.) of these symbols in the chart is
8423        ** largely governed by the host <tt>Symbol</tt>'s
8424        ** <tt>fillSpacing</tt> and <tt>fillThickness</tt>
8425        ** properties.
8426        ** 
8427        ** <p> For instance, with the default <tt>fillThickness</tt>
8428        ** of 0 for the <tt>BOX_CENTER</tt> symbol, curves display
8429        ** only explicitly specified data points, without any
8430        ** connecting lines between them. But, if you set
8431        ** <tt>fillThickness</tt> to 1, GChart interpolates a series
8432        ** of 1 pixel by 1 pixel rectangular "dots" between successive
8433        ** data points, with an intra-dot spacing defined by the
8434        ** symbol's <tt>fillSpacing</tt> setting.  <p>
8435        **
8436        ** Since v2.5, a new <tt>fillSpacing==0</tt> setting, with the
8437        ** special meaning of "continuous filling", is allowed. If an
8438        ** external canvas library has been plugged into GChart via
8439        ** <tt>setCanvasFactory</tt>,
8440        ** higher quality, continuously filled pie, line, and area charts
8441        ** can be produced via the combination: <tt>fillSpacing==0</tt> and
8442        ** <tt>fillThickness > 0</tt> along with one of the pie, line, or
8443        ** bar symbol types described below.<p>
8444        ** 
8445        ** You must select each curve's symbol type from the predefined
8446        ** set of supported types listed in the "Field Summary"
8447        ** section below. The default symbol type is <tt>BOX_CENTER</tt>.
8448        **
8449        ** @see Curve#getSymbol getSymbol
8450        ** @see Symbol#setSymbolType setSymbolType
8451        ** @see Symbol#setFillSpacing setFillSpacing
8452        ** @see Symbol#setFillThickness setFillThickness
8453        ** @see Symbol Symbol
8454        ** 
8455        **/ 
8456       public static class SymbolType {
8457    
8458          /*
8459           * For efficiency during hit testing, points get separated
8460           * into bins associated with adjacent vertical
8461           * (or horizontal) bands that cover the plot area.
8462           * <p>
8463           * 
8464           * Subclasses (such as those for producing horizontal bar
8465           * charts) whose rendered symbols do not have a fixed width
8466           * across all the points on a single curve, MUST set this
8467           * field to true within their constructors, because
8468           * the hit testing approach assumes fixed "thickness"
8469           * symbols for simplicity/efficiency.
8470           * <p>
8471           * 
8472           * Subclasses that have both a fixed width and height MAY
8473           * set this to true if they are typically used in a way
8474           * that tends to make horizontal banding a better (= tends
8475           * to place same # of points in each band) binning strategy.
8476           *
8477           * <p>
8478           *
8479           * If <tt>null</tt>, GChart uses a simple heuristic that assumes
8480           * that a brush that is wider than high implies developer is
8481           * trying to let user distinguish finer y differences, and thus
8482           * our bands should separate points more finely (and hence allow
8483           * for faster band-indexed hit testing) if we use horizontal
8484           * banding in this case (and vertical otherwise).
8485           * <p>
8486           * 
8487           * See the <tt>bandSeparatePoints</tt> method for more info.
8488           * 
8489           */
8490          Boolean isHorizontallyBanded = null;
8491          // Use smallest min band size, since I expect per band
8492          // cost to be small compared to per-point hit testing.
8493          protected final int MIN_BAND_SIZE = 1;  
8494    //      Boolean isHorizontallyBanded() {return isHorizontallyBanded; }
8495    
8496         /* Thickness (in pixels) of hit-test-bands used with this
8497          * symbol type.
8498          * <p>
8499          * 
8500          * Gets overriden for pie slice symbol types, which base
8501          * thickness on pie diameter.
8502          */ 
8503          protected double getBandThickness(PlotPanel pp,
8504                                            Symbol sym, boolean onY2) {
8505            double result;
8506            if (sym.isHorizontallyBanded()) 
8507               result = Math.max(MIN_BAND_SIZE,
8508                                sym.getHeight(pp, onY2));
8509            else 
8510               result = Math.max(MIN_BAND_SIZE, sym.getWidth(pp));
8511            return result;
8512          }
8513    
8514          // is overridden by pie slices, which use a different brush shape
8515          protected int getBrushHeight(Symbol sym) {
8516             int result = sym.getBrushHeight();
8517             return result;
8518          }
8519          // again, so pie slices can override
8520          protected AnnotationLocation getBrushLocation(Symbol sym) {
8521             AnnotationLocation result = sym.getBrushLocation();
8522             return result;
8523          }
8524          // is overridden by pie slices, which use a different brush shape
8525          protected int getBrushWidth(Symbol sym) {
8526             int result = sym.getBrushWidth();
8527             return result;
8528          }
8529         /*
8530          * This symbol type provides a convenient anchor point at
8531          * one of the standard 9 named positions within the plot
8532          * area.  The actual x,y of the points using this symbol
8533          * type is ignored.  Useful for placing annotations around
8534          * and along the perimeter of the plot panel.<p>
8535          *
8536          * For example, chart decorations such as axis labels and
8537          * footnotes internally use symbols of this type (with
8538          * appropriate setAnnotationXShift or setAnnotationYShift
8539          * adjustments to position the decoration appropriately
8540          * relative to the anchor point). End-users can use a curve
8541          * with this symbol type, along with a single point and
8542          * appropriate widget-based annotation, to place a table in
8543          * the upper left corner of the plot area, etc.
8544          * 
8545          */
8546    
8547         private static class AnnotationAnchor extends SymbolType {
8548            AnnotationLocation location;
8549            AnnotationAnchor(AnnotationLocation location) {
8550              super(0, 0, 0, 0, 0, 0);
8551              this.location = location;
8552            }
8553            // actual curve symbol zero-sized so it does not
8554            // appear--it's just for positioning the annotation.
8555            public double getAdjustedWidth(double width, 
8556                                            double x,
8557                                            double xPrev, double xNext,
8558                                            double xMin, double xMax,
8559                                            double xMid) {
8560                return 0;
8561            }
8562             public double getAdjustedHeight(double height, double y,
8563                                          double yPrev, double yNext,
8564                                             double yMin, double yMax,double yMid) {
8565               return 0;
8566             }
8567             
8568             // Just return one of the standard 9 positions, or the mouse
8569             // coordinates, based on the location defined in the
8570             // constructor.
8571            double getUpperLeftX(double width, double x,
8572                                 double xPrev, double xNext,
8573                                 double xMin, double xMax, double xMid,
8574                                 int xMouse) {
8575               double result;
8576               if (AnnotationLocation.AT_THE_MOUSE == location)
8577                  result = (GChart.NAI == xMouse) ? Double.NaN : xMouse;
8578               else if (AnnotationLocation.AT_THE_MOUSE_SNAP_TO_X == location)
8579                  result = (GChart.NAI == xMouse) ? Double.NaN : x;
8580               else if (AnnotationLocation.AT_THE_MOUSE_SNAP_TO_Y == location)
8581                  result = (GChart.NAI == xMouse) ? Double.NaN : xMouse;
8582               else if (AnnotationLocation.NORTHWEST == location ||
8583                   AnnotationLocation.WEST == location ||
8584                   AnnotationLocation.SOUTHWEST == location)
8585                  result = xMin;
8586               else if (AnnotationLocation.NORTHEAST == location ||
8587                        AnnotationLocation.EAST == location ||
8588                        AnnotationLocation.SOUTHEAST == location)
8589                  result = xMax;
8590               else // NORTH, CENTER, or SOUTH
8591                  result = (xMin + xMax)/2;
8592               
8593               return result;
8594               
8595          }        
8596             double getUpperLeftY(double height, double y,
8597                                  double yPrev, double yNext,
8598                                  double yMin, double yMax, double yMid,
8599                                  int yMouse)  {
8600               double result;
8601               if (AnnotationLocation.AT_THE_MOUSE == location)
8602                  result = (GChart.NAI == yMouse) ? Double.NaN : yMouse;
8603               else if (AnnotationLocation.AT_THE_MOUSE_SNAP_TO_X == location)
8604                  result = (GChart.NAI == yMouse) ? Double.NaN : yMouse;
8605               else if (AnnotationLocation.AT_THE_MOUSE_SNAP_TO_Y == location)
8606                  result = (GChart.NAI == yMouse) ? Double.NaN : y;
8607               else if (AnnotationLocation.NORTHWEST == location ||
8608                   AnnotationLocation.NORTH == location ||
8609                   AnnotationLocation.NORTHEAST == location)
8610                  result = yMin;
8611               else if (AnnotationLocation.SOUTHWEST == location ||
8612                        AnnotationLocation.SOUTH == location ||
8613                        AnnotationLocation.SOUTHEAST == location)
8614                  result = yMax;
8615               else // WEST, CENTER, or EAST
8616                  result = (yMin + yMax)/2;
8617               return result;
8618             }
8619         }
8620    
8621    
8622         private static class HBarBaseline extends SymbolType {
8623             HBarBaseline(int wm, int hm) {
8624                super(wm, hm, 0.5, 0.5, 0, 0,  Boolean.TRUE);
8625             }
8626             protected double defaultFillSpacing() {
8627               return DEFAULT_BAR_FILL_SPACING;
8628             }
8629             protected AnnotationLocation defaultHoverLocation() {
8630                return DEFAULT_HBAR_BASELINE_HOVER_LOCATION;
8631             }
8632             public double getAdjustedWidth(double width, 
8633                                            double x,
8634                                            double xPrev, double xNext,
8635                                            double xMin, double xMax,
8636                                            double xMid) {
8637                return x - xMid;
8638             }
8639    
8640          double getUpperLeftX(double width, double x,
8641                               double xPrev, double xNext,
8642                               double xMin, double xMax, double xMid,
8643                               int xMouse) {
8644             return xMid;
8645          }   
8646             
8647             
8648             int getIconHeight(int legendFontSize) {
8649                return (int) Math.round(legendFontSize/2.);
8650             }
8651             int getIconWidth(int legendFontSize) {
8652                return legendFontSize;
8653             }
8654    
8655         } // end of class HBarBaseline
8656         private static class HBarLeft extends SymbolType {
8657             HBarLeft(int wm, int hm) {
8658                 super(wm, hm, 0.5, 0.5, 0.5, 0.5,  Boolean.TRUE);
8659             }
8660             protected double defaultFillSpacing() {
8661               return DEFAULT_BAR_FILL_SPACING;
8662             }
8663             protected AnnotationLocation defaultHoverLocation() {
8664                return DEFAULT_HBARLEFT_HOVER_LOCATION;
8665             }
8666             public double getAdjustedWidth(double width, 
8667                                         double x,
8668                                         double xPrev, double xNext,
8669                                         double xMin, double xMax, double xMid) {
8670                return x - xMin;
8671             }
8672             int getIconHeight(int legendFontSize) {
8673                return (int) Math.round(legendFontSize/2.);
8674             }
8675             int getIconWidth(int legendFontSize) {
8676                return legendFontSize;
8677             }
8678    
8679         } // end of class HBarLeft
8680         
8681          private static class HBarRight extends SymbolType {
8682             HBarRight(int wm, int hm) {
8683                 super(wm, hm, 0.5, 0.5, 0.5, 0.5, Boolean.TRUE);
8684             }
8685             
8686             protected double defaultFillSpacing() {
8687               return DEFAULT_BAR_FILL_SPACING;
8688             }
8689             protected AnnotationLocation defaultHoverLocation() {
8690                return DEFAULT_HBARRIGHT_HOVER_LOCATION;
8691             }
8692             public double getAdjustedWidth(double width, double x,
8693                                            double xPrev, double xNext,
8694                                            double xMin, double xMax,
8695                                            double xMid) {
8696                return xMax - x;
8697             }
8698             int getIconHeight(int legendFontSize) {
8699                return (int) Math.round(legendFontSize/2.);
8700             }
8701             int getIconWidth(int legendFontSize) {
8702                return legendFontSize;
8703             }      
8704          } // end of class HBarRight
8705    
8706    
8707        // draws a connected straight line between successive points
8708        private static class LineSymbolType extends SymbolType {
8709             LineSymbolType() {
8710    // same constructor as BOX_CENTER, which centers line segments on
8711    // the points that they represent, as required.
8712                 super(0, 0, 0, 0, 0, 0);
8713             }
8714    
8715         // fillSpacing to use when a symbol's fillSpacing is
8716         // set to GChart.NAI (an undefined integer)
8717          protected double defaultFillSpacing() {
8718            return DEFAULT_LINE_FILL_SPACING;
8719          }
8720         // fillThickness to use when a symbol's fillThickness is
8721         // set to GChart.NAI (an undefined integer)
8722          protected int defaultFillThickness() {
8723            return DEFAULT_LINE_FILL_THICKNESS; 
8724          }
8725          int getIconHeight(int legendFontSize) {
8726                return 3; // leaves room for a 1px border and a center
8727          }
8728          int getIconWidth(int legendFontSize) {
8729              return Math.max(3, legendFontSize);
8730          }      
8731         /*
8732          * Draws an approximate line from x,y to nextX, nextY, using an
8733          * appropriate series of vertical (for a > 45 degree slope) or (for
8734          * a < 45 degree slope) horizontal line segments. If a GWT canvas
8735          * is available and if continuous fill (fillSpacing==0) was
8736          * requested the lineTo,stroke,etc. of the canvas Widget are
8737          * instead used to draw the line.
8738          * <p>
8739          * 
8740          * The canvas part of this code assumes/requires that points
8741          * on a curve are rendered sequentially, and that on the
8742          * first point on the curve <tt>prevX</tt> and
8743          * <tt>prevY</tt>, and on the last point <tt>nextX</tt> and
8744          * <tt>nextY</tt>, are undefined (Double.NaN)
8745          * 
8746          */
8747          
8748          void realizeSymbol(PlotPanel pp,
8749                             GraphicsRenderingPanel grp,
8750                             AnnotationRenderingPanel arp,
8751                             Symbol symbol,
8752                             Annotation annotation,
8753                             boolean onY2, 
8754                             boolean clipPlotArea,
8755                             boolean clipDecoratedChart,
8756                             boolean drawMainSymbol,
8757                             double x, double y, 
8758                             double prevX, double prevY,
8759                             double nextX, double nextY) {
8760    
8761           if ((x!=x) || (y!=y)) // this point undefined (isNaN)
8762              return;
8763           // else point itself is at least defined
8764           
8765           double spacing = symbol.getFillSpacing();
8766           int thickness = symbol.getFillThickness();
8767           GChartCanvasLite canvas = grp.getCanvas();
8768           
8769           if (0 == spacing && null != canvas && thickness > 0) {
8770              // when canvas is available and continuous fill requested,
8771              // BOX_CENTER and LINE work exactly the same way
8772              BOX_CENTER.realizeSymbol(
8773                 pp, grp, arp, symbol, annotation,
8774                 onY2,  clipPlotArea, clipDecoratedChart, drawMainSymbol,
8775                 x,  y, prevX,  prevY, nextX,  nextY);
8776              return;
8777           }
8778    
8779           double xPx = pp.xToPixel(x);
8780           double yPx = pp.yToPixel(y, onY2);
8781           double nextXPx = pp.xToPixel(nextX);
8782           double nextYPx = pp.yToPixel(nextY, onY2);
8783           
8784           if (nextX==nextX && nextY==nextY &&  // next point defined
8785                    thickness > 0 &&   // not a zero thickness connection 
8786                    (x!=nextX || y!=nextY) ) { // this/next point not overlayed
8787             // draw HTML-element rendered line segment
8788              
8789             // Continuous fill not supported; 1px is reasonable approx.
8790             if (0 == spacing) spacing = 1; 
8791             double deltaX = nextXPx - xPx;
8792             double deltaY = nextYPx - yPx;
8793             boolean dXIsShorter = deltaX*deltaX < deltaY*deltaY;
8794             // increasing width by 1 adds half px on each edge
8795             // to heal the occasional roundoff-induced gap
8796             final double EPS = 1;
8797    // TODO: the case in which the connecting line does not intersect the
8798    // plot area, and off-plot-area points are not being drawn is handled
8799    // very inefficiently, and not entirely correctly, by trying to draw
8800    // the entire line and excluding each segment as we attempt to draw it.
8801    // Need to compute intersecting sub-line-segment and just draw that
8802    // instead, ignoring lines with no intersecting segments completely. Can
8803    // make a huge difference with lots of off-chart points, such as a
8804    // deliberately narrowed x axis range.
8805             if (deltaX == 0) { // special-case of vertical line
8806    
8807                 realizeOneImageOfSymbol(pp, grp, arp, symbol, null, 
8808                                         onY2,
8809                                         clipPlotArea,
8810                                         clipDecoratedChart,
8811                                         xPx,
8812                                         0.5*(yPx+nextYPx),
8813                                         Double.NaN, Double.NaN,
8814                                         nextXPx, nextYPx,
8815                                         thickness,
8816                                         Math.abs(nextYPx - yPx)+EPS);
8817             }
8818             else if (deltaY == 0) { // special case of horizontal line
8819    
8820                 realizeOneImageOfSymbol(pp, grp, arp, symbol, null, 
8821                                          onY2, 
8822                                         clipPlotArea,
8823                                         clipDecoratedChart,
8824                                         0.5*(xPx+nextXPx),
8825                                         yPx,
8826                                         Double.NaN, Double.NaN,
8827                                         nextXPx, nextYPx,
8828                                         Math.abs(nextXPx - xPx)+EPS,
8829                                         thickness);
8830             }
8831             else if (dXIsShorter) { // series of vertical segments
8832               double xMin = (xPx < nextXPx) ? xPx : nextXPx;
8833               double xMax = (xPx > nextXPx) ? xPx : nextXPx;
8834               double yAtXMin = (xPx < nextXPx) ? yPx : nextYPx;
8835               double yAtXMax =(xPx > nextXPx) ? yPx : nextYPx;
8836    
8837               double xiPrev = xMin;
8838               double yiPrev = yAtXMin;
8839               double xi = xiPrev;
8840               double yi = yiPrev;
8841               // round up to err on side of providing more detail
8842               int N = (int) Math.ceil((xMax-xMin)/spacing);
8843               double dy = Math.abs((yAtXMax - yAtXMin)/N)+EPS;
8844               for (int i = 1; i <= N; i++) {
8845                 xi = xMin + i*(xMax - xMin)/N;
8846                 yi = yAtXMin + i * (yAtXMax - yAtXMin)/N;
8847                 realizeOneImageOfSymbol(pp, grp, arp, symbol, null, 
8848                        onY2, 
8849                        clipPlotArea,
8850                        clipDecoratedChart,
8851                        0.5*(xiPrev+xi), 0.5*(yiPrev+yi),
8852                        Double.NaN, Double.NaN,
8853                        nextXPx, nextYPx,
8854                        thickness, dy);
8855                 xiPrev = xi;
8856                 yiPrev = yi;
8857                }
8858             }
8859             else { // dY is shorter. Series of horizontal segments
8860               double yMin = (yPx < nextYPx) ? yPx : nextYPx;
8861               double yMax = (yPx > nextYPx) ? yPx : nextYPx;
8862               double xAtYMin = (yPx < nextYPx) ? xPx : nextXPx;
8863               double xAtYMax = (yPx > nextYPx) ? xPx : nextXPx;
8864    
8865               double xiPrev = xAtYMin;
8866               double yiPrev = yMin;
8867               double xi = xiPrev;
8868               double yi = yiPrev;
8869               int N = (int) Math.ceil((yMax-yMin)/spacing);
8870               double dx = Math.abs((xAtYMax - xAtYMin)/N)+ EPS;
8871               for (int i = 1; i <= N; i++) {
8872                 yi = yMin + i*(yMax - yMin)/N;
8873                 xi = xAtYMin + i * (xAtYMax - xAtYMin)/N;
8874                 realizeOneImageOfSymbol(pp, grp, arp, symbol, null, 
8875                        onY2, 
8876                        clipPlotArea,
8877                        clipDecoratedChart,
8878                        0.5*(xiPrev+xi),0.5*(yiPrev+yi),
8879                        Double.NaN, Double.NaN,
8880                        nextXPx, nextYPx,
8881                        dx, thickness);
8882                 xiPrev = xi;
8883                 yiPrev = yi;
8884                }
8885             }
8886           }
8887           
8888           // the "main" symbol (the one on the (x,y) point itself) is
8889           // rendered last to put it on top of interpolated images 
8890           if (drawMainSymbol) {
8891              double w = symbol.getWidth(pp);
8892              double h = symbol.getHeight(pp, onY2);
8893              realizeOneImageOfSymbol(pp, grp, arp, symbol, annotation, 
8894                                      onY2, 
8895                                      clipPlotArea,
8896                                      clipDecoratedChart,
8897                                      xPx, yPx,
8898                                      Double.NaN, Double.NaN,
8899                                      nextXPx, nextYPx,
8900                                      w, h);
8901           }
8902         } // realizeSymbol
8903    
8904       }
8905          /* Symbols that are assigned this symbol type can be used
8906           * to represent a pie chart slice.
8907           *
8908           * The pivot point (center of containing pie) is at the x,y
8909           * location of the point. Typically, only a single point
8910           * per pie-slice curve is used (multiple points simply
8911           * translate the same pie slice symbol to another position,
8912           * such behavior is useful if you want to use
8913           * a pie slice as a traditional curve symbol, but
8914           * it isn't needed for a typical pie chart).
8915           * <p>
8916           *
8917           * The initial angle and angle subtended by the slice are
8918           * specified by the <tt>pieSliceOrientation</tt> and
8919           * <tt>pieSliceSize</tt> properties of the host
8920           * <tt>Symbol</tt> (these properties only have meaning with
8921           * pie slice symbol types). Typically, several curves share
8922           * a common pie center point (x,y) and have orientations
8923           * and sizes that are coordinated so that the slices fit
8924           * together to form a single complete pie. GChart
8925           * facilitates this by choosing (by default) the next
8926           * slice's orientation so that it is adjacent to the
8927           * preceeding slice. However, other useful idioms include,
8928           * for example, adjusting the x,y pivots to produce
8929           * "exploded pie charts", or using a single slice that
8930           * fills up the entire pie as a disc-like alternative to
8931           * <tt>BOX_CENTER</tt>.  <p>
8932           *
8933           * The radius of the slice is chosen as the radius such
8934           * that the rectangle defined by the hosting Symbol's width
8935           * and height just barely fits within a circle with that
8936           * radius. This convention allows users to define the
8937           * pie radius in terms of the x model coordinates, y model
8938           * coordinates, or in pixels, as desired. 
8939           * <p>
8940           * 
8941           * The host <tt>Symbol</tt>'s fillSpacing and fillThickness
8942           * properties, along with horizontallyShaded and
8943           * verticallyShaded properties of this SymbolType, govern
8944           * how the slice is filled in.
8945           *
8946           * For more information with example code, see the
8947           * discussion under the {@link #PIE_SLICE_OPTIMAL_SHADING
8948           * PIE_SLICE_OPTIMAL_SHADING} symbol type.
8949           * 
8950           */
8951          
8952            private static class PieSliceSymbolType extends SymbolType {
8953              private boolean horizontallyShaded;
8954              private boolean verticallyShaded;
8955              private boolean optimallyShaded;
8956    
8957              PieSliceSymbolType(boolean horizontallyShaded,
8958                                 boolean verticallyShaded,
8959                                 boolean optimallyShaded,
8960                                 double pixelPadLeft,
8961                                 double pixelPadRight,
8962                                 double pixelPadTop,
8963                                 double pixelPadBottom) {
8964                // same as BOX_SOUTHEAST (allows shading bars to be
8965                // easily positions by their upper left corners):
8966                 super(1,1, pixelPadLeft, pixelPadRight,
8967                       pixelPadTop, pixelPadBottom);
8968    
8969                this.horizontallyShaded = horizontallyShaded;
8970                this.verticallyShaded = verticallyShaded;
8971                this.optimallyShaded = optimallyShaded;
8972              }
8973    
8974               protected AnnotationLocation defaultHoverLocation() {
8975                  return DEFAULT_PIE_SLICE_HOVER_LOCATION;
8976               }
8977              /*
8978               * @Override
8979               * 
8980               * For simplicity, pie slices are given the upper bound
8981               * band thickness of a slice that occupies the entire pie.
8982               * <p>
8983               * 
8984               * The case where hit test banding is most needed: lots
8985               * of very small full pies on a single curve (pie used as
8986               * circular alternative to a rectangular point marker)
8987               * won't suffer from this up-sizing approximation,
8988               * since it uses full pies anyway.
8989               * 
8990               */ 
8991              protected double getBandThickness(PlotPanel pp, Symbol sym,
8992                                                boolean onY2) {
8993                 double result = Math.max(MIN_BAND_SIZE,
8994                                          2*sym.getPieSliceRadius(pp, onY2));
8995                return result;
8996              }
8997            /*
8998             * @override
8999             *
9000             * Pie slices use a special radially oriented brush, whose radial
9001             * dimension is the larger of the specified brush width and
9002             * height.
9003             * <p>
9004             * 
9005             * So, from the point of view of the banded/binned hit testing
9006             * algorithm, which works entirely with rectangles, it is as if
9007             * the brush were a square with side equal to the larger of the
9008             * brush width and height. Thus, regardless of if the pie uses
9009             * horizontal or vertical hit test banding, the as-if-rectangular
9010             * brush used in binned/banded hit testing is same square box,
9011             * given by this method and its companion, <tt>getBrushWidth</tt>,
9012             * below.
9013             * <p>
9014             *
9015             * This code also relies on the fact that for pie slices, only the
9016             * larger of width, height has an impact on the more exact,
9017             * slice/angle/radius closeness testing that is applied only to
9018             * the subset of nearby points determined by using the bins/bands.
9019             * So, making brush width and height the same for pie slices
9020             * doesn't cause any detail hit testing errors (as it would for ordinary
9021             * rectangular hit testing).
9022             * <p>
9023             *
9024             * TODO: Above works (I think) but is convoluted. Try to find a
9025             * clearer, simpler, way to express/handle pie slice differences.
9026             * The special case brush location handling for pies also seems
9027             * a bit obscure.
9028             * 
9029             */ 
9030            protected int getBrushHeight(Symbol sym) {
9031               int result = Math.max(sym.getBrushHeight(),
9032                                        sym.getBrushWidth());
9033               return result;
9034            }
9035            // @override (pie slices always use a centered location)
9036            protected AnnotationLocation getBrushLocation(Symbol sym) {
9037               AnnotationLocation result = AnnotationLocation.CENTER;
9038               return result;
9039            }
9040            // @override (see comment on getBrushHeight above)
9041            protected int getBrushWidth(Symbol sym) {
9042               int result = Math.max(sym.getBrushHeight(),
9043                                        sym.getBrushWidth());
9044               return result;
9045            }
9046    
9047    
9048              protected double defaultFillSpacing() {
9049                return DEFAULT_PIE_SLICE_FILL_SPACING;
9050              }
9051              protected int defaultFillThickness() {
9052                return DEFAULT_PIE_SLICE_FILL_THICKNESS;
9053              }
9054              protected String defaultHovertextTemplate() {
9055                return DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE;
9056              }
9057    
9058              // min/max x (cosine) and y (sine) over "unit circle slice"
9059              private static class SliceLimits {
9060                 double xMin;
9061                 double xMax;
9062                 double yMin;
9063                 double yMax;
9064              }
9065    
9066              // Gets min/max sin, cos over slice cut from unit circle
9067              private SliceLimits getSliceLimits(double tMin,
9068                                                 double tMax) {
9069                SliceLimits result = new SliceLimits(); 
9070                double xMin = 0; // origin of 0,0 present in every slice
9071                double xMax = 0; // (it's the pie center/slice pivot point)
9072                double yMin = 0;
9073                double yMax = 0;
9074                double tmp = 0;
9075                // points where each edge intersects the arc could be
9076                // extremal points--include them too.
9077                tmp = Math.cos(tMin);
9078                xMin = (xMin < tmp) ? xMin : tmp;    
9079                xMax = (xMax > tmp) ? xMax : tmp;
9080                tmp = Math.sin(tMin);
9081                yMin = (yMin < tmp) ? yMin : tmp;    
9082                yMax = (yMax > tmp) ? yMax : tmp;    
9083    
9084                tmp = Math.cos(tMax);
9085                xMin = (xMin < tmp) ? xMin : tmp;    
9086                xMax = (xMax > tmp) ? xMax : tmp;
9087                tmp = Math.sin(tMax);
9088                yMin = (yMin < tmp) ? yMin : tmp;    
9089                yMax = (yMax > tmp) ? yMax : tmp;
9090    
9091                // finally if slice includes any special extreme points
9092                // on the arc (namely, points of the arc that are
9093                // either due north, due south, due east or due west)
9094                // include those points in determining the min/max x
9095                // and min/max y included in the slice:
9096                double halfPi = Math.PI/2.;
9097                for (int i = (int) Math.ceil(tMin/halfPi);
9098                     i*halfPi < tMax; i++) {
9099                  double t = i*halfPi;
9100                  tmp =  Math.cos(t);
9101                  xMin = (xMin < tmp) ? xMin : tmp;    
9102                  xMax = (xMax > tmp) ? xMax : tmp;
9103                  tmp = Math.sin(t);
9104                  yMin = (yMin < tmp) ? yMin : tmp;    
9105                  yMax = (yMax > tmp) ? yMax : tmp;
9106                }
9107    
9108                result.xMin = xMin;
9109                result.xMax = xMax;
9110                result.yMin = yMin;
9111                result.yMax = yMax;
9112    
9113                return result;
9114    
9115              }
9116              
9117              protected double getEdgeLeft(PlotPanel pp, Symbol symbol,
9118                                           double x, boolean onY2) {
9119    
9120                 double r = symbol.getPieSliceRadius(pp, onY2);
9121                 double theta0 = symbol.getPieSliceTheta0();
9122                 double theta1 = symbol.getPieSliceTheta1();
9123                 SliceLimits sl = getSliceLimits(theta1, theta0);
9124                 double xPx = pp.xToPixel(x);
9125                 // scale up the xMin on unit circle to get to left edge 
9126                 double result =  xPx + sl.xMin * r;
9127                 return result;
9128              }
9129              protected double getEdgeRight(PlotPanel pp,
9130                                            Symbol symbol,
9131                                            double x,
9132                                            boolean onY2) {
9133                 double r = symbol.getPieSliceRadius(pp, onY2);
9134                 double theta0 = symbol.getPieSliceTheta0();
9135                 double theta1 = symbol.getPieSliceTheta1();
9136                 SliceLimits sl = getSliceLimits(theta1, theta0);
9137                 double xPx = pp.xToPixel(x);
9138                 // scale up the xMax on unit circle to get to right edge 
9139                 double result = xPx + sl.xMax * r;
9140                 return result;
9141              }
9142    
9143              
9144              protected double getEdgeTop(PlotPanel pp, Symbol symbol,
9145                                          double y, boolean onY2) {
9146    
9147                 double r = symbol.getPieSliceRadius(pp, onY2);
9148                 double theta0 = symbol.getPieSliceTheta0();
9149                 double theta1 = symbol.getPieSliceTheta1();
9150                 SliceLimits sl = getSliceLimits(theta1, theta0);
9151                 double yPx = pp.yToPixel(y, onY2);
9152                 // minus for the Cartesian to pixel-coord transform
9153                 double result = yPx - sl.yMax * r;             
9154                 return result;
9155              }
9156              protected double getEdgeBottom(PlotPanel pp, Symbol symbol,
9157                                                 double y, boolean onY2) {
9158    
9159                 double r = symbol.getPieSliceRadius(pp, onY2);
9160                 double theta0 = symbol.getPieSliceTheta0();
9161                 double theta1 = symbol.getPieSliceTheta1();
9162                 SliceLimits sl = getSliceLimits(theta1, theta0);
9163                 double yPx = pp.yToPixel(y, onY2);
9164                 // minus for the Cartesian to pixel-coord transform
9165                 double result = yPx - sl.yMin * r;             
9166                 return result;
9167              }
9168    
9169    
9170    
9171              // returns the y coordinate where a pie slice edge
9172              // intersects a given vertical line, or NaN if none.
9173              private static double yWherePieEdgeIntersectsVerticalLine(
9174                double xOfVerticalLine,
9175                double xPieCenter, double yPieCenter,
9176                double pieRadius, double pieEdgeAngle) {
9177                double result = Double.NaN;
9178                double dxToArc = pieRadius*Math.cos(pieEdgeAngle);  
9179                if (dxToArc != 0) {
9180      // The fraction of the way (from pie center to pie perimeter
9181      // along the pie slice edge) that you must go to reach the point
9182      // at which the vertical line intersects with the pie slice
9183      // edge. For example, this fraction is 0.5 whenever the vertical
9184      // line bisects the pie slice edge.  
9185                  double t = (xOfVerticalLine-xPieCenter)/dxToArc;
9186                  if (GChart.withinRange(t,0,1)) {
9187                    result = yPieCenter -
9188                             t * pieRadius * Math.sin(pieEdgeAngle); 
9189                  }
9190                }
9191                return result;
9192              }
9193    
9194              // returns the x coordinate where a pie slice edge
9195              // intersects a given horizontal line, or NaN if none.
9196              private static double xWherePieEdgeIntersectsHorizontalLine(
9197                double yOfHorizontalLine,
9198                double xPieCenter, double yPieCenter,
9199                double pieRadius, double pieEdgeAngle) {
9200                double result = Double.NaN;
9201                double dyToArc = pieRadius*Math.sin(pieEdgeAngle);
9202                if (dyToArc != 0) {
9203      // The fraction of the way (from pie center to pie perimeter
9204      // along the pie slice edge) that you must go to reach the point
9205      // at which the horizontal line intersects with the pie slice
9206      // edge. For example, this fraction is 0.5 whenever the horizontal
9207      // line bisects the pie slice edge.  
9208                  double t = (yPieCenter - yOfHorizontalLine)/dyToArc;
9209                  if (GChart.withinRange(t,0,1)) {
9210                    result = xPieCenter +
9211                             t * pieRadius * Math.cos(pieEdgeAngle); 
9212                  }
9213                }
9214                return result;
9215              }
9216              /*
9217               * Returns the angle of a line extending from (0,0) to
9218               * (x,y) in radians in the standard range, 0 to 2*Pi. For
9219               * example, a line pointing due east such as (1,0) would
9220               * return 0, one pointing due north such as (0,0.5) would
9221               * return Pi/2, one pointing due west such as (-4.13,0)
9222               * would return Pi and the point (1,1) returns Pi/4.  
9223               * <p>
9224               *
9225               * x,y are in the ordinary cartesian coordinate system
9226               * (not in the typical graphics/pixel coordinates)
9227               * 
9228               */
9229              private static double angle(double x, double y) {
9230                double result = Double.NaN;
9231                if (x == 0) {
9232                  if (y > 0)
9233                    result = Math.PI/2.;
9234                  else if (y < 0)
9235                    result = 3*Math.PI/2.;
9236                }
9237                else if (x> 0 && y >= 0) 
9238                  result = Math.atan(y/x);
9239                else if (x<0 && y >= 0)
9240                  result = Math.PI - Math.atan(-y/x);
9241                else if (x <0 && y < 0)
9242                  result = Math.PI + Math.atan(y/x);
9243                else if (x > 0 && y < 0)
9244                  result = 2*Math.PI- Math.atan(-y/x);
9245    
9246                return result;
9247              }
9248    
9249              // is the given angle between the two angles given?
9250              private static boolean angleInRange(double angle,
9251                                                    double theta0,
9252                                                    double theta1) {
9253    
9254                 if (theta0 > theta1)
9255                    return angleInRange(angle, theta1, theta0);
9256                // angle is in standard 0 to 2*Pi range, but thetas
9257                // can be "wrapped around" several negative 
9258                // multiples of 2*Pi less than the standard range;
9259                // this loop brings angle into same range as thetas
9260                while (angle > theta1)
9261                   angle -= 2*Math.PI;
9262    
9263                boolean result = GChart.withinRange(angle, theta0, theta1);
9264                return result;
9265              }
9266    
9267            /*
9268             *
9269             * @Override
9270             *
9271             * The x, y coordinates at the "center" of the slice for
9272             * hit testing purposes.
9273             *
9274             * During hit testing, if more than one symbol touches
9275             * the brush, the point whose center is closest to
9276             * the mouse position is selected.
9277             *
9278             * To simplify the calculation, that center point is taken
9279             * to be the center of the pie containing the slice, rather
9280             * than the center of the slice per se. Though not an ideal
9281             * choice, it is unlikely to cause significant deviations
9282             * from user expectations, given how pie slices tend to be
9283             * used to compose full pies out of a series of
9284             * non-overlapping slices.
9285             * 
9286             */ 
9287            protected double getCenterX(PlotPanel pp, Symbol symbol,
9288                                        int iPoint) {
9289               Curve.Point p = symbol.getParent().getPoint(iPoint);
9290               double result = pp.xToPixel(p.getX());
9291               return result;
9292            }
9293            /* @Override
9294             *
9295             * See comment on getCenterX above.
9296             *
9297             */
9298            protected double getCenterY(PlotPanel pp, Symbol symbol,
9299                                        int iPoint,
9300                                        boolean onY2) {
9301               Curve.Point p = symbol.getParent().getPoint(iPoint);
9302               double result = pp.yToPixel(p.getY(), onY2);
9303               return result;
9304            }
9305            /*
9306             * @Override
9307             *
9308             * Pie slices redefine what constitutes intersection of the
9309             * mouse-centered brush and the rendered symbol to be:
9310             * "mouse position within a radially-expanded
9311             * version of the slice". The pie radius is expanded by
9312             * half the larger dimension of the point selection brush.
9313             *
9314             */  
9315            protected boolean isIntersecting(PlotPanel pp,
9316                                             Symbol symbol,
9317                                             int iPoint,
9318                                             boolean onY2,
9319                                             int xBrush,
9320                                             int yBrush,
9321                                             int brushWidth,
9322                                             int brushHeight) {
9323    
9324               boolean result = false;
9325               Curve.Point p = symbol.getParent().getPoint(iPoint);
9326               double x = p.getX();  // pie center point (slice pivot)
9327               double y = p.getY();
9328               double xPx = pp.xToPixel(x);
9329               double yPx = pp.yToPixel(y, onY2);
9330               double dx = xBrush-xPx;
9331               // - represents switch from graphics to cartesian coordinates
9332               double dy = -(yBrush-yPx);
9333    
9334               double rSquared = dx*dx + dy*dy;
9335               double angle = angle(dx, dy);
9336               // pie angles grow clockwise but radians counter-clockwise,
9337               // hence the odd "0 into max, 1 into min" mapping below.  
9338               double thetaMax = symbol.getPieSliceTheta0();
9339               double thetaMin = symbol.getPieSliceTheta1();
9340               double rPiePlus = symbol.getPieSliceRadius(pp, onY2) +
9341                                 0.5*Math.max(brushWidth, brushHeight);
9342    
9343               /*
9344                * Enforce a minimum slice angle for hit testing
9345                * purposes, equivalent to  +/- 1 px of play along the
9346                * arcs of tiny slices, to make them easier to select:<p>
9347                *
9348                * <pre>
9349                *   r*minDTheta = 1 px
9350                * </pre>
9351                * 
9352                * This helps with tiny slices adjacent to large ones, but if
9353                * several tiny slices are adjacent to each other, or if both
9354                * adjacent slices come after the tiny slice in the curve
9355                * order (e.g. the tiny slice is the very first slice) it
9356                * still won't be selectable. Developers can switch
9357                * curve order to get around this, but it's not ideal.
9358                * <p>
9359                * 
9360                * TODO: Integrate a "closest to slice angle" criterion
9361                * to resolve ties when hit testing slices to provide
9362                * a better hit testing behavior with small or overlapping
9363                * slices.
9364                *   
9365                */
9366               double minDTheta = (rPiePlus < 1)? 1.0 : 1./rPiePlus;
9367               if (thetaMax - thetaMin < 2*minDTheta) {
9368                 double thetaMid = 0.5*(thetaMax + thetaMin);
9369                 thetaMin = thetaMid - minDTheta;
9370                 thetaMax = thetaMid + minDTheta;
9371               }
9372               if (rSquared <= rPiePlus*rPiePlus &&
9373                   angleInRange(angle,thetaMin,thetaMax))
9374                  result = true;
9375    
9376               return result;
9377            }
9378    
9379    
9380            void realizeSymbol(PlotPanel pp,
9381                               GraphicsRenderingPanel grp,
9382                               AnnotationRenderingPanel arp,   
9383                               Symbol symbol, 
9384                               Annotation annotation,
9385                               boolean onY2, 
9386                               boolean clipPlotArea,
9387                               boolean clipDecoratedChart,
9388                               boolean drawMainSymbol,
9389                               double x, double y, 
9390                               double prevX, double prevY,
9391                               double nextX, double nextY) {
9392    
9393               if (!drawMainSymbol) return;
9394               double xPx = pp.xToPixel(x);
9395               double yPx = pp.yToPixel(y, onY2);
9396               double spacing = symbol.getFillSpacing();
9397               int thickness = symbol.getFillThickness();   
9398               double r = symbol.getPieSliceRadius(pp, onY2);
9399               double theta0 = symbol.getPieSliceTheta0();
9400               double theta1 = symbol.getPieSliceTheta1();
9401               GChartCanvasLite canvas = grp.getCanvas();          
9402               // x!=x is a faster isNaN  
9403               if ((xPx!=xPx) || (yPx!=yPx))
9404                  return; // undefined slice pivot point
9405               else if (clipPlotArea &&
9406                   !intersects(xPx-r, yPx-r, xPx+r, yPx+r,
9407                                0, 0, pp.getXChartSize(), pp.getYChartSize()))
9408                  return; // rect containing pie is off plot area
9409               else if (clipDecoratedChart) {
9410                   int yAxisWidth = pp.getYAxisEnsembleWidth();
9411                   int titleThickness = pp.chartTitleThickness();
9412                   if (!SymbolType.intersects(0.0 - yAxisWidth,
9413                               0.0 - titleThickness,
9414                               pp.getXChartSizeDecoratedQuickly()-yAxisWidth,
9415                               pp.getYChartSizeDecoratedQuickly()-titleThickness,
9416                               xPx-r, yPx-r, xPx+r, yPx+r))
9417                 return; // rect containing pie is off decorated chart
9418               }
9419               // else bounding rectangle of pie containing the slice visible
9420               
9421               if (0 == spacing && null != canvas && thickness > 0) {
9422                  // continuous fill pie slice and canvas is available
9423                  
9424                  /*
9425                   * Solid fill pie slices implement the notion of internal vs
9426                   * external borders a bit differently than rectangular
9427                   * symbols.
9428                   * <p>
9429                   * 
9430                   * Internal borders are always drawn "centered",
9431                   * that is, half internal, half external.  In part,
9432                   * this is because that is how "stroke" of the
9433                   * canvas API does it, so it's easier to implement.
9434                   * But mainly it is because, when you assemble
9435                   * several slices into a full pie, a centered
9436                   * border, provided that each slice has the same
9437                   * border color, is really the only choice that
9438                   * looks right (this is a constraint of the
9439                   * geometry how the slices fit together into a
9440                   * pie).  <p>
9441                   *
9442                   * External borders (negative border width) are
9443                   * drawn outside the slice proper by doubling the
9444                   * thickness, and then over-filling the internal
9445                   * part of the border by issuing the fill after,
9446                   * instead of before, the border is drawn. Though
9447                   * external borders don't look right within a pie
9448                   * (because of how the slices occlude each other's
9449                   * borders) they can be handy for making slice
9450                   * selection borders that are drawn entirely
9451                   * outside of the selected slice.
9452                   * 
9453                   */
9454                  int borderWidth = symbol.getBorderWidth();
9455                  int adjustedBorderWidth = (borderWidth >= 0) ?
9456                      borderWidth : 2*Math.abs(borderWidth);
9457                  
9458                   /*
9459                    * With incubator's <tt>GWTCanvas</tt>, IE7 & Chrome draw
9460                    * 0 and 2*Pi slices incorrectly.  See issues #278 
9461                    * #282 for more information: <p>
9462                    * 
9463                    * http://code.google.com/p/google-web-toolkit-incubator/issues/detail?id=278
9464                    * http://code.google.com/p/google-web-toolkit-incubator/issues/detail?id=282
9465                    * <p>
9466                    *
9467                    * Pies with > 1000 px radii are unlikely (tried
9468                    * using 10000, but it didn't work in Chrome).
9469                    *
9470                    */ 
9471                  final double MIN_DTHETA = 1./1000;
9472                  final double MAX_DTHETA = 2*Math.PI - MIN_DTHETA;
9473    
9474                  // canvas measures angles clockwise from +x-axis;
9475                  // our angles are counter-clockwise from +x-axis
9476                  double dTheta = theta0 - theta1;
9477                  double angleStart = 2*Math.PI-theta0;
9478                  double angleEnd = angleStart +
9479                      Math.max(MIN_DTHETA, Math.min(dTheta, MAX_DTHETA));
9480    
9481                  if (dTheta >= MIN_DTHETA || borderWidth < 0) {
9482                     canvas.beginPath();
9483                     canvas.setLineWidth(adjustedBorderWidth);
9484    
9485                     canvas.arc(xPx - grp.x0, yPx - grp.y0, r,
9486                                angleStart, angleEnd, false);
9487                     if (dTheta <= MAX_DTHETA)
9488                        canvas.lineTo(xPx - grp.x0, yPx- grp.y0);
9489                     // else avoid "line to center" in full pies
9490    
9491                     canvas.closePath();
9492    
9493                     String borderColor = symbol.getBorderColor();
9494                     String backgroundColor = symbol.getBackgroundColor();
9495                     /*
9496                      * XXX: The approach to transparent border/fill
9497                      * used below is to simply not stroke the border
9498                      * or to not fill the inside of the path. This
9499                      * isn't exactly right, because the region where
9500                      * the border overlaps the filled area does not
9501                      * always become transparent when it should.
9502                      * These errors likely won't be noticed in most
9503                      * usage scenarios, and without the ability to
9504                      * replace filled/stroked regions with
9505                      * transparent pixels (I don't think GWTCanvas
9506                      * can do this?) there isn't an easy fix.
9507                      * 
9508                      */
9509    
9510    
9511                     // non-negative borders fill before stroking (thus
9512                     // stroke overwrites internal half of border)
9513                     if (borderWidth >= 0 && thickness > 0 &&
9514                         TRANSPARENT_BORDER_COLOR != backgroundColor &&
9515                         // GWTCanvas thows an exception w "transparent"
9516                         "transparent" != backgroundColor) {
9517                         canvas.setFillStyle(backgroundColor);
9518                         canvas.fill();
9519                     }
9520    
9521                     // stroke whenever a border is present
9522                     if (borderWidth != 0 &&
9523                         TRANSPARENT_BORDER_COLOR != borderColor &&
9524                         "transparent" != borderColor) {
9525                        canvas.setStrokeStyle(borderColor);
9526                        canvas.stroke();
9527                     }
9528    
9529                     // negative borders fill AFTER stroking (thus zapping
9530                     // the internal half of the stroked border).
9531                     if (borderWidth < 0 && thickness > 0 &&
9532                         TRANSPARENT_BORDER_COLOR != backgroundColor &&
9533                         "transparent" != backgroundColor) {
9534                         canvas.setFillStyle(backgroundColor);
9535                         canvas.fill();
9536                      }
9537                  }
9538                  // else 0-sized slice, 0 or internal border, is just dropped
9539               }
9540               else {
9541                  if (0 == spacing) spacing = 1;
9542                  // if center point is on the chart, draw it:
9543    
9544                  double prevXPx = pp.xToPixel(prevX);
9545                  double prevYPx = pp.yToPixel(prevY, onY2);
9546                  double nextXPx = pp.xToPixel(nextX);
9547                  double nextYPx = pp.yToPixel(nextY, onY2);
9548                  int nBands = (int) Math.round(r/spacing);
9549                  /* Holds positions at which the current vertical or
9550                   * horizontal "gridline-like band" intersects the outter
9551                   * perimeter of the current pie slice. These positions
9552                   * are used to define the location and size of shading
9553                   * bars required for each pie slice.
9554                   *
9555                   * Note: Although most pie slice perimeters are convex
9556                   * and thus have perimeters that intersect a gridline
9557                   * in at most two points, pie slices that take up more
9558                   * than half of the entire pie have perimeters that
9559                   * can (across their pacman-like mouth) intersect a
9560                   * gridline at up to four points.
9561                   * 
9562                   */
9563                  final int MAX_PIE_SLICE_PERIMETER_INTERSECTIONS = 4;
9564                  double[] p = new double[MAX_PIE_SLICE_PERIMETER_INTERSECTIONS];
9565                  final double EPS = 0.5;
9566                  SliceLimits sl = getSliceLimits(theta1, theta0);
9567                  boolean optimalIsVertical =
9568                                             (sl.yMax - sl.yMin) > (sl.xMax - sl.xMin);
9569                  boolean isFullPie = (symbol.getPieSliceSize() == 1.0);  
9570                  // perform any vertical shading that may be required:
9571                  if (nBands > 0 && (verticallyShaded ||
9572                                     (optimallyShaded && optimalIsVertical))) {
9573                     for (int i = (int) Math.round(nBands*sl.xMin);
9574                          i < sl.xMax*nBands; i++) {
9575                        int nP = 0;
9576                        double dxPx = r*(i+0.5)/nBands;
9577                        double dyPx = Math.sqrt(r*r - dxPx*dxPx); 
9578                        // x of vertical line bisecting the shading band
9579                        double xi = xPx + dxPx;
9580                        // y-positions where this band crosses circle perimeter
9581                        double c1 = yPx - dyPx; 
9582                        double c2 = yPx + dyPx; 
9583                        // y-positions where this band crosses each slice edge
9584                        // (full pies don't have pie slice edges)
9585                        double e1 = isFullPie ?
9586                                    Double.NaN :
9587                                    yWherePieEdgeIntersectsVerticalLine(
9588                           xi,xPx,yPx,r,theta0);
9589                        double e2 = isFullPie?
9590                                    Double.NaN :
9591                                    yWherePieEdgeIntersectsVerticalLine(
9592                           xi,xPx,yPx,r,theta1);
9593                        // Exclude circle perimeter intercepts outside of
9594                        // the slice.  Note: Pixel y coordinates used in
9595                        // browser increase going down, but cartesian y
9596                        // coordinates used in trig functions increase
9597                        // going up, hence the sign-flipping on second arg
9598                        // of angle function below.
9599                        if (angleInRange(angle(xi-xPx,yPx-c1),theta0,theta1))
9600                           p[nP++] = c1;
9601                        // intersection points sorted by increasing y within p[]
9602                        if (e1 < e2) {
9603                           // x!=x is a faster isNaN   
9604                           if (!(e1!=e1)) p[nP++] = e1; 
9605                           if (!(e2!=e2)) p[nP++] = e2;
9606                        }
9607                        else {
9608                           if (!(e2!=e2)) p[nP++] = e2;
9609                           if (!(e1!=e1)) p[nP++] = e1; 
9610                        }
9611    
9612                        if (angleInRange(angle(xi-xPx, yPx-c2),theta0,theta1))
9613                           p[nP++] = c2;
9614                        for (int j = 1; j < nP; j++) {
9615                           // logic below avoids drawing a line across the
9616                           // non-convex "pacman mouth" that occurs with any
9617                           // bigger-than-half-pie-sized slices, by
9618                           // requiring that a line drawn from the pie
9619                           // center to an interpolated point on each
9620                           // shading bar forms an angle in the slice's
9621                           // angular range. We use a point 30% rather than
9622                           // 50% of the way inbetween to avoid ever hitting the
9623                           // center of the pie (where angle is ambiguous).
9624                           //
9625                           // Note that, due to roundoff error, you cannot
9626                           // ALWAYS rely on the (mathematically correct)
9627                           // fact that problematic bars always connect p[1]
9628                           // and p[2].
9629                           if (Math.abs(theta0-theta1) <= Math.PI ||
9630                               angleInRange(angle(xi-xPx,
9631                                                  yPx-(0.3*p[j]+0.7*p[j-1])),
9632                                            theta0,theta1)) {
9633                              // widening of EPS pixels on either side fills in
9634                              // tiny intra-slice gaps (that can otherwise appear
9635                              // due to roundoff) by making each bar a tad bigger.
9636                              realizeOneImageOfSymbol(pp, grp, arp,
9637                                 symbol, null, 
9638                                 onY2,  
9639                                 clipPlotArea,
9640                                 clipDecoratedChart,
9641                                 xi-0.5*
9642                                 thickness,
9643                                 p[j-1]-EPS,
9644                                 prevXPx, prevYPx,
9645                                 nextXPx, nextYPx,
9646                                 thickness, 
9647                                 p[j] - p[j-1] +2*EPS);
9648                           }
9649                        }
9650                     }
9651                  }
9652    
9653                  // Now do any required horizontal shading. This is
9654                  // basically the same as the code for vertical shading
9655                  // above (w appropriate transposition/adjustments).
9656                  if (nBands > 0 && (horizontallyShaded ||
9657                                     (optimallyShaded && !optimalIsVertical))) {
9658                     for (int i = (int) Math.round(-nBands*sl.yMax);
9659                          i < -nBands * sl.yMin; i++) {
9660                        int nP = 0;
9661                        double dyPx = r*(i+0.5)/nBands;
9662                        double dxPx = Math.sqrt(r*r - dyPx*dyPx); 
9663                        // y of the horizontal line bisecting the shading band
9664                        double yi = yPx + dyPx;
9665    
9666                        // x-positions where this band crosses circle perimeter
9667                        double c1 = xPx - dxPx;
9668                        double c2 = xPx + dxPx;
9669    
9670                        // x-positions where this band crosses each slice edge
9671                        // (full pies don't have pie slice edges)
9672                        double e1 = isFullPie ?
9673                                    Double.NaN :
9674                                    xWherePieEdgeIntersectsHorizontalLine(
9675                           yi,xPx,yPx,r,theta0);
9676                        double e2 = isFullPie ?
9677                                    Double.NaN :
9678                                    xWherePieEdgeIntersectsHorizontalLine(
9679                           yi,xPx,yPx,r,theta1);
9680                        // exclude circle perimeter intercepts outside of
9681                        // the slice
9682                        if (angleInRange(angle(c1-xPx, yPx-yi),theta0,theta1))
9683                           p[nP++] = c1;
9684    
9685                        // intersection points sorted by increasing x within p[]
9686                        if (e1 < e2) {
9687                           // x!=x is a faster isNaN
9688                           if (!(e1!=e1)) p[nP++] = e1;
9689                           if (!(e2!=e2)) p[nP++] = e2;
9690                        }
9691                        else {
9692                           if (!(e2!=e2)) p[nP++] = e2;
9693                           if (!(e1!=e1)) p[nP++] = e1;
9694                        }
9695    
9696                        if (angleInRange(angle(c2-xPx, yPx-yi),theta0,theta1))
9697                           p[nP++] = c2;
9698    
9699                        for (int j = 1; j < nP; j++) {
9700                           // c.f. comment on corresponding vertical code above.  
9701                           if (Math.abs(theta0-theta1) <= Math.PI ||
9702                               angleInRange(angle((0.3*p[j]+0.7*p[j-1])-xPx,
9703                                                  yPx-yi),
9704                                            theta0,theta1)) {
9705                              // widening of EPS pixels on either side fills in
9706                              // tiny intra-slice gaps that can sometimes appear
9707                              // by making slices just a tad bigger.
9708                              realizeOneImageOfSymbol(pp, grp, arp,
9709                                 symbol, null, 
9710                                 onY2,  
9711                                 clipPlotArea,
9712                                 clipDecoratedChart,
9713                                 p[j-1]-EPS,
9714                                 yi-0.5*
9715                                 thickness,
9716                                 prevXPx, prevYPx,
9717                                 nextXPx, nextYPx,
9718                                 p[j]-p[j-1] + 2*EPS,
9719                                 thickness);
9720                           }
9721                        }
9722                     }
9723                  }
9724               }
9725    
9726               // if the image has an attached label, realize that
9727               if (annotation!=null &&
9728                   (annotation.getText() != null ||
9729                    annotation.getWidget() != null) &&
9730                   annotation.getVisible()) {
9731    
9732                  // plus x-axis, for shifts, always corresponds to
9733                  // outward pointing radius that bisects the slice,
9734                  // with positive y axis, for shifts, at a 90 degree
9735                  // counter-clockwise rotation from this x. Basic
9736                  // trigonometry and this spec yeilds lines below.
9737                  double thetaMid = (theta0+theta1)/2.;
9738                  double dX = annotation.getXShift();
9739                  double dY = annotation.getYShift();
9740                  double sinTheta = Math.sin(thetaMid);
9741                  double cosTheta = Math.cos(thetaMid);
9742                  AnnotationLocation loc = annotation.getLocation();
9743                  if (null == loc) loc = defaultAnnotationLocation(); 
9744                  // note: pixel Y increases down but yShift & "trig Y"
9745                  // increase going up, which explains dY sign reversal
9746                  arp.renderAnnotation(annotation,
9747                                      loc.decodePieLocation(thetaMid),
9748                                      xPx+(r+dX)*cosTheta - dY*sinTheta,
9749                                      yPx-(r+dX)*sinTheta - dY*cosTheta,
9750                                      0, 0,
9751                                      symbol);
9752               }
9753            }
9754          } // end of class PieSliceSymbolType 
9755    
9756    
9757          
9758          private static class VBarBottom extends SymbolType {
9759             VBarBottom(int wm, int hm) {
9760                 super(wm, hm,0.5,0.5,0.5,0.5, Boolean.FALSE);
9761             }
9762             protected double defaultFillSpacing() {
9763                return DEFAULT_BAR_FILL_SPACING;
9764             }
9765             protected AnnotationLocation defaultHoverLocation() {
9766                return DEFAULT_VBARBOTTOM_HOVER_LOCATION;
9767             }
9768             public double getAdjustedHeight(double height, double y,
9769                                          double yPrev, double yNext,
9770                                          double yMin, double yMax, double yMid) {
9771                return yMax - y;
9772             }
9773             int getIconHeight(int legendFontSize) {
9774                return legendFontSize;
9775             }
9776             int getIconWidth(int legendFontSize) {
9777                return (int) Math.round(legendFontSize/2.);
9778             }
9779          } // end of class VBarBottom
9780          private static class VBarBaseline extends SymbolType {
9781             VBarBaseline(int wm, int hm) {
9782                 super(wm, hm, 0, 0, 0.5, 0.5, Boolean.FALSE);
9783             }
9784             protected double defaultFillSpacing() {
9785                return DEFAULT_BAR_FILL_SPACING;
9786             }
9787             protected AnnotationLocation defaultHoverLocation() {
9788                return DEFAULT_VBAR_BASELINE_HOVER_LOCATION;
9789             }
9790             public double getAdjustedHeight(double height, double y,
9791                                          double yPrev, double yNext,
9792                                             double yMin, double yMax,double yMid) {
9793               return y - yMid;
9794             }
9795             
9796             double getUpperLeftY(double height, double y,
9797                                  double yPrev, double yNext,
9798                                  double yMin, double yMax, double yMid,
9799                                  int yMouse)  {
9800                 return yMid;
9801             }
9802             
9803             int getIconHeight(int legendFontSize) {
9804                return legendFontSize;
9805             }
9806             int getIconWidth(int legendFontSize) {
9807                return (int) Math.round(legendFontSize/2.);
9808             }
9809          } // end of class VBarBaseline
9810          /** Use vertical bars that extend from the top of the chart
9811           ** to each point on the curve. 
9812           **/ 
9813          private static class VBarTop extends SymbolType {
9814             VBarTop(int wm, int hm) {
9815                 super(wm, hm, 0.5, 0.5, 0.5, 0.5, Boolean.FALSE);
9816             }
9817             protected double defaultFillSpacing() {
9818                return DEFAULT_BAR_FILL_SPACING;
9819             }
9820             protected AnnotationLocation defaultHoverLocation() {
9821                return DEFAULT_VBARTOP_HOVER_LOCATION;
9822             }
9823             public double getAdjustedHeight(double height, double y,
9824                                          double yPrev, double yNext,
9825                                          double yMin, double yMax, double yMid) {
9826                return y - yMin;
9827             }
9828             int getIconHeight(int legendFontSize) {
9829                return legendFontSize;
9830             }
9831             int getIconWidth(int legendFontSize) {
9832                return (int) Math.round(legendFontSize/2.);
9833             }
9834          } // end of class VBarTop
9835          /**
9836           ** Points on curves with this symbol type are positioned
9837           ** at the center of the plot area, and do not have a
9838           ** visible symbol.<p>
9839           ** 
9840           **
9841           ** Use this symbol type, along with the
9842           ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
9843           ** <tt>setAnnotationYShift</tt> methods, to position
9844           ** annotations relative to the center of the plot area.
9845           **
9846           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
9847           ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
9848           ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
9849           ** 
9850           **/ 
9851          public static SymbolType ANCHOR_CENTER =
9852             new AnnotationAnchor(AnnotationLocation.CENTER);
9853          
9854          /**
9855           ** Points on curves with this symbol type are positioned
9856           ** at the center of the right edge of the plot area, and
9857           ** do not have a visible symbol.<p>
9858           **
9859           ** Use this symbol type, along with the
9860           ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
9861           ** <tt>setAnnotationYShift</tt> methods, to position
9862           ** annotations relative to the center of the right
9863           ** edge of the plot area.
9864           **
9865           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
9866           ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
9867           ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
9868           ** 
9869           **/ 
9870          public static SymbolType ANCHOR_EAST =
9871             new AnnotationAnchor(AnnotationLocation.EAST);
9872    
9873          /**
9874           ** When passed to the <tt>setHoverAnnotationSymbolType</tt>
9875           ** method, this symbol type enables
9876           ** <tt>setTitle</tt>-like, "anchored at the mouse cursor"
9877           ** hover annotation positioning. Specifically, hover annotions act as
9878           ** if they were annotations of 1px x 1px
9879           ** points placed at the current mouse cursor position.
9880           ** <p>
9881           **
9882           ** Because this and its related symbol types,
9883           ** <tt>ANCHOR_MOUSE_SNAP_TO_X</tt> and
9884           ** <tt>ANCHOR_MOUSE_SNAP_TO_Y</tt>, are intended only to
9885           ** facilitate positioning of hover-induced pop-up annotations
9886           ** (via the <tt>setHoverAnnotationSymbolType</tt> method) I
9887           ** cannot imagine a scenario where it would make sense to use
9888           ** them as the symbol type of an ordinary, user defined, curve
9889           ** (if you find a use for this, please let me know).
9890           **
9891           ** 
9892           ** @see #ANCHOR_MOUSE_SNAP_TO_X ANCHOR_MOUSE_SNAP_TO_X
9893           ** @see #ANCHOR_MOUSE_SNAP_TO_Y ANCHOR_MOUSE_SNAP_TO_Y
9894           ** @see Symbol#setHoverLocation setHoverLocation
9895           ** @see Symbol#setHoverAnnotationSymbolType setHoverAnnotationSymbolType
9896           ** @see Symbol#setHovertextTemplate setHovertextTemplate
9897           ** @see Symbol#setHoverXShift setHoverXShift
9898           ** @see Symbol#setHoverYShift setHoverYShift
9899           ** @see Symbol#setHoverWidget setHoverWidget
9900           **/ 
9901          public static SymbolType ANCHOR_MOUSE = new AnnotationAnchor(
9902             AnnotationLocation.AT_THE_MOUSE);
9903    
9904          /**
9905           * The same as the ANCHOR_MOUSE symbol type, except that
9906           * the x coordinate of the rendered symbol is taken from
9907           * the x coordinate of the point, rather than the x
9908           * coordinate of the mouse.
9909           * 
9910           * @see #ANCHOR_MOUSE ANCHOR_MOUSE
9911           * @see #ANCHOR_MOUSE_SNAP_TO_Y ANCHOR_MOUSE_SNAP_TO_Y
9912           * 
9913           */
9914          public static SymbolType ANCHOR_MOUSE_SNAP_TO_X =
9915             new AnnotationAnchor(AnnotationLocation.AT_THE_MOUSE_SNAP_TO_X);
9916          /**
9917           * The same as the ANCHOR_MOUSE symbol type, except that
9918           * the y coordinate of the rendered symbol is taken from
9919           * the y coordinate of the point, rather than the y
9920           * coordinate of the mouse.
9921           * 
9922           * @see #ANCHOR_MOUSE ANCHOR_MOUSE
9923           * @see #ANCHOR_MOUSE_SNAP_TO_X ANCHOR_MOUSE_SNAP_TO_X
9924           * 
9925           */
9926          public static SymbolType ANCHOR_MOUSE_SNAP_TO_Y =
9927             new AnnotationAnchor(AnnotationLocation.AT_THE_MOUSE_SNAP_TO_Y);
9928          /**
9929           ** Points on curves with this symbol type are positioned
9930           ** at the center of the top edge of the plot area, and do
9931           ** not have a visible symbol.<p>
9932           **
9933           ** Use this symbol type, along with the
9934           ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
9935           ** <tt>setAnnotationYShift</tt> methods, to position
9936           ** annotations relative to the center of the top edge of
9937           ** the plot area.
9938           **
9939           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
9940           ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
9941           ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
9942           ** 
9943           **/ 
9944          public static SymbolType ANCHOR_NORTH =
9945             new AnnotationAnchor(AnnotationLocation.NORTH);
9946          /**
9947           ** Points on curves with this symbol type are positioned
9948           ** at the upper right corner of the plot area, and do not
9949           ** have a visible symbol.<p>
9950           **
9951           ** Use this symbol type, along with the
9952           ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
9953           ** <tt>setAnnotationYShift</tt> methods, to position
9954           ** annotations relative to the upper right corner of the
9955           ** plot area.
9956           **
9957           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
9958           ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
9959           ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
9960           ** 
9961           **/ 
9962          public static SymbolType ANCHOR_NORTHEAST =
9963             new AnnotationAnchor(AnnotationLocation.NORTHEAST);
9964    
9965          /**
9966           ** Points on curves with this symbol type are positioned
9967           ** at the upper left corner of the plot area, and do not
9968           ** have a visible symbol.<p>
9969           **
9970           ** Use this symbol type, along with the
9971           ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
9972           ** <tt>setAnnotationYShift</tt> methods, to position
9973           ** annotations relative to the upper left corner of the
9974           ** plot area.
9975           **
9976           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
9977           ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
9978           ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
9979           ** 
9980           **/ 
9981          public static SymbolType ANCHOR_NORTHWEST =
9982             new AnnotationAnchor(AnnotationLocation.NORTHWEST);
9983    
9984          /**
9985           ** Points on curves with this symbol type are positioned
9986           ** at the center of the bottom edge of the plot area, and
9987           ** do not have a visible symbol.<p>
9988           **
9989           ** Use this symbol type, along with the
9990           ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
9991           ** <tt>setAnnotationYShift</tt> methods, to position
9992           ** annotations relative to the center of the bottom edge
9993           ** of the plot area.
9994           **
9995           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
9996           ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
9997           ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
9998           ** 
9999           **/ 
10000          public static SymbolType ANCHOR_SOUTH =
10001             new AnnotationAnchor(AnnotationLocation.SOUTH);
10002    
10003    
10004          /**
10005           ** Points on curves with this symbol type are positioned
10006           ** at the lower right corner of the plot area, and do not
10007           ** have a visible symbol.<p>
10008           **
10009           ** Use this symbol type, along with the
10010           ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
10011           ** <tt>setAnnotationYShift</tt> methods, to position
10012           ** annotations relative to the lower right corner of the
10013           ** plot area.
10014           **
10015           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
10016           ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
10017           ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
10018           ** 
10019           **/ 
10020          public static SymbolType ANCHOR_SOUTHEAST =
10021             new AnnotationAnchor(AnnotationLocation.SOUTHEAST);
10022    
10023    
10024          /**
10025           ** Points on curves with this symbol type are positioned
10026           ** at the lower left corner of the plot area, and do not
10027           ** have a visible symbol.<p>
10028           **
10029           ** Use this symbol type, along with the
10030           ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
10031           ** <tt>setAnnotationYShift</tt> methods, to position
10032           ** annotations relative to the lower left corner of the
10033           ** plot area.
10034           **
10035           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
10036           ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
10037           ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
10038           ** 
10039           **/ 
10040          public static SymbolType ANCHOR_SOUTHWEST =
10041             new AnnotationAnchor(AnnotationLocation.SOUTHWEST);
10042    
10043          /**
10044           ** Points on curves with this symbol type are positioned
10045           ** at the center of the left edge of the plot area, and do
10046           ** not have a visible symbol.<p>
10047           **
10048           ** Use this symbol type, along with the
10049           ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
10050           ** <tt>setAnnotationYShift</tt> methods, to position
10051           ** annotations relative to the center of the left edge of
10052           ** the plot area.
10053           **
10054           ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
10055           ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
10056           ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
10057           ** 
10058           **/ 
10059          public static SymbolType ANCHOR_WEST =
10060             new AnnotationAnchor(AnnotationLocation.WEST);
10061    
10062         /** Use rectangles horizontally and vertically centered
10063           ** on each point of the curve */ 
10064          public static SymbolType BOX_CENTER =
10065               new SymbolType(0,0,0,0,0,0);
10066          /** Use rectangles just to the right of, and
10067           ** vertically centered on, each point of the curve */ 
10068          public static SymbolType BOX_EAST = 
10069               new SymbolType(1, 0, 0.5, -0.5, 0, 0);
10070          /** Use rectangles just above, and horizontally centered
10071           ** on, each point of the curve */ 
10072          public static SymbolType BOX_NORTH = 
10073             new SymbolType(0, -1,0,0,-0.5,0.5);
10074          
10075          /** Use rectangles just above, and to the right of,
10076           ** each point of the curve */ 
10077          public static SymbolType BOX_NORTHEAST = 
10078             new SymbolType(1, -1, 0.5,-0.5,-0.5,0.5);
10079          
10080          /** Use rectangles just above and to the left of,
10081           ** each point of the curve */ 
10082          public static SymbolType BOX_NORTHWEST =
10083              new SymbolType(-1, -1, -0.5, 0.5, -0.5, 0.5);
10084    
10085          /** Use rectangles just below, and horizontally centered
10086           ** on, each point of the curve */ 
10087          public static SymbolType BOX_SOUTH = 
10088             new SymbolType(0, 1, 0, 0, 0.5, -0.5);
10089    
10090          /** Use rectangles just below, and to the right of,
10091           ** each point of the curve */ 
10092          public static SymbolType BOX_SOUTHEAST = 
10093             new SymbolType(1, 1, 0.5, -0.5, 0.5, -0.5);
10094    
10095          /** Use rectangles just below, and to the left of, 
10096           ** each point of the curve */ 
10097          public static SymbolType BOX_SOUTHWEST = 
10098            new SymbolType(-1, 1, -0.5, 0.5, 0.5, -0.5);
10099    
10100          /** Use rectangles just to the left of, and vertically centered
10101           ** on, each point of the curve */ 
10102          public static SymbolType BOX_WEST = 
10103            new SymbolType(-1, 0, -0.5, 0.5, 0, 0);
10104          /**
10105           ** Use horizontal bars that extend from the x,y position
10106           ** associated with each point, to the x position defined
10107           ** by the host <tt>Symbol</tt>'s baseline property, and that are
10108           ** vertically centered on the data point.
10109           **
10110           ** @see Symbol#setBaseline setBaseline
10111           ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
10112           ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
10113           ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
10114           ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
10115           ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
10116           ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
10117           ** @see Symbol Symbol
10118           ** 
10119           **/ 
10120          public static SymbolType HBAR_BASELINE_CENTER =
10121            new HBarBaseline(0,0);
10122          /**
10123           ** Use horizontal bars that extend from the x,y position
10124           ** associated with each point, to the x position defined
10125           ** by the host <tt>Symbol</tt>'s baseline property, and whose
10126           ** bottom edge passes through the data point.
10127           **
10128           ** @see Symbol#setBaseline setBaseline
10129           ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
10130           ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
10131           ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
10132           ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
10133           ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
10134           ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
10135           ** @see Symbol Symbol
10136           ** 
10137           **/ 
10138          public static SymbolType HBAR_BASELINE_NORTH =
10139            new HBarBaseline(0,-1);
10140          /**
10141           ** Use horizontal bars that extend from the x,y position
10142           ** associated with each point, to the x position defined
10143           ** by the host <tt>Symbol</tt>'s baseline property, and whose
10144           ** top edge passes through the data point.
10145           **
10146           ** @see Symbol#setBaseline setBaseline
10147           ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
10148           ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
10149           ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
10150           ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
10151           ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
10152           ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
10153           ** 
10154           **/ 
10155          public static SymbolType HBAR_BASELINE_SOUTH =
10156            new HBarBaseline(0,1);
10157          /** Use horizontal bars that extend from the right y-axis
10158           ** to each point on the curve, and that are vertically
10159           ** centered on the point.
10160           **/ 
10161          public static SymbolType HBAR_EAST = new HBarRight(1,0);
10162          private static SymbolType line  = new LineSymbolType();   
10163          /**
10164           ** @deprecated
10165           **
10166           ** As of version 2.4, this symbol has been redefined to
10167           ** be synonomous with the LINE symbol type.
10168           ** <p>
10169           **
10170           ** Prior to v2.4, this symbol drew a horizontal bar from
10171           ** each point to the x coordinate of the next point.  Some
10172           ** applications may need to use a revised point set in
10173           ** order to produce the same curves using <tt>LINE</tt>
10174           ** that they used to produce with this symbol.  <p>
10175           **
10176           ** See the discussion within the {@link #VBAR_NEXT
10177           ** VBAR_NEXT} symbol for more information about why
10178           ** support for these vertically and horizontally constrained
10179           ** connecting line symbol types was dropped.
10180           ** 
10181           ** @see #LINE LINE
10182           ** @see #HBAR_PREV HBAR_PREV
10183           ** @see #VBAR_PREV VBAR_PREV
10184           ** @see #VBAR_NEXT VBAR_NEXT
10185           ** 
10186           **/      
10187          public static SymbolType HBAR_NEXT = line;
10188          /** Use horizontal bars that extend from the right y-axis
10189           ** to each point on the curve, and that are vertically
10190           ** just above the point.
10191           **/ 
10192          public static SymbolType HBAR_NORTHEAST = new HBarRight(1,-1);
10193    
10194    
10195          /** Use horizontal bars that extend from the left y-axis
10196           ** to each point on the curve, and that are vertically
10197           ** just above point. 
10198           **/ 
10199          public static SymbolType HBAR_NORTHWEST  = new HBarLeft(-1,-1);
10200          /**
10201           ** @deprecated
10202           **
10203           ** As of version 2.4, this symbol has been redefined to
10204           ** be synonymous with the LINE symbol type.
10205           ** <p>
10206           **
10207           ** Prior to v2.4, this symbol drew a horizontal bar from
10208           ** each point to the x coordinate of the previous point.
10209           ** Some applications may need to use a revised point set
10210           ** in order to produce the same curves using <tt>LINE</tt>
10211           ** that they used to produce with this symbol.  <p>
10212           **
10213           ** See the discussion within the {@link #VBAR_NEXT
10214           ** VBAR_NEXT} symbol for more information about why
10215           ** support for these vertically and horizontally constrained
10216           ** connecting line symbol types was dropped.
10217           ** 
10218           ** @see #LINE LINE
10219           ** @see #HBAR_NEXT HBAR_NEXT
10220           ** @see #VBAR_PREV VBAR_PREV
10221           ** @see #VBAR_NEXT VBAR_NEXT
10222           ** 
10223           ** 
10224           **/
10225          public static SymbolType HBAR_PREV = line;
10226          /** Use horizontal bars that extend from the right y-axis
10227           ** to each point on the curve, and that are vertically
10228           ** just below the point.
10229           **/ 
10230          public static SymbolType HBAR_SOUTHEAST = new HBarRight(1,1);
10231          /** Use horizontal bars that extend from the left y-axis
10232           ** to each point on the curve, and that are vertically
10233           ** just below the point. 
10234           **/ 
10235          public static SymbolType HBAR_SOUTHWEST  = new HBarLeft(-1,1);
10236    
10237          /** Use horizontal bars that extend from the left y-axis
10238           ** to each point on the curve, and that are vertically
10239           ** centered on the point. 
10240           **/ 
10241          public static SymbolType HBAR_WEST  = new HBarLeft(-1,0);   
10242    
10243    
10244          /**
10245           ** This symbol type draws a continuous straight line between
10246           ** successive individual data points.  By default, the line is
10247           ** drawn via an appropriate series of rectangular HTML elements,
10248           ** which can produce a "stair-step" look at certain angles.
10249           **
10250           ** <small><blockquote> <i>Tip:</i> You can get
10251           ** order-of-magnitude faster, and crisper, line charts by
10252           ** adding an external vector graphics library to GChart
10253           ** via the <tt>setCanvasFactory</tt> method.</small> <p>
10254           ** 
10255           ** Apart from this connecting line, the individual data
10256           ** points are displayed exactly as they would have been
10257           ** displayed via BOX_CENTER.  <p>
10258           ** 
10259           ** Produces a connecting line similar to what could be
10260           ** produced via BOX_CENTER with a fill spacing of 1px,
10261           ** except that it uses a more efficient representation
10262           ** that merges vertical or horizontal "dot blocks" into
10263           ** single HTML elements whenever possible.
10264           ** <p>
10265           ** 
10266           ** To produce a line without showing the individual data
10267           ** points as separate rectangular symbols, set width and
10268           ** height to match your symbol's specified
10269           ** <tt>fillThickness</tt>.
10270           ** 
10271           ** @see #BOX_CENTER BOX_CENTER
10272           ** @see #setCanvasFactory setCanvasFactory
10273           ** @see Symbol#setFillThickness setFillThickness
10274           ** 
10275           ** 
10276           **
10277           **/
10278          public static SymbolType LINE = line;
10279    
10280          /** @deprecated
10281           ** 
10282           ** In GChart 2.3, you had to use this special symbol type
10283           ** to get GChart to use an external canvas library to
10284           ** draw crisp connecting lines.
10285           ** <p>
10286           **
10287           ** As of GChart 2.5, the <tt>LINE</tt> symbol type will,
10288           ** by default, be rendered with whatever external canvas
10289           ** library you provide to GChart via the
10290           ** <tt>setCanvasFactory</tt> method. 
10291           ** 
10292           ** <p>
10293           **
10294           ** So now, <tt>LINE_CANVAS</tt> is just another name
10295           ** for <tt>LINE</tt>. Please replace <tt>LINE_CANVAS</tt> with
10296           ** <tt>LINE</tt> in your code.
10297           ** <p>
10298           **
10299           ** <small> Note that <tt>LINE</tt> only draws continuous lines if
10300           ** fill spacing (<tt>setFillSpacing</tt>) is <tt>0</tt>. With
10301           ** fill spacing > 0, it uses the old HTML-rendering method. Since
10302           ** <tt>0</tt> is now the new default fill spacing for the
10303           ** <tt>LINE</tt> symbol type, normally <tt>LINE</tt> works
10304           ** exactly like <tt>LINE_CANVAS</tt> did. But, if you had
10305           ** explicitly set the fill spacing, you may have to remove this
10306           ** specification, or set it to <tt>0</tt>, to get the same
10307           ** behavior you had before with <tt>LINE_CANVAS</tt>.  <p>
10308           ** </small>
10309           ** 
10310           ** @see #LINE LINE
10311           ** @see #setCanvasFactory setCanvasFactory
10312           ** @see Symbol#setFillSpacing setFillSpacing
10313           ** 
10314           **/
10315          public static SymbolType LINE_CANVAS  = line;
10316          /**
10317           ** A symbol type that does not draw any main symbol. Use
10318           ** this symbol type for curves whose points exist solely
10319           ** for the purpose of positioning their associated
10320           ** annotations. Note that if <tt>fillThickness</tt> is
10321           ** non-zero, any connecting dots between the points will
10322           ** still be drawn.  <p>
10323           **
10324           ** Equivalent to using the <tt>BOX_CENTER</tt> symbol
10325           ** type, but with the host symbol's width and height both
10326           ** set to zero, so that no box symbol is ever visible.
10327           ** <p>
10328           **
10329           ** On Disabling hover selection feedback via <tt>NONE</tt>:
10330           ** <p>
10331           ** 
10332           ** <blockquote><small>
10333           ** Note that if the border width of the host symbol is negative,
10334           ** consistent with a 0 x 0 px <tt>BOX_CENTER</tt> symbol type, an
10335           ** external border will still appear around the
10336           ** <tt>SymbolType.NONE</tt> symbol. Because the default hover
10337           ** selection border width is <tt>-1</tt>, when passing
10338           ** <tt>SymbolType.NONE</tt> to
10339           ** <tt>setHoverSelectionSymbolType</tt>, you generally will also
10340           ** need to add a code line such as:
10341           **
10342           ** <pre>
10343           **   getCurve().getSymbol().setHoverSelectionBorderWidth(0);
10344           ** </pre>
10345           ** <p>
10346           **
10347           ** If your intention is to disable hover selection feedback,
10348           ** it's probably easier to just use
10349           ** <tt>setHoverSelectionEnabled(false)</tt>, rather than
10350           ** setting the hover selection symbol type to <tt>NONE</tt>.
10351           ** 
10352           **</small></blockquote>
10353           **
10354           ** @see #BOX_CENTER BOX_CENTER
10355           ** @see Symbol#setFillThickness setFillThickness
10356           ** @see Symbol#setHoverSelectionSymbolType
10357           ** setHoverSelectionSymbolType
10358           ** @see Symbol#setHoverSelectionEnabled setHoverSelectionEnabled
10359           **/
10360          public static SymbolType NONE = 
10361             new SymbolType(0, 0, 0, 0, 0, 0) {
10362             public double getAdjustedWidth(double width, double x,
10363                                         double xPrev, double xNext,
10364                                         double xMin, double xMax, double xMid) {
10365                return 0;
10366             }
10367             public double getAdjustedHeight(double height, double y,
10368                                          double yPrev, double yNext,
10369                                          double yMin, double yMax, double yMid) {
10370                return 0;
10371             }
10372             int getIconHeight(int legendFontSize) {
10373                return 0;
10374             }
10375             int getIconWidth(int legendFontSize) {
10376                return 0;
10377             }
10378    
10379          };
10380          /**
10381           ** Draws a pie slice whose area is shaded using horizontal
10382           ** bars.
10383           **
10384           ** <p>
10385           ** The vertical distance between corresponding edges of
10386           ** successive bars is governed by the symbol's fill
10387           ** spacing property; the height of each bar is defined by
10388           ** the symbol's fill thickness property; the border and
10389           ** background of each shading bar are defined by the
10390           ** symbol's border color, border width, border style, and background
10391           ** color properties.
10392           **
10393           ** <p> The radius of the pie slice (length of the non-arc
10394           ** sides of the slice) is chosen such that a circle with
10395           ** this radius circumscribes the host <tt>Symbol</tt>'s
10396           ** width/height determined rectangle. The slice pivot point
10397           ** is defined by each point's x,y position, and the
10398           ** orientation and size of the slice by the corresponding
10399           ** properties (see links below) of the host <tt>Symbol</tt>.
10400           **
10401           ** @see Symbol#setFillSpacing setFillSpacing
10402           ** @see Symbol#setFillThickness setFillThickness
10403           ** @see Symbol#setBorderColor setBorderColor
10404           ** @see Symbol#setBorderWidth setBorderWidth
10405           ** @see Symbol#setBackgroundColor setBackgroundColor
10406           ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
10407           ** @see Symbol#setPieSliceSize setPieSliceSize
10408           ** @see Curve.Point#setX setX
10409           ** @see Curve.Point#setY setY
10410           ** @see #PIE_SLICE_VERTICAL_SHADING PIE_SLICE_VERTICAL_SHADING
10411           ** @see #PIE_SLICE_HATCHED_SHADING PIE_SLICE_HATCHED_SHADING
10412           ** @see #PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
10413           ** @see Symbol Symbol
10414           ** 
10415           **
10416           **/
10417          public static SymbolType PIE_SLICE_HORIZONTAL_SHADING =
10418            new PieSliceSymbolType(true, false, false, 0, 0, 0, 0);
10419          /**
10420           ** Draws a pie slice whose area is shaded using vertical
10421           ** bars. 
10422           ** <p>
10423           ** 
10424           ** The horizontal distance between corresponding edges of
10425           ** successive bars is governed
10426           ** by the symbol's fill spacing property; the width
10427           ** of each bar is defined by the symbol's fill thickness
10428           ** property; the border and background of each
10429           ** shading bar are defined by the symbol's border color,
10430           ** border width, and background color properties.
10431           ** 
10432           ** <p> The radius of the pie slice (length of the non-arc
10433           ** sides of the slice) is chosen such that a circle with
10434           ** this radius circumscribes the host <tt>Symbol</tt>'s
10435           ** width/height determined rectangle. The slice pivot point
10436           ** is defined by each point's x,y position, and the
10437           ** orientation and size of the slice by the corresponding
10438           ** properties (see links below) of the host <tt>Symbol</tt>.
10439           **
10440           ** @see Symbol#setFillSpacing setFillSpacing
10441           ** @see Symbol#setFillThickness setFillThickness
10442           ** @see Symbol#setBorderColor setBorderColor
10443           ** @see Symbol#setBorderWidth setBorderWidth
10444           ** @see Symbol#setBackgroundColor setBackgroundColor
10445           ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
10446           ** @see Symbol#setPieSliceSize setPieSliceSize
10447           ** @see Curve.Point#setX setX
10448           ** @see Curve.Point#setY setY
10449           ** @see #PIE_SLICE_HORIZONTAL_SHADING PIE_SLICE_HORIZONTAL_SHADING
10450           ** @see #PIE_SLICE_HATCHED_SHADING PIE_SLICE_HATCHED_SHADING
10451           ** @see #PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
10452           ** @see Symbol Symbol
10453           ** 
10454           **
10455           **/
10456          public static SymbolType PIE_SLICE_VERTICAL_SHADING =
10457            new PieSliceSymbolType(false, true, false, 0, 0, 0, 0);
10458          /**
10459           ** Draws a pie slice whose area is shaded using both vertical
10460           ** and horizontal bars, which produces a "cross-hatched"
10461           ** pattern.
10462           ** <p>
10463           **
10464           ** The distance between corresponding edges of successive
10465           ** bars is governed by the symbol's fill spacing
10466           ** property; the thickness of each bar is defined by the
10467           ** symbol's fill thickness property; the border and
10468           ** background of each shading bar are defined by the
10469           ** symbol's border color, border width, border style, and background
10470           ** color properties.
10471           ** 
10472           ** <p> The radius of the pie slice (length of the non-arc
10473           ** sides of the slice) is chosen such that a circle with
10474           ** this radius circumscribes the host <tt>Symbol</tt>'s
10475           ** width/height determined rectangle. The slice pivot point
10476           ** (i.e. pie center) is defined by each point's x,y position, and the
10477           ** orientation and size of the slice by the corresponding
10478           ** properties (see links below) of the host <tt>Symbol</tt>.
10479           **
10480           ** @see Symbol#setFillSpacing setFillSpacing
10481           ** @see Symbol#setFillThickness setFillThickness
10482           ** @see Symbol#setBorderColor setBorderColor
10483           ** @see Symbol#setBorderWidth setBorderWidth
10484           ** @see Symbol#setBackgroundColor setBackgroundColor
10485           ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
10486           ** @see Symbol#setPieSliceSize setPieSliceSize
10487           ** @see Curve.Point#setX setX
10488           ** @see Curve.Point#setY setY
10489           ** @see #PIE_SLICE_VERTICAL_SHADING PIE_SLICE_VERTICAL_SHADING
10490           ** @see #PIE_SLICE_HORIZONTAL_SHADING PIE_SLICE_HORIZONTAL_SHADING
10491           ** @see #PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
10492           ** @see Symbol Symbol
10493           ** 
10494           **
10495           **/
10496          public static SymbolType PIE_SLICE_HATCHED_SHADING =
10497            new PieSliceSymbolType(true, true, false, 0, 0, 0, 0);
10498          /**
10499           ** Draw a pie slice whose area is shaded using either
10500           ** vertical bars or horizontal bars--whichever
10501           ** renders the slice more efficiently. Specifically, pie
10502           ** slices that are wider than they are tall use horizontal
10503           ** shading and pie slices that are taller than they are
10504           ** wide use vertical shading. These choices minimize the
10505           ** the number of shading bars (and thus memory and time)
10506           ** required to render the pie slice.
10507           ** 
10508           ** <p>
10509           ** 
10510           ** The distance between corresponding edges of successive
10511           ** bars is governed by the symbol's fill spacing property;
10512           ** the thickness of each bar is defined by the symbol's
10513           ** fill thickness property; the border and background of
10514           ** each shading bar are defined by the symbol's border
10515           ** color, border width, and background color properties.
10516           ** <p>
10517           ** 
10518           ** The pie slice radius is always determined by the
10519           ** formula:
10520           **
10521           ** <p>
10522           ** <blockquote>
10523           **   <pre>
10524           **   sqrt(symbolWidth^2+symbolHeight^2)/2
10525           **   </pre>
10526           ** </blockquote>
10527           **
10528           ** <p>
10529           ** Here <tt>symbolWidth</tt> and <tt>symbolHeight</tt> are the pie
10530           ** slice symbol's width and height, in pixels.
10531           ** <p>
10532           ** 
10533           ** Note that this formula implies that the pie slice
10534           ** radius is the one associated with the circle that
10535           ** circumscribes the symbol, that is, the smallest circle
10536           ** that is big enough to completely contain the symbol's
10537           ** width/height defined bounding rectangle. Equivalently,
10538           ** the length of the pie slice radius equals the half the
10539           ** length of the diagonal across the symbol's bounding
10540           ** rectangle.
10541           ** 
10542           ** <p>
10543           **
10544           ** To assure an integral number of shading bars and thus
10545           ** improve the visual look of the pie chart, GChart
10546           ** automatically rounds the radius to the nearest
10547           ** multiple of the specified <tt>fillSpacing</tt>.  For
10548           ** example, if the radius computed from the above formula
10549           ** were 96 pixels and the <tt>fillSpacing</tt> were 10
10550           ** pixels, GChart would actually use a radius of 100 pixels.
10551           ** 
10552           ** <p>
10553           **
10554           ** <i>Tip:</i> To produce a pie slice with a radius, r, set
10555           ** the symbol's height to 0, and its width to 2*r (or
10556           ** visa-versa). To specify the radius in pixels, use the
10557           ** symbol's <tt>setWidth</tt> and <tt>setHeight</tt>
10558           ** methods; to specify the radius in "model units" (which
10559           ** scale up or down with the chart dimensions) use
10560           ** <tt>setModelWidth</tt> and <tt>setModelHeight</tt> instead.
10561           ** 
10562           ** <p>
10563           **
10564           ** 
10565           ** <p>
10566           ** The slice pivot point (i.e. pie center)
10567           ** is defined by each point's x,y position, and the
10568           ** orientation and size of the slice by the
10569           ** <tt>setPieSliceOrientation</tt> and
10570           ** <tt>setPieSliceSize</tt> methods
10571           ** of the host <tt>Symbol</tt>.
10572           ** <p>
10573           **
10574           ** Creating a pie chart from such pie slices requires
10575           ** that you define a separate curve for each slice,
10576           ** as illustrated in the code below:
10577           **
10578           ** {@code.sample ..\..\..\..\..\..\gcharttestapp\src\com\googlecode\gchart\gcharttestapp\client\GChartExample09.java}
10579           **
10580           ** <p>
10581           ** 
10582           ** Which produces this: <p>
10583           ** 
10584           ** <img
10585           ** src="{@docRoot}/com/googlecode/gchart/client/doc-files/gchartexample09.png">
10586           **
10587           ** <p> Note how, because
10588           ** <tt>PIE_SLICE_OPTIMAL_SHADING</tt> was used, vertical
10589           ** or horizontal shading is automatically selected so as
10590           ** to minimize the number of shading bars in each slice.
10591           **
10592           ** @see Symbol Symbol
10593           ** @see Symbol#setFillSpacing setFillSpacing
10594           ** @see Symbol#setFillThickness setFillThickness
10595           ** @see Symbol#setBorderColor setBorderColor
10596           ** @see Symbol#setBorderWidth setBorderWidth
10597           ** @see Symbol#setBackgroundColor setBackgroundColor
10598           ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
10599           ** @see Symbol#setPieSliceSize setPieSliceSize
10600           ** @see Symbol#setWidth setWidth
10601           ** @see Symbol#setHeight setHeight
10602           ** @see Symbol#setModelWidth setModelWidth
10603           ** @see Symbol#setModelHeight setModelHeight
10604           ** @see Curve.Point#setX setX
10605           ** @see Curve.Point#setY setY
10606           ** @see #PIE_SLICE_VERTICAL_SHADING PIE_SLICE_VERTICAL_SHADING
10607           ** @see #PIE_SLICE_HORIZONTAL_SHADING PIE_SLICE_HORIZONTAL_SHADING
10608           ** @see #PIE_SLICE_HATCHED_SHADING PIE_SLICE_HATCHED_SHADING
10609           **
10610           **/
10611          public static SymbolType PIE_SLICE_OPTIMAL_SHADING =
10612            new PieSliceSymbolType(false, false, true, 0, 0, 0, 0);
10613          
10614          /**
10615           ** Use vertical bars that extend from the x,y position
10616           ** associated with each point, to the y position defined
10617           ** by the host <tt>Symbol</tt>'s baseline property, and that are
10618           ** horizontally centered on the data point.
10619           **
10620           ** @see Symbol Symbol
10621           ** @see Symbol#setBaseline setBaseline
10622           ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
10623           ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
10624           ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
10625           ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
10626           ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
10627           ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
10628           ** 
10629           **/ 
10630          public static SymbolType VBAR_BASELINE_CENTER = new VBarBaseline(0,0);
10631          /**
10632           ** Use vertical bars that extend from the x,y position
10633           ** associated with each point, to the y position defined
10634           ** by the host <tt>Symbol</tt>'s baseline property, and whose
10635           ** right edge passes through the data point.
10636           **
10637           ** @see Symbol Symbol
10638           ** @see Symbol#setBaseline setBaseline
10639           ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
10640           ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
10641           ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
10642           ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
10643           ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
10644           ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
10645           ** 
10646           **/ 
10647          public static SymbolType VBAR_BASELINE_WEST = new VBarBaseline(-1,0);
10648          /**
10649           ** Use vertical bars that extend from the x,y position
10650           ** associated with each point, to the y position defined
10651           ** by the host <tt>Symbol</tt>'s baseline property, and whose
10652           ** left edge passes through the data point.
10653           **
10654           ** @see Symbol#setBaseline setBaseline
10655           ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
10656           ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
10657           ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
10658           ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
10659           ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
10660           ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
10661           ** 
10662           **/ 
10663          public static SymbolType VBAR_BASELINE_EAST = new VBarBaseline(1,0);
10664          /**
10665           ** @deprecated
10666           ** 
10667           ** As of version 2.4, this symbol has been redefined to
10668           ** be synonomous with the LINE symbol type.
10669           ** <p>
10670           **
10671           ** Prior to v2.4, this symbol drew a vertical bar from
10672           ** each point to the y coordinate of the next point. Some
10673           ** applications may need to use a revised point set in
10674           ** order to produce the same curves with <tt>LINE</tt>
10675           ** that they used to produce with this symbol.
10676           ** <p>
10677           **
10678           ** Support was dropped because:
10679           ** 
10680           ** <p>
10681           ** <ol>
10682           ** 
10683           **   <li>Continued support would have complicated
10684           ** implementation of the new hover feedback system
10685           ** introduced with v2.4 (these are the only symbols whose
10686           ** hit-testing-related size depends on preceding or
10687           ** subsequent points).<p>
10688           **
10689           **   <li>With the introduction of
10690           ** <tt>LINE</tt> the main reason for this, and related,
10691           ** vertically (or horizontally) constrained line drawing
10692           ** symbol types had been eliminated (had <tt>LINE</tt>
10693           ** existed at the beginning, these constrained line drawing
10694           ** symbol types would never have been added).
10695           ** <p>
10696           ** 
10697           ** </ol>
10698           ** <p>
10699           ** 
10700           ** Finally, note that if lines are vertical or horizontal,
10701           ** and solidly connected, <tt>LINE</tt> automatically
10702           ** collapses them into a single element, so no
10703           ** element-based efficiency losses need be associated with
10704           ** replacing curves using such rectilinear symbol types
10705           ** with equivalent curves rendered via the
10706           ** <tt>LINE</tt> symbol type.
10707           ** <p>
10708           ** 
10709           ** 
10710           ** @see #HBAR_PREV HBAR_PREV
10711           ** @see #HBAR_NEXT HBAR_NEXT
10712           ** @see #LINE LINE
10713           ** @see #VBAR_PREV VBAR_PREV
10714           **
10715           **/
10716          public static SymbolType VBAR_NEXT = line;
10717    /*
10718            new SymbolType(0,0, 0, 0, 0.5, 0.5) {
10719             public double getAdjustedHeight(double height,
10720                                          double y,
10721                                          double yPrev, double yNext,
10722                                          double yMin, double yMax,
10723                                          double yMid) {
10724                // x!=x is a faster isNaN
10725                return yNext - y;
10726             }
10727             public double getUpperLeftY(double height, double y,
10728                                      double yPrev, double yNext,
10729                                      double yMin, double yMax, double yMid) {
10730                return y;
10731             }
10732             int getIconHeight(int legendFontSize) {
10733                return legendFontSize;
10734             }
10735             int getIconWidth(int legendFontSize) {
10736                return (int) Math.round(legendFontSize/4.);
10737             }
10738    
10739          };
10740    */      
10741          /** Use vertical bars that extend from the top of the chart
10742           ** to each point on the curve, and are horizontally
10743           ** centered on the point.
10744           **/ 
10745          public static SymbolType VBAR_NORTH = new VBarTop(0, -1);
10746          /** Use vertical bars that extend from the top of the chart
10747           ** to each point on the curve, and are horizontally
10748           ** to the right of the point.
10749           **/ 
10750          public static SymbolType VBAR_NORTHEAST = new VBarTop(1, -1);
10751          
10752          /** Use vertical bars that extend from the top of the chart
10753           ** to each point on the curve, and are horizontally
10754           ** to the left of the point.
10755           **/ 
10756          public static SymbolType VBAR_NORTHWEST = new VBarTop(-1, -1);
10757          /**
10758           ** @deprecated
10759           **
10760           ** As of version 2.4, this symbol has been redefined to
10761           ** be synonomous with the LINE symbol type.
10762           ** <p>
10763           **
10764           ** Prior to v2.4, this symbol drew a vertical bar from
10765           ** each point to the y coordinate of the previous point.
10766           ** Some applications may need to use a revised point set
10767           ** in order to produce the same curves using <tt>LINE</tt>
10768           ** that they used to produce with this symbol.  <p>
10769           **
10770           ** See the discussion within the {@link #VBAR_NEXT
10771           ** VBAR_NEXT} symbol for more information about why
10772           ** support for these vertically and horizontally constrained
10773           ** connecting line symbol types was dropped.
10774           ** 
10775           ** @see #LINE LINE
10776           ** @see #HBAR_PREV HBAR_PREV
10777           ** @see #HBAR_NEXT HBAR_NEXT
10778           ** @see #VBAR_NEXT VBAR_NEXT
10779           ** 
10780           **/
10781          
10782          public static SymbolType VBAR_PREV = line;
10783    /*         new SymbolType(0,0,0,0,0.5,0.5) {
10784             public double getAdjustedHeight(double height, double y,
10785                                          double yPrev, double yNext,
10786                                          double yMin, double yMax, double yMid) {
10787                return yPrev - y;
10788             }
10789             public double getUpperLeftY(double height, double y,
10790                                      double yPrev, double yNext,
10791                                      double yMin, double yMax, double yMid) {
10792                return y;
10793             }
10794             int getIconHeight(int legendFontSize) {
10795                return legendFontSize;
10796             }
10797             int getIconWidth(int legendFontSize) {
10798                return (int) Math.round(legendFontSize/4.);
10799             }
10800    
10801          };
10802    */      
10803          /** Use vertical bars that extend from the x-axis
10804           ** to each point on the curve, and that are horizontally
10805           ** centered on the point.
10806           **/ 
10807          public static SymbolType VBAR_SOUTH = new VBarBottom(0, 1);
10808          /** Use vertical bars that extend from the x-axis
10809           ** to each point on the curve, and that are horizontally
10810           ** to the right of the point.
10811           **/ 
10812          public static SymbolType VBAR_SOUTHEAST = new VBarBottom(1, 1);
10813          
10814          /** Use vertical bars that extend from the x-axis
10815           ** to each point on the curve, and that are horizontally
10816           ** to the left of the point.
10817           **/ 
10818          public static SymbolType VBAR_SOUTHWEST =
10819              new VBarBottom(-1, 1);
10820          /**
10821           ** Represents a single x-axis grid-line. You can use
10822           ** this symbol to draw a single vertical bar
10823           ** across the chart. 
10824           ** 
10825           **/
10826          public static SymbolType XGRIDLINE = 
10827             new SymbolType(0,0,0,0,0.5,0.5,Boolean.FALSE) {
10828             public double getAdjustedHeight(double height, double y,
10829                                            double yPrev, double yNext,
10830                                            double yMin, double yMax, double yMid) {
10831                return yMax - yMin;
10832             }
10833             public double getUpperLeftY(double height, double y,
10834                                      double yPrev, double yNext,
10835                                      double yMin, double yMax, double yMid,
10836                                      int yMouse) {
10837                return yMin;
10838             }
10839             int getIconHeight(int legendFontSize) {
10840                return legendFontSize;
10841             }
10842             int getIconWidth(int legendFontSize) {
10843                return 1;
10844             }
10845    
10846          };
10847          /**
10848           ** Represents a single y-axis (or y2-axis) grid-line. You
10849           ** can use this symbol to draw a single horizontal line (or
10850           ** bar) across the chart, for example, to display an upper
10851           ** bound or control limit.
10852           ** 
10853           **/ 
10854          public static SymbolType YGRIDLINE = 
10855             new SymbolType(0,0,0.5,0.5,0,0, Boolean.TRUE) {
10856             public double getAdjustedWidth(double width, double x,
10857                                         double xPrev, double xNext,
10858                                         double xMin, double xMax, double xMid) {
10859                return xMax - xMin;
10860             }
10861             public double getUpperLeftX(double width, double x,
10862                                         double xPrev, double xNext,
10863                                         double xMin, double xMax, double xMid,
10864                                         int xMouse) {
10865                return xMin;
10866             }
10867             int getIconHeight(int legendFontSize) {
10868                return 1;
10869             }
10870             int getIconWidth(int legendFontSize) {
10871                return legendFontSize;
10872             }
10873    
10874          };
10875    
10876          /**
10877           **  @deprecated
10878           ** 
10879           ** This symbol is the same as <tt>YGRIDLINE</tt> and
10880           ** was added by mistake in version 1.
10881           ** (the y-axis isn't defined by the symbol type, but
10882           **  rather by the curve's <tt>setYAxis</tt> method).
10883           ** <p>
10884           ** Please use <tt>YGRIDLINE</tt> instead.
10885           **
10886           ** @see #YGRIDLINE YGRIDLINE
10887           ** @see GChart.Curve#setYAxis setYAxis
10888           ** 
10889           **/
10890         
10891          public static SymbolType Y2GRIDLINE = YGRIDLINE;
10892    
10893         // play similar role as same-named fields of AnnotationLocation
10894          protected int heightMultiplier;
10895          protected int widthMultiplier;
10896           /*
10897           * If a symbol's left, right, top, or bottom edge
10898           * represents the x or y location associated with this
10899           * symbol, the corresponding pixel paddings are 0.
10900           * 
10901           * If a position 1/2 pixel to the right, left, below, or
10902           * above (respectively) those edges represents the position
10903           * of the x or y in question, the values are 0.5.
10904           *
10905           * Why is this needed? Because GChart uses 1 px gridlines
10906           * whose center represents the point associated with the
10907           * gridline, and to get a corresponding edge to perfectly
10908           * overlay a gridline when its associated position is
10909           * the same as that gridline, we need to associated the
10910           * position of the represented x or y coordinate not
10911           * with the symbol's box edge itself, but rather
10912           * with a position 1/2 px towards the center of the
10913           * symbol. If you don't specify an extra half pixel
10914           * for, say, a vertical bar, it won't align right
10915           * on top of gridlines when it has the same height
10916           * as the associated gridline.
10917           * <p>
10918           *
10919           * In effect, we deliberately add a 1/2 px error to
10920           * certain symbols so that they appear to line up
10921           * perfectly with the gridlines.
10922           *
10923           */
10924          protected double pixelPadLeft;
10925          protected double pixelPadRight;
10926          protected double pixelPadTop;
10927          protected double pixelPadBottom;
10928         
10929          // symbols are part of the internals of a GChart,
10930          // so only we should instantiate them.
10931          private SymbolType(int widthMultiplier,
10932                             int heightMultiplier,
10933                             double pixelPadLeft,
10934                             double pixelPadRight,
10935                             double pixelPadTop,
10936                             double pixelPadBottom,
10937                             Boolean isHorizontallyBanded) {
10938             validateMultipliers(widthMultiplier, heightMultiplier);
10939             this.widthMultiplier = widthMultiplier;
10940             this.heightMultiplier = heightMultiplier;
10941             this.pixelPadLeft = pixelPadLeft;
10942             this.pixelPadRight = pixelPadRight;
10943             this.pixelPadTop = pixelPadTop;
10944             this.pixelPadBottom = pixelPadBottom;
10945             this.isHorizontallyBanded = isHorizontallyBanded;
10946          }
10947          
10948          private SymbolType(int widthMultiplier,
10949                             int heightMultiplier,
10950                             double pixelPadLeft,
10951                             double pixelPadRight,
10952                             double pixelPadTop,
10953                             double pixelPadBottom) {
10954             this(widthMultiplier,heightMultiplier,
10955                  pixelPadLeft,
10956                  pixelPadRight,
10957                  pixelPadTop,
10958                  pixelPadBottom, null);
10959          }
10960    
10961    
10962          double getAdjustedHeight(double height, double y,
10963                                            double yPrev, double yNext,
10964                                            double yMin, double yMax, double yMid) {
10965             return height;
10966          }
10967    
10968          double getAdjustedWidth(double width, double x,
10969                                          double xPrev, double xNext,
10970                                          double xMin, double xMax, double xMid) {
10971             return width;
10972          }
10973    
10974          /*
10975           * Pixel x-coordinate at center of bounding rectangle surrounding
10976           * given symbol rendered with this symbol type.
10977           * <p>
10978           * 
10979           * This method defines the x-coordinate of the rendered
10980           * symbol's center-point (used for hit testing purposes) for all
10981           * symbols except pie slices.
10982           * 
10983           */
10984          protected double getCenterX(PlotPanel pp, Symbol symbol,
10985                                      double prevX, double x, double nextX) {
10986            double xMin = pp.getXMin();
10987            double xMax = pp.getXMax(); 
10988            double xMid = symbol.getBaseline();
10989            // x!=x is a faster isNaN
10990            if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
10991            double xMinPx = pp.xToPixel(xMin);
10992            double xMaxPx = pp.xToPixel(xMax);
10993            double xMidPx = pp.xToPixel(xMid);
10994            double xPx = pp.xToPixel(x);
10995            double prevXPx = pp.xToPixel(prevX);
10996            double nextXPx = pp.xToPixel(nextX);
10997            double width = symbol.getWidth(pp);
10998            
10999            double symWidth = getAdjustedWidth(width, xPx,
11000                                              prevXPx, nextXPx,
11001                                              xMinPx, xMaxPx, xMidPx);
11002            if ((symWidth!=symWidth)) return Double.NaN;
11003            
11004            double xLeft = getUpperLeftX(width, xPx,
11005                                         prevXPx, nextXPx,
11006                                         xMinPx, xMaxPx, xMidPx,
11007                                         pp.getXMousePlotArea());
11008            if ((xLeft!=xLeft)) return Double.NaN;
11009            
11010            double xCenter = xLeft + symWidth/2.;
11011    
11012            return xCenter;
11013    
11014          }
11015    
11016          /*
11017           * Pixel x-coordinate at center of the symbol, used for
11018           * hit-testing purposes by rectangular symbol types.
11019           * <p>
11020           * 
11021           * Overridden by pie slice symbol types.
11022           * 
11023           */
11024          protected double getCenterX(PlotPanel pp, Symbol symbol,
11025                                      int iPoint) {
11026    
11027            Curve c = symbol.getParent();
11028            Curve.Point p  = c.getPoint(iPoint);
11029            double prevX = Double.NaN;
11030            double x = p.getX();
11031            double nextX = Double.NaN;
11032            if (iPoint > 0)
11033                prevX = c.getPoint(iPoint-1).getX();
11034            if (iPoint+1 < c.getNPoints())
11035                nextX = c.getPoint(iPoint+1).getX();
11036             
11037            double result = getCenterX(pp, symbol,
11038                                       prevX, x, nextX);
11039    
11040            return result;
11041          }
11042    
11043          
11044          /*
11045           * Pixel y-coordinate at center of bounding rectangle surrounding
11046           * given symbol rendered with this symbol type.
11047           * <p>
11048           * 
11049           * This method defines the y-coordinate of the rendered
11050           * symbol's center-point (used for hit testing purposes) for all
11051           * symbols except pie slices.
11052           * 
11053           */
11054          protected double getCenterY(PlotPanel pp, Symbol symbol,
11055                         double prevY, double y, double nextY, boolean onY2) {
11056    
11057            // the cartesian data and pixel Y coordinates are
11058            // flipped, hence the (counter-intuitive) min/max
11059            // interchange below:
11060            double yMin =  onY2?pp.getY2Max():pp.getYMax();
11061            double yMax =  onY2?pp.getY2Min():pp.getYMin();
11062            double yMid = symbol.getBaseline();
11063            // x!=x is a faster isNaN
11064            if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
11065            double yMinPx = pp.yToPixel(yMin,onY2);
11066            double yMaxPx = pp.yToPixel(yMax,onY2);
11067            double yMidPx = pp.yToPixel(yMid,onY2);
11068            double yPx = pp.yToPixel(y, onY2);
11069            double prevYPx = pp.yToPixel(prevY, onY2);
11070            double nextYPx = pp.yToPixel(nextY, onY2);
11071            double height = symbol.getHeight(pp, onY2);
11072    
11073            double symHeight = getAdjustedHeight(height, yPx,
11074                                   prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
11075            if ((symHeight!=symHeight)) return Double.NaN;
11076            
11077            double yTop = getUpperLeftY(height, yPx,
11078                                        prevYPx, nextYPx,
11079                                        yMinPx, yMaxPx, yMidPx,
11080                                        pp.getYMousePlotArea());
11081            if ((yTop!=yTop)) return Double.NaN;
11082            
11083            double yCenter =  yTop + symHeight/2.;
11084    
11085            return yCenter;
11086    
11087          }
11088    
11089          /*
11090           * Pixel y-coordinate at center of the symbol, used for
11091           * hit-testing purposes by rectangular symbol types.
11092           * <p>
11093           * 
11094           * Overridden by pie slice symbol types.
11095           * 
11096           */
11097          protected double getCenterY(PlotPanel pp, Symbol symbol,
11098                                      int iPoint,
11099                                      boolean onY2) {
11100    
11101             Curve c = symbol.getParent();
11102             Curve.Point p = c.getPoint(iPoint);
11103             double prevY = Double.NaN;
11104             double y = p.getY();
11105             double nextY = Double.NaN;
11106             if (iPoint > 0)
11107                prevY = c.getPoint(iPoint-1).getY();
11108             if (iPoint+1 < c.getNPoints())
11109                nextY = c.getPoint(iPoint+1).getY();
11110             
11111             double result = getCenterY(pp, symbol,
11112                                        prevY, y, nextY, onY2);
11113    
11114             return result;
11115          }
11116    
11117          // pixel coordinate of left edge of symbol if rendered at given x
11118          // Note: this can actually be the right edge if the symbol
11119          // width is negative, as can occur with baseline-based bars
11120          protected double getEdgeLeft(PlotPanel pp, Symbol symbol,
11121                                       double x, boolean onY2) {
11122            double xMin = pp.getXMin();
11123            double xMax = pp.getXMax(); 
11124            double xMid = symbol.getBaseline();
11125            // x!=x is a faster isNaN
11126            if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
11127            double xMinPx = pp.xToPixel(xMin);
11128            double xMaxPx = pp.xToPixel(xMax);
11129            double xMidPx = pp.xToPixel(xMid);
11130            double xPx = pp.xToPixel(x);
11131            double prevXPx = Double.NaN;
11132            double nextXPx = Double.NaN;
11133            double width = symbol.getWidth(pp);
11134            
11135            double symWidth = getAdjustedWidth(width, xPx,
11136                                              prevXPx, nextXPx,
11137                                              xMinPx, xMaxPx, xMidPx);
11138            if ((symWidth!=symWidth)) return Double.NaN;
11139            
11140            double xLeft = getUpperLeftX(width, xPx,
11141                                         prevXPx, nextXPx,
11142                                         xMinPx, xMaxPx, xMidPx,
11143                                         pp.getXMousePlotArea());
11144            if ((xLeft!=xLeft)) return Double.NaN;
11145            double result = xLeft;
11146            return result;
11147          }
11148    
11149          // pixel coordinate of right edge of symbol if rendered at given x
11150          // Note: this can actually be the left edge if the symbol
11151          // width is negative, as can occur with baseline-based bars
11152          protected double getEdgeRight(PlotPanel pp, Symbol symbol,
11153                                        double x, boolean onY2) {
11154            double xMin = pp.getXMin();
11155            double xMax = pp.getXMax(); 
11156            double xMid = symbol.getBaseline();
11157            // x!=x is a faster isNaN
11158            if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
11159            double xMinPx = pp.xToPixel(xMin);
11160            double xMaxPx = pp.xToPixel(xMax);
11161            double xMidPx = pp.xToPixel(xMid);
11162            double xPx = pp.xToPixel(x);
11163            double prevXPx = Double.NaN;
11164            double nextXPx = Double.NaN;
11165            double width = symbol.getWidth(pp);
11166            
11167            double symWidth = getAdjustedWidth(width, xPx,
11168                                              prevXPx, nextXPx,
11169                                              xMinPx, xMaxPx, xMidPx);
11170            if ((symWidth!=symWidth)) return Double.NaN;
11171            
11172            double xLeft = getUpperLeftX(width, xPx,
11173                                         prevXPx, nextXPx,
11174                                         xMinPx, xMaxPx, xMidPx,
11175                                         pp.getXMousePlotArea());
11176            if ((xLeft!=xLeft)) return Double.NaN;
11177            
11178            double result = xLeft + symWidth;
11179    
11180            return result;
11181    
11182          }
11183    
11184    
11185          // pixel coordinate of top edge of symbol if rendered at given y
11186          // Note: this can actually be the bottom edge if the symbol
11187          // width is negative, as can occur with baseline-based bars
11188          protected double getEdgeTop(PlotPanel pp, 
11189                                      Symbol symbol,
11190                                      double y,
11191                                      boolean onY2) {
11192    
11193            // the cartesian data and pixel Y coordinates are
11194            // flipped, hence the (counter-intuitive) min/max
11195            // interchange below:
11196            double yMin =  onY2?pp.getY2Max():pp.getYMax();
11197            double yMax =  onY2?pp.getY2Min():pp.getYMin();
11198            double yMid = symbol.getBaseline();
11199            // x!=x is a faster isNaN
11200            if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
11201            double yMinPx = pp.yToPixel(yMin,onY2);
11202            double yMaxPx = pp.yToPixel(yMax,onY2);
11203            double yMidPx = pp.yToPixel(yMid,onY2);
11204            double yPx = pp.yToPixel(y, onY2);
11205            double prevYPx = Double.NaN;
11206            double nextYPx = Double.NaN;
11207            double height = symbol.getHeight(pp, onY2);
11208    
11209            double symHeight = getAdjustedHeight(height, yPx,
11210                                   prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
11211            if ((symHeight!=symHeight)) return Double.NaN;
11212            
11213            double yTop = getUpperLeftY(height, yPx,
11214                                        prevYPx, nextYPx,
11215                                        yMinPx, yMaxPx, yMidPx,
11216                                        pp.getYMousePlotArea());
11217            if ((yTop!=yTop)) return Double.NaN;
11218            
11219            double result = yTop;
11220    
11221            return result;
11222    
11223          }
11224    
11225    
11226          // pixel coordinate of bottom edge of symbol if rendered at given y
11227          // Note: this can actually be the top edge if the symbol
11228          // width is negative, as can occur with baseline-based bars
11229          protected double getEdgeBottom(PlotPanel pp, 
11230                                         Symbol symbol,
11231                                         double y,
11232                                         boolean onY2) {
11233    
11234            // the cartesian data and pixel Y coordinates are
11235            // flipped, hence the (counter-intuitive) min/max
11236            // interchange below:
11237            double yMin =  onY2?pp.getY2Max():pp.getYMax();
11238            double yMax =  onY2?pp.getY2Min():pp.getYMin();
11239            double yMid = symbol.getBaseline();
11240            // x!=x is a faster isNaN
11241            if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
11242            double yMinPx = pp.yToPixel(yMin,onY2);
11243            double yMaxPx = pp.yToPixel(yMax,onY2);
11244            double yMidPx = pp.yToPixel(yMid,onY2);
11245            double yPx = pp.yToPixel(y, onY2);
11246            double prevYPx = Double.NaN;
11247            double nextYPx = Double.NaN;
11248            double height = symbol.getHeight(pp, onY2);
11249    
11250            double symHeight = getAdjustedHeight(height, yPx,
11251                                   prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
11252            if ((symHeight!=symHeight)) return Double.NaN;
11253            
11254            double yTop = getUpperLeftY(height, yPx,
11255                                        prevYPx, nextYPx,
11256                                        yMinPx, yMaxPx, yMidPx,
11257                                        pp.getYMousePlotArea());
11258            if ((yTop!=yTop)) return Double.NaN;
11259            
11260            double result = yTop+symHeight;
11261    
11262            return result;
11263    
11264          }
11265    
11266          // gets edge that is furthest away from the point, horizontally
11267          // Note: for bar charts, this is the edge of the symbol
11268          // along the y-axis, y2-axis, or vertical baseline.
11269          protected double getEdgeOppositeHorizontally(
11270                                        PlotPanel pp, Symbol symbol,
11271                                        double x, boolean onY2) {
11272            double xMin = pp.getXMin();
11273            double xMax = pp.getXMax(); 
11274            double xMid = symbol.getBaseline();
11275            // x!=x is a faster isNaN
11276            if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
11277            double xMinPx = pp.xToPixel(xMin);
11278            double xMaxPx = pp.xToPixel(xMax);
11279            double xMidPx = pp.xToPixel(xMid);
11280            double xPx = pp.xToPixel(x);
11281            double prevXPx = Double.NaN;
11282            double nextXPx = Double.NaN;
11283            double width = symbol.getWidth(pp);
11284            
11285            double symWidth = getAdjustedWidth(width, xPx,
11286                                              prevXPx, nextXPx,
11287                                              xMinPx, xMaxPx, xMidPx);
11288            if ((symWidth!=symWidth)) return Double.NaN;
11289            
11290            double xLeft = getUpperLeftX(width, xPx,
11291                                         prevXPx, nextXPx,
11292                                         xMinPx, xMaxPx, xMidPx,
11293                                         pp.getXMousePlotArea());
11294            if ((xLeft!=xLeft)) return Double.NaN;
11295    
11296            double result = xLeft + symWidth;
11297            if (Math.abs(xLeft - xPx) > Math.abs(result - xPx))
11298               result = xLeft;
11299    
11300            return result;
11301    
11302          }
11303    
11304          // gets edge that is furthest away from the point, vertically
11305          // Note: for bar charts, this is the edge of the symbol
11306          // along the x-axis, x2-axis, or horizontal baseline.
11307          protected double getEdgeOppositeVertically(PlotPanel pp, 
11308                                         Symbol symbol,
11309                                         double y,
11310                                         boolean onY2) {
11311    
11312            // the cartesian data and pixel Y coordinates are
11313            // flipped, hence the (counter-intuitive) min/max
11314            // interchange below:
11315            double yMin =  onY2?pp.getY2Max():pp.getYMax();
11316            double yMax =  onY2?pp.getY2Min():pp.getYMin();
11317            double yMid = symbol.getBaseline();
11318            // x!=x is a faster isNaN
11319            if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
11320            double yMinPx = pp.yToPixel(yMin,onY2);
11321            double yMaxPx = pp.yToPixel(yMax,onY2);
11322            double yMidPx = pp.yToPixel(yMid,onY2);
11323            double yPx = pp.yToPixel(y, onY2);
11324            double prevYPx = Double.NaN;
11325            double nextYPx = Double.NaN;
11326            double height = symbol.getHeight(pp, onY2);
11327    
11328            double symHeight = getAdjustedHeight(height, yPx,
11329                                   prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
11330            if ((symHeight!=symHeight)) return Double.NaN;
11331            
11332            double yTop = getUpperLeftY(height, yPx,
11333                                        prevYPx, nextYPx,
11334                                        yMinPx, yMaxPx, yMidPx,
11335                                        pp.getYMousePlotArea());
11336            if ((yTop!=yTop)) return Double.NaN;
11337            
11338            double result = yTop+symHeight;
11339            if (Math.abs(yTop - yPx) > Math.abs(result - yPx))
11340               result = yTop;
11341    
11342            return result;
11343    
11344          }
11345          
11346          /*
11347           * Determines if a symbol, rendered at the specified
11348           * position (and with the given positions of the previous
11349           * and subsequent points, and the y-axis on which it is
11350           * rendered) intersects with a given rectangle.
11351           * 
11352           */ 
11353          private boolean isIntersecting(
11354                         PlotPanel pp, Symbol symbol, 
11355                         double prevX, double x, double nextX, 
11356                         double prevY, double y, double nextY, boolean onY2,
11357                         double top, double right,
11358                         double bottom, double left) {
11359    
11360            double xMin = pp.getXMin();
11361            double xMax = pp.getXMax(); 
11362            double xMid = symbol.getBaseline();
11363            // x!=x is a faster isNaN
11364            if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
11365            double xMinPx = pp.xToPixel(xMin);
11366            double xMaxPx = pp.xToPixel(xMax);
11367            double xMidPx = pp.xToPixel(xMid);
11368            double xPx = pp.xToPixel(x);
11369            double prevXPx = pp.xToPixel(prevX);
11370            double nextXPx = pp.xToPixel(nextX);
11371            double width = symbol.getWidth(pp);
11372            
11373            double symWidth = getAdjustedWidth(width, xPx,
11374                                              prevXPx, nextXPx,
11375                                              xMinPx, xMaxPx, xMidPx);
11376            if ((symWidth!=symWidth)) return false;
11377            
11378            double xLeft = getUpperLeftX(width, xPx,
11379                                         prevXPx, nextXPx,
11380                                         xMinPx, xMaxPx, xMidPx,
11381                                         pp.getXMousePlotArea());
11382            if ((xLeft!=xLeft)) return false;
11383    
11384            // note: symWidth can be negative.
11385            if (Math.max(xLeft, xLeft + symWidth) < left)
11386               return false;  // symbol is entirely to left of rectangle
11387            else if (Math.min(xLeft, xLeft + symWidth) > right)
11388               return false; // symbol is entirely to right of rectangle
11389            // else brush and symbol have overlapping x-intervals 
11390            
11391            // the cartesian data and pixel Y coordinates are flipped,
11392            // hence the (counter-intuitive) min/max interchange below:
11393            double yMin =  onY2?pp.getY2Max():pp.getYMax();
11394            double yMax =  onY2?pp.getY2Min():pp.getYMin();
11395            double yMid = symbol.getBaseline();
11396            // x!=x is a faster isNaN
11397            if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
11398            double yMinPx = pp.yToPixel(yMin,onY2);
11399            double yMaxPx = pp.yToPixel(yMax,onY2);
11400            double yMidPx = pp.yToPixel(yMid,onY2);
11401            double yPx = pp.yToPixel(y, onY2);
11402            double prevYPx = pp.yToPixel(prevY, onY2);
11403            double nextYPx = pp.yToPixel(nextY, onY2);
11404            double height = symbol.getHeight(pp, onY2);
11405    
11406            double symHeight = getAdjustedHeight(height, yPx,
11407                                   prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
11408            if ((symHeight!=symHeight)) return false;
11409            
11410            double yTop = getUpperLeftY(height, yPx,
11411                                        prevYPx, nextYPx,
11412                                        yMinPx, yMaxPx, yMidPx,
11413                                        pp.getYMousePlotArea());
11414            if ((yTop!=yTop)) return false;
11415            
11416            // note: symHeight can be negative.
11417            if (Math.max(yTop, yTop + symHeight) < top)
11418               return false; // symbol is entirely above rectangle
11419            else if (Math.min(yTop, yTop + symHeight) > bottom)
11420               return false; // symbol is entirely below the rectangle
11421            // else rectangle and symbol have overlapping y-intervals 
11422    
11423            // overlapping x and y intervals ==> rectangle intersects symbol
11424            return true;
11425    
11426          }
11427          
11428          /*
11429           * Determines if a symbol, when rendered at a given point,
11430           * intersects with a "rectangular brush".
11431           * <p>
11432           * 
11433           * This brush is typically centered at the current mouse
11434           * position, and allows the user to select the point on a
11435           * curve, the pie slice, etc. for which hover feedback will
11436           * be displayed.
11437           * <p>
11438           *
11439           * This method gets overridden for pie slices (due to
11440           * their non-rectangular shape).
11441           * 
11442           */ 
11443          protected boolean isIntersecting(PlotPanel pp,
11444                                         Symbol symbol,
11445                                         int iPoint,
11446                                         boolean onY2,
11447                                         int xBrush,
11448                                         int yBrush,
11449                                         int brushWidth,
11450                                         int brushHeight) {
11451    
11452             Curve c = symbol.getParent();
11453             Curve.Point p = c.getPoint(iPoint);
11454             double prevX = Double.NaN;
11455             double x = p.getX();
11456             double nextX = Double.NaN;
11457             double prevY = Double.NaN;
11458             double y = p.getY();
11459             double nextY = Double.NaN;
11460             if (iPoint > 0) {
11461                prevX = c.getPoint(iPoint-1).getX();
11462                prevY = c.getPoint(iPoint-1).getY();
11463             }
11464             if (iPoint+1 < c.getNPoints()) {
11465                nextX = c.getPoint(iPoint+1).getX();
11466                nextY = c.getPoint(iPoint+1).getY();
11467             }
11468    
11469             // Treat mouse cursor as if it were a 0x0 pixel symbol
11470             // centered at xBrush, yBrush to which an annotation of
11471             // the width, height of the brush is attached.
11472             int top = symbol.getBrushLocation().getUpperLeftY(
11473                yBrush, brushHeight, 0);
11474             int bottom = top + brushHeight;
11475             int left = symbol.getBrushLocation().getUpperLeftX(
11476                xBrush, brushWidth, 0);
11477             int right = left + brushWidth;
11478             
11479             boolean result = isIntersecting(pp, symbol,
11480                                        prevX, x, nextX,    
11481                                        prevY, y, nextY, onY2,
11482                                        top, right, bottom, left); 
11483    
11484             return result;
11485          }
11486    
11487          
11488          // width of border of symbol displayed in legend key
11489          int getIconBorderWidth(int legendFontSize,
11490                                 double symBorderFraction) {
11491             int result = 0;
11492             if (symBorderFraction > 0) {
11493                result = (int) Math.max(1.0, Math.floor(
11494                   symBorderFraction * Math.min(
11495                     getIconWidth(legendFontSize),
11496                     getIconHeight(legendFontSize))));
11497             }
11498            return result;
11499         }
11500          int getIconHeight(int legendFontSize) {
11501             return (int) Math.round(0.75*legendFontSize);
11502          }
11503    
11504          int getIconWidth(int legendFontSize) {
11505             return (int) Math.round(0.75*legendFontSize);
11506          }   
11507          
11508          
11509          double getUpperLeftX(double width, double x,
11510                               double xPrev, double xNext,
11511                               double xMin, double xMax, double xMid,
11512                               int xMouse) {
11513             double adjWidth = getAdjustedWidth(width, x,
11514                                          xPrev, xNext, xMin, xMax, xMid);
11515             double result = 
11516               x + (0.5*(widthMultiplier - 1)) * adjWidth;
11517             return result;
11518          }   
11519    
11520          double getUpperLeftY(double height, double y,
11521                               double yPrev, double yNext,
11522                               double yMin, double yMax, double yMid,
11523                               int yMouse)  {
11524             double adjHeight = getAdjustedHeight(height, y,
11525                                              yPrev, yNext, yMin, yMax, yMid);
11526             double result = 
11527               y + (0.5*(heightMultiplier - 1)) * adjHeight;
11528             return result;
11529    
11530          }
11531    
11532          protected AnnotationLocation defaultAnnotationLocation() {
11533             //         return AnnotationLocation.SOUTH;
11534             AnnotationLocation result = defaultHoverLocation();
11535             return result;
11536          }
11537         // fillSpacing to use when a symbol's fillSpacing is Double.NaN
11538          protected double defaultFillSpacing() {
11539            return DEFAULT_SYMBOL_FILL_SPACING;
11540          }
11541         // fillThickness to use when a symbol's fillThickness is
11542         // GChart.NAI
11543          protected int defaultFillThickness() {
11544            return DEFAULT_SYMBOL_FILL_THICKNESS;
11545          }
11546          // symbol-type-specific default hovertextTemplate  
11547          protected String defaultHovertextTemplate() {
11548              return DEFAULT_HOVERTEXT_TEMPLATE;
11549          }
11550          // symbol-type-specific default location of hover feedback  
11551          protected AnnotationLocation defaultHoverLocation() {
11552              return DEFAULT_HOVER_LOCATION;
11553          }
11554      /*
11555       * Unmanaged images. Supports older code that simply
11556       * zaps/recreates each image, relying on browser's garbage
11557       * collector to deal with the reuse issue (that's slower).
11558       * 
11559       */ 
11560       private Image createImage(Symbol symbol,
11561                                 double width, double height,
11562                                 int borderWidth,
11563                                 String url) {
11564    
11565         Image result = new Image(url);
11566         // if smaller of width, height is at least twice
11567         // the border width, border width is used as is, otherwise,
11568         // it's replaced with half the smaller of width, height:
11569         int cappedBW = (int)
11570           ((2*borderWidth <= ((width < height) ? width : height)) ?
11571            borderWidth : (((width < height) ? width : height)/2));                        
11572    
11573         String borderColor = symbol.getBorderColorCSS();
11574          // If border was too big to fit inside rectangle, since GChart
11575          // borders are uniform around the rectangle, odd-sized
11576          // dimensions can leave a single "leftover" 1px inside the
11577          // border. Set background to the border's color so that the
11578         // border, in effect, takes up the entire rectangle.
11579         String backgroundColor = (cappedBW == borderWidth) ?
11580                                symbol.getBackgroundColorCSS() :
11581                                borderColor;
11582         // In principle, x,y position should also change with transparency
11583         // emulation in some cases. But these images are only used in
11584         // tables on the legend key, where they are always centered, so
11585         // that doesn't matter.
11586    
11587          if (TRANSPARENT_BORDER_COLOR == borderColor) {//transparency emulation
11588             if (cappedBW > 0) {
11589                // to emulate an internal transparent border using a 0 width
11590                // border, we need to shrink the size by twice the amount
11591                // of the border.
11592                height -= 2*cappedBW; // shrink size
11593                width -= 2*cappedBW; 
11594             }
11595             // else, external border is just eliminated, no adjustment needed
11596             cappedBW = 0;
11597             borderColor = "transparent";
11598             if (TRANSPARENT_BORDER_COLOR == backgroundColor)
11599                backgroundColor = "transparent";
11600          }
11601          else if (cappedBW > 0) {
11602            height -= 2*cappedBW; // shrink size to incorporate
11603            width -= 2*cappedBW; // impact of internal border.
11604          }
11605         GChart.setBackgroundColor(result, backgroundColor);
11606         GChart.setBorderColor(result, borderColor);
11607         GChart.setBorderStyle(result, symbol.getBorderStyle());
11608         GChart.setBorderWidth(result, Math.abs(cappedBW));
11609         result.setPixelSize((int) Math.round(width),
11610                             (int) Math.round(height));
11611         return result;
11612       }
11613         // creates small image of symbol (used in the chart legend).
11614       Image createIconImage(Symbol symbol, 
11615                             int legendFontSize, double symBorderFraction) {
11616          Image result = createImage(symbol,
11617                             getIconWidth(legendFontSize),
11618                             getIconHeight(legendFontSize),
11619                             getIconBorderWidth(legendFontSize,
11620                                                symBorderFraction),
11621                             symbol.getImageURL());
11622            return result;
11623       }
11624    
11625       // are two one-dimensional ranges (x1...x2 and y1...y2) disjoint?
11626       static private boolean areDisjointRanges(double x1, double x2,
11627                                                double y1, double y2) {
11628         boolean result = false;
11629         if ((x1 < y1 && x2 < y1 && x1 < y2 && x2 < y2) ||
11630             (y1 < x1 && y2 < x1 && y1 < x2 && y2 < x2))
11631           result = true;
11632         return result;
11633       }
11634    
11635       // do two rectangular regions intersect (left/right and/or
11636       // top/bottom can be interchanged and it still works)
11637       static boolean intersects(
11638         double left1, double top1, double right1, double bottom1,
11639         double left2, double top2, double right2, double bottom2) {
11640         boolean result = true;
11641         if (areDisjointRanges(left1, right1, left2, right2) ||
11642             areDisjointRanges(top1, bottom1, top2, bottom2))
11643           result = false;
11644         return result;
11645       }
11646    
11647       
11648       /* renders a single image that is part of a (possibly
11649        * multi-image) symbol, along with that image's annotation */
11650       protected void realizeOneImageOfSymbol(PlotPanel pp,
11651                                              GraphicsRenderingPanel grp,  
11652                                              AnnotationRenderingPanel arp,  
11653                                              Symbol symbol,
11654                                              Annotation annotation,
11655                                              boolean onY2,
11656                                              boolean clipPlotArea,
11657                                              boolean clipDecoratedChart,
11658                                              double xPx, double yPx, 
11659                                              double prevXPx, double prevYPx,
11660                                              double nextXPx, double nextYPx,
11661                                              double width, double height) {
11662    
11663    
11664            double xMin = pp.getXMin();
11665            double xMax = pp.getXMax(); 
11666            double xMid = symbol.getBaseline();
11667            // x!=x is a faster isNaN
11668            if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
11669            double xMinPx = pp.xToPixel(xMin);
11670            double xMaxPx = pp.xToPixel(xMax);
11671            double xMidPx = pp.xToPixel(xMid);
11672           
11673            double symWidth = getAdjustedWidth(width, xPx,
11674                                              prevXPx, nextXPx,
11675                                              xMinPx, xMaxPx, xMidPx);
11676            if ((symWidth!=symWidth)) return; // x!=x is a faster isNaN
11677            
11678            double xLeft = getUpperLeftX(width, xPx,
11679                                         prevXPx, nextXPx,
11680                                         xMinPx, xMaxPx, xMidPx,
11681                                         pp.getXMousePlotArea());
11682            if ((xLeft!=xLeft)) return; // x!=x is a faster isNaN
11683            
11684            double xCenter = xLeft + symWidth/2.;
11685            // the data and pixel Y coordinates are flipped, hence
11686            // the (counter-intuitive) min/max interchange below:
11687            double yMin =  onY2?pp.getY2Max():pp.getYMax();
11688            double yMax =  onY2?pp.getY2Min():pp.getYMin();
11689            double yMid = symbol.getBaseline();
11690            // x!=x is a faster isNaN
11691            if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
11692            double yMinPx = pp.yToPixel(yMin,onY2);
11693            double yMaxPx = pp.yToPixel(yMax,onY2);
11694            double yMidPx = pp.yToPixel(yMid,onY2);
11695            
11696            double symHeight = getAdjustedHeight(
11697                                      height, yPx,
11698                                      prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
11699            if ((symHeight!=symHeight)) return; // x!=x is a faster isNaN
11700            
11701            double yTop = getUpperLeftY(height, yPx,
11702                                        prevYPx, nextYPx,
11703                                        yMinPx, yMaxPx, yMidPx,
11704                                        pp.getYMousePlotArea());
11705            if ((yTop!=yTop)) return; // x!=x is a faster isNaN
11706            
11707            double yCenter =  yTop + symHeight/2.;
11708    
11709            if (clipPlotArea &&
11710                !intersects(xMinPx, yMinPx, xMaxPx, yMaxPx,
11711                            xLeft, yTop, xLeft+symWidth, yTop+symHeight))
11712              return; // image is completely off plot area, so skip it.
11713            else if (clipDecoratedChart) {
11714               int yAxisWidth = pp.getYAxisEnsembleWidth();
11715               int titleThickness = pp.chartTitleThickness();
11716               if (!intersects(xMinPx - yAxisWidth,
11717                               yMinPx - titleThickness,
11718                               pp.getXChartSizeDecoratedQuickly()-yAxisWidth,
11719                               pp.getYChartSizeDecoratedQuickly()-titleThickness,
11720                               xLeft, yTop, xLeft+symWidth, yTop+symHeight))
11721                  return; // image completely off decorated chart, so skip
11722            }
11723            // translate negative width, height to equivalent
11724            // positive values that image tags can handle
11725            int signWidth = 1;
11726            if (symWidth < 0) {
11727               xLeft = xLeft + symWidth;
11728               symWidth *= -1;
11729               signWidth = -1;
11730            }
11731            int signHeight = 1;
11732            if (symHeight < 0) {
11733               yTop = yTop + symHeight;
11734               symHeight *= -1;
11735               signHeight = -1;
11736            }
11737    
11738            // Positive pixel padding pushes the specified edge
11739            // outward from the center by the given amount, without
11740            // changing the location of the center the symbol.
11741            // Similarly, negative padding, pushes the edge inward.
11742    
11743            if (symWidth != 0) {
11744              xLeft -= pixelPadLeft;
11745              symWidth += pixelPadLeft + pixelPadRight;
11746            }
11747            // else, zero width, keep it that way (no padding added)
11748            
11749            if (symHeight != 0) {
11750              yTop -= pixelPadTop;
11751              symHeight += pixelPadTop + pixelPadBottom;
11752            }
11753            // else, zero height, keep it that way (no padding added)
11754    
11755            int borderWidth = symbol.getBorderWidth();
11756            // borderWidth < 0 ==> external border
11757            if ((symWidth > 0 && symHeight > 0) || borderWidth < 0) {
11758               grp.renderBorderedImage(symbol.getBackgroundColorCSS(),
11759                                            symbol.getBorderColorCSS(),
11760                                            symbol.getBorderStyle(),
11761                                            borderWidth,
11762                                            symWidth,
11763                                            symHeight,
11764                                            xLeft, yTop, symbol.getImageURL());
11765            }
11766            // if the image has an attached label, realize that
11767            if (annotation!=null &&
11768                (annotation.getText() != null ||
11769                 annotation.getWidget() != null) &&
11770                annotation.getVisible()) {
11771               AnnotationLocation loc = annotation.getLocation();
11772               if (null == loc) loc = defaultAnnotationLocation(); 
11773               loc = AnnotationLocation.transform(loc, signWidth, signHeight);
11774              // Note: yShift follows orientation of cartesian y
11775              // Axis, which is 180 degrees different from pixel y
11776              // coordinates, hence the extra "-" below.
11777              //
11778              // signWidth, signHeight multipliers assure that shifts are
11779              // appropriately symetrical for bars above and below or to the
11780              // left or right of their baselines (only baseline bars use
11781              // negative symbol widths) For example, a yShift of
11782              // 10px would shift up for bars above the baseline, and down
11783              // for bars below the baseline, which is usually what you
11784              // want (e.g. placing labels above or below the bars). 
11785              arp.renderAnnotation(annotation, loc,
11786                                xCenter+signWidth*annotation.getXShift(),
11787                                yCenter-signHeight*annotation.getYShift(),
11788                                symWidth, symHeight,
11789                                symbol);
11790            }
11791         
11792       }
11793    
11794         // Distance from the point (x1, y1) to the point (x2, y2)
11795         protected double distance(
11796             double x1, double y1, double x2, double y2) {
11797            double result = Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); 
11798            return result;
11799         }
11800         /*
11801          * Renders the symbol at the specified position within the plot
11802          * panel, by creating appropriately positioned Image and Label
11803          * objects within the given rendering panel.
11804          * <p>
11805          *
11806          * Most of the Image widgets will be replaced with drawing on the
11807          * rendering panel's dedicated canas Widget if an external canvas
11808          * capability has been bolted onto GChart, and continuous fill
11809          * (fillSpacing == 0) has been requested for the curve.
11810          * 
11811          * <p>
11812          * 
11813          * So-rendered symbols are used to represent: each point on a curve
11814          * (including any "filled" elements linearly interpolated between
11815          * successive points, such as, point-to-point connecting lines and
11816          * "areas under the curve") and (via special hidden system curves)
11817          * axes, gridlines, ticks, tick-labels, titles, footnotes, and
11818          * the legend key.
11819          * <p>
11820          *
11821          * This method is overridden for pie slice symbols and
11822          * the LINE symbol type.
11823          *
11824          */
11825         // retains coord of an area chart's "filled to" axis/baseline
11826         static double oppositeEdge = Double.NaN; 
11827         void realizeSymbol(PlotPanel pp,
11828                            GraphicsRenderingPanel grp,
11829                            AnnotationRenderingPanel arp,
11830                            Symbol symbol, 
11831                            Annotation annotation,
11832                            boolean onY2,
11833                            boolean clipPlotArea,
11834                            boolean clipDecoratedChart,
11835                            boolean drawMainSymbol,
11836                            double x, double y, 
11837                            double prevX, double prevY,
11838                            double nextX, double nextY) {
11839    
11840           if ((x!=x) || (y!=y)) // this point undefined (isNaN)
11841              return;
11842    
11843           double xPx = pp.xToPixel(x);
11844           double yPx = pp.yToPixel(y, onY2);
11845           double prevXPx = pp.xToPixel(prevX);
11846           double prevYPx = pp.yToPixel(prevY, onY2);
11847           double nextXPx = pp.xToPixel(nextX);
11848           double nextYPx = pp.yToPixel(nextY, onY2);
11849           double spacing = symbol.getFillSpacing();
11850           int thickness = symbol.getFillThickness();
11851           GChartCanvasLite canvas = grp.getCanvas();
11852           
11853           if (0 == spacing && null != canvas && thickness > 0) { // if canvas rendered
11854             if (null == isHorizontallyBanded) {
11855               /*
11856                * Continuous fill, canvas available, and not explicitly
11857                * horizontally or vertically banded. For example, BOX_*
11858                * symbol types are not explicitly oriented, but VBAR_*
11859                * (vertically) and HBAR_* (horizontally) are: use canvas
11860                * to draw a straight line between points.  <p>
11861                *
11862                * Code in this branch also gets executed by the LINE
11863                * symbol type.
11864                *
11865                */ 
11866                 int borderWidth = symbol.getBorderWidth();
11867                 // negative (external) border widens line by 2*|borderWidth|  
11868                 int externalLineWidth = (borderWidth >= 0) ?
11869                                          thickness :
11870                                         (thickness + 2*Math.abs(borderWidth));
11871                 int internalLineWidth = (borderWidth >= 0) ?
11872                       Math.max(thickness-2*borderWidth,0) : thickness;
11873                 String borderColor = symbol.getBorderColor();
11874                 String backgroundColor = symbol.getBackgroundColor();
11875                 if (externalLineWidth > 0 &&
11876                     ((TRANSPARENT_BORDER_COLOR != borderColor &&
11877                      "transparent" != borderColor) ||
11878                      (TRANSPARENT_BORDER_COLOR != backgroundColor &&
11879                      "transparent" != backgroundColor))) {
11880                    if (prevX != prevX || prevY != prevY) {
11881                       // first defined point after an undefined point ==> new path 
11882                       // (need to draw zero-thickness lines for possible line
11883                       // endings user may have defined by overriding beginPath)
11884                       canvas.beginPath();
11885                       canvas.moveTo(xPx - grp.x0, yPx - grp.y0);
11886                    }
11887                    if (nextX!=nextX || nextY!=nextY) {
11888                       // last defined point before undefined point ==> draw accumulated path
11889                      if (TRANSPARENT_BORDER_COLOR != borderColor &&
11890                          "transparent" != borderColor &&
11891                          externalLineWidth > 0) {
11892                          canvas.setStrokeStyle(borderColor);
11893                          canvas.setLineWidth(externalLineWidth);
11894                          canvas.stroke();
11895                      }
11896                      if (TRANSPARENT_BORDER_COLOR != backgroundColor &&
11897                          "transparent" != backgroundColor &&
11898                          internalLineWidth > 0) {
11899                         canvas.setLineWidth(internalLineWidth);
11900                         canvas.setStrokeStyle(backgroundColor);
11901                         canvas.stroke();
11902                      }
11903                    }
11904                    else // not at end of chain ==> add one more segment to the path
11905                       // (need to draw doubled points for possibly "line join"
11906                       // user may have defined via overriding beginPath)
11907                       canvas.lineTo(nextXPx - grp.x0, nextYPx - grp.y0);
11908                 }
11909              // else lines are 0-width or transparent, so not rendered
11910              }
11911              else {
11912              /*
11913               * Explicitly oriented bandedness occurs only for vert or
11914               * horizontal bars. x,y coordinates are connected into
11915               * a path (as in a line chart), and then that path
11916               * is extended into a closed polygon by adding a
11917               * closing segment formed from an appropriate
11918               * section of an axis or baseline.
11919               *
11920               */ 
11921    
11922              /*
11923               * Draw area interpolated between successive bars.
11924               *
11925               * Note that the "opposite" edge could be a point on an
11926               * x or y axis, or on the curve's baseline, depending
11927               * on the kind of bar chart involved: it's the edge
11928               * of the bar that is furthest from the x,y point.
11929               * 
11930               */
11931                 boolean closeStrokeAndFill = false;
11932                 if (Boolean.FALSE == isHorizontallyBanded) {
11933                    if (prevX != prevX || prevY != prevY) {
11934                       // 1st point, or 1st point after a break in the line
11935                       oppositeEdge = getEdgeOppositeVertically(
11936                          pp, symbol, y, onY2);
11937                       canvas.beginPath();
11938                       canvas.moveTo(xPx - grp.x0, oppositeEdge - grp.y0);
11939                       canvas.lineTo(xPx - grp.x0, yPx - grp.y0);
11940                    }
11941                    if (nextX!=nextX || nextY!=nextY) {
11942                       // last point, or last point before a break in the line
11943                       canvas.lineTo(xPx - grp.x0, oppositeEdge - grp.y0);
11944                       closeStrokeAndFill = true;
11945                    }
11946                    else {
11947                       canvas.lineTo(nextXPx - grp.x0, nextYPx - grp.y0);
11948                    }
11949                 }
11950                 else {
11951    
11952                    if (prevX != prevX || prevY != prevY) {
11953                       // 1st point, or 1st point after a break in the line
11954                       oppositeEdge = getEdgeOppositeHorizontally(
11955                          pp, symbol, x, onY2);
11956                       canvas.beginPath();
11957                       canvas.moveTo(oppositeEdge - grp.x0, yPx - grp.y0);
11958                       canvas.lineTo(xPx - grp.x0, yPx - grp.y0);
11959                    }
11960                    if (nextX!=nextX || nextY!=nextY) {
11961                       // last point, or last point before a break in the line
11962                       canvas.lineTo(oppositeEdge - grp.x0, yPx - grp.y0);
11963                       closeStrokeAndFill = true;
11964                    }
11965                    else {
11966                       canvas.lineTo(nextXPx - grp.x0, nextYPx - grp.y0);
11967                    }
11968    
11969                 }
11970    
11971                 if (closeStrokeAndFill) {
11972    
11973                     canvas.closePath();
11974                     int borderWidth = symbol.getBorderWidth();
11975                 // negative (external) border requires double-wide stroke
11976                     int lineWidth = (borderWidth >= 0) ?
11977                                      borderWidth: (2*Math.abs(borderWidth));
11978                     String borderColor = symbol.getBorderColor();
11979                     String backgroundColor = symbol.getBackgroundColor();
11980                     
11981       /* XXX: Simply dropping the rendering as we do below does not
11982        * exactly simulate the effect of transparent border/fill,
11983        * specifically:
11984        *
11985        * <ol>
11986        *   <li> Transparent internal border ==> the background
11987        *          fill shines through the inner half of that border
11988        *   <li> Transparent external border ==> Works OK
11989        *   <li> Transparent fill w external border ==>
11990        *           border extended internally to double width
11991        *   <li> Transparent fill w internal border ==> works OK
11992        * </ol>  
11993        *
11994        * GWTCanvas does not (?) provide a mechanism to "stroke
11995        * transparent pixels", which is what I really needed. And
11996        * emulating this, though possible via properly positioned
11997        * inner/outter regions, etc. would have required a lot of effort
11998        * to assure that sharply peaked angles, say, get rendered right.
11999        * 
12000        */
12001    
12002                     // non-negative borders fill before stroking (thus
12003                     // stroke overwrites internal half of border)
12004                     if (borderWidth >= 0 && thickness > 0 &&
12005                         TRANSPARENT_BORDER_COLOR != backgroundColor &&
12006                         "transparent" != backgroundColor) {
12007                        canvas.setFillStyle(backgroundColor);
12008                        canvas.fill();
12009                     }
12010    
12011                     // stroke whenever a border is present
12012                     if (borderWidth != 0 &&
12013                        TRANSPARENT_BORDER_COLOR != borderColor &&
12014                         "transparent" != borderColor) {
12015                        canvas.setStrokeStyle(borderColor);
12016                        canvas.setLineWidth(lineWidth);
12017                        canvas.stroke();
12018                     }
12019    
12020                     // negative borders fill AFTER stroking (thus zapping
12021                     // the internal half of the stroked border).
12022                     if (borderWidth < 0 && thickness > 0 &&
12023                         TRANSPARENT_BORDER_COLOR != backgroundColor &&
12024                         "transparent" != backgroundColor) {
12025                         canvas.setFillStyle(backgroundColor);
12026                         canvas.fill();
12027                     }
12028                 }
12029              }
12030           } // if (0 == spacing && null != canvas && thickness > 0)  
12031           else if (nextX==nextX && nextY==nextY &&  // next point defined
12032                    thickness > 0 &&   // not a zero thickness connection 
12033                    (x!=nextX || y!=nextY) ) { // this/next point not overlayed
12034             if (0 == spacing) // 1px is as close as HTML-element
12035                 spacing = 1;  // based filling can get to continuous
12036             double d = distance(xPx,yPx,nextXPx,nextYPx);
12037             int nChunks = (int) Math.round(d/spacing);
12038             if (nChunks > 1) {
12039                double deltaX = nextXPx - xPx;
12040                double deltaY = nextYPx - yPx;
12041                boolean dXIsLonger = deltaX*deltaX > deltaY*deltaY;
12042                if (dXIsLonger) {
12043                   deltaY /= deltaX; // from now on, dy is really dy/dx
12044                   deltaX /= nChunks;// from now on, dx is for 1 chunk
12045                }
12046                else {
12047                   deltaX /= deltaY;  // from now on, dx is really dx/dy
12048                   deltaY /= nChunks; // from now on, dy is for 1 chunk
12049                }
12050              // i==0 corresponds to the (to-be-drawn-last) symbol on (x,y).
12051                for (int i = 1; i < nChunks; i++) {
12052                  // linearly interpolate forwards towards the next
12053                  // point; forward interpolation (usually) lets us
12054                  // place the "main" symbol for the original point on
12055                  // top of these interpolated symbols, in one pass.
12056                  double xi;
12057                  double yi;
12058    
12059                  // Rounding to the longer dimension first, then
12060                  // using that pixelated position to determine other
12061                  // dimension tends to keep points closer to being
12062                  // on the mathematically ideal line (at the cost of
12063                  // being less evenly spaced along that line). It's
12064                  // not too hard to see the improved alignment on
12065                  // GChartExample03, for example.
12066                  if (dXIsLonger) {
12067                     xi = Math.round(xPx + deltaX * i);
12068                     yi = Math.round(yPx + deltaY*(xi - xPx));
12069                  }
12070                  else { // delta y is longer
12071                     yi = Math.round(yPx + deltaY * i);
12072                     xi = Math.round(xPx + deltaX*(yi - yPx));
12073                 }              
12074    
12075    
12076                  // interpolated symbols set width & height to
12077                  // thickness, but are otherwise the same as main
12078                  // symbol at (x,y)
12079                  realizeOneImageOfSymbol(pp, grp, arp, symbol, null, 
12080                                          onY2,
12081                                          clipPlotArea,
12082                                          clipDecoratedChart,
12083                                          xi, yi,
12084                                          prevXPx, prevYPx,
12085                                          nextXPx, nextYPx,
12086                                          thickness, 
12087                                          thickness);
12088                }
12089             }
12090             // else points too close to require any "filler" elements
12091           }
12092           // the "main" symbol (the one on the (x,y) point itself) is
12093           // rendered last to put it on top of interpolated images; this
12094           // is also where any annotation on the point gets rendered.
12095           if (drawMainSymbol) {
12096              realizeOneImageOfSymbol(pp, grp, arp, symbol, annotation, 
12097                                      onY2,
12098                                      clipPlotArea,
12099                                      clipDecoratedChart,
12100                                      xPx, yPx,
12101                                      prevXPx, prevYPx,
12102                                      nextXPx, nextYPx,
12103                                      symbol.getWidth(pp), 
12104                                      symbol.getHeight(pp,onY2));
12105           }
12106         }
12107    
12108    
12109    
12110         
12111       } // end of class SymbolType 
12112    
12113       /**
12114        ** Defines keywords <tt>INSIDE</tt>, <tt>OUTSIDE</tt>, and
12115        ** <tt>CENTERED</tt> that specify the location of ticks
12116        ** relative to their axis.
12117        ** <p>
12118        **
12119        ** @see Axis#setTickLocation setTickLocation
12120        ** 
12121        **/
12122       public static final class TickLocation {
12123          /*
12124           * An integer form of the tick location (-1 - OUTSIDE,
12125           * 0 - CENTERED, +1 - INSIDE) that facilitates
12126           * generating appropriate symbol types for rendering ticks
12127           * 
12128           */ 
12129          int locationIndex;
12130          private TickLocation(int locationIndex) {
12131             this.locationIndex = locationIndex;
12132          }
12133         /**
12134          ** Indicates that ticks are located outside of the axis.
12135          **
12136          ** @see Axis#setTickLocation setTickLocation
12137          **/ 
12138         public static final TickLocation OUTSIDE = new TickLocation(-1);
12139         /**
12140          ** Indicates that ticks are centered on the axis.
12141          **
12142          ** @see Axis#setTickLocation setTickLocation
12143          **/ 
12144         public static final TickLocation CENTERED = new TickLocation(0);
12145         /**
12146          ** Indicates that ticks are located inside of the axis.
12147          **
12148          ** @see Axis#setTickLocation setTickLocation
12149          **/ 
12150         public static final TickLocation INSIDE = new TickLocation(1);
12151    
12152         // symbol type representing ticks on x axes at given position
12153         // axisPosition of  -1 is x-axis, +1 is x2-axis.
12154         //
12155         // (symbols representing ticks depend on the axis they are on)
12156         SymbolType getXAxisSymbolType(int axisPosition) {
12157            final SymbolType[] symbolMap =
12158              {SymbolType.BOX_NORTH, SymbolType.BOX_CENTER, SymbolType.BOX_SOUTH};
12159            SymbolType result =
12160               symbolMap[axisPosition*locationIndex+1];
12161            return result;                
12162          }
12163         // symbol type representing ticks on y axes at given position
12164         // axisPosition of  -1 is y-axis, +1 is y2-axis
12165         //
12166         // (symbols representing ticks depend on the axis they are on)
12167         SymbolType getYAxisSymbolType(int axisPosition) {
12168            final SymbolType[] symbolMap =
12169              {SymbolType.BOX_EAST, SymbolType.BOX_CENTER, SymbolType.BOX_WEST};
12170            SymbolType result =
12171               symbolMap[axisPosition*locationIndex+1];
12172            return result;                
12173         }
12174         
12175       } // class TickLocation
12176                        
12177      /**
12178       * Defines how the <tt>update</tt> method updates the touched
12179       * point, that is, the point the user is considered to be
12180       * hovered over.
12181       *
12182       * @see #update(TouchedPointUpdateOption) update
12183       * 
12184       */
12185                
12186      public static final class TouchedPointUpdateOption {
12187         private TouchedPointUpdateOption() {super();}
12188    
12189         /**
12190          * When this option is passed to the update method, any
12191          * touched point is cleared as a consequence of the update.
12192          * <p>
12193          *
12194          * This option can be used when you want to "start fresh"
12195          * with regards to hover feedback after an update, and want
12196          * to assure that only explicit user-generated mouse move
12197          * actions (rather than objects moving <i>underneath</i> a
12198          * fixed-position mouse cursor) can trigger hover feedback.
12199          *
12200          * @see #update update
12201          * @see #TOUCHED_POINT_LOCKED TOUCHED_POINT_LOCKED
12202          * @see #TOUCHED_POINT_UPDATED TOUCHED_POINT_UPDATED
12203          * 
12204          */
12205         public static final TouchedPointUpdateOption TOUCHED_POINT_CLEARED =
12206            new TouchedPointUpdateOption();
12207         
12208         /**
12209          * When this option is passed to the update method, any
12210          * previously touched point is locked in (remains unchanged).
12211          * <p>
12212          *
12213          * For example, if the mouse is over a certain point before
12214          * the update, and that point moves away from the mouse
12215          * (without the mouse moving otherwise) as a consequence of
12216          * the update, the hover feedback remains "locked in" to the
12217          * original point, even though the mouse is no longer on top
12218          * of that point.
12219          * <p>
12220          *
12221          * This option is useful for hover widgets that modify the
12222          * position, size, symbol of points/curves, and do not want the
12223          * selected point/curve (and popup hover widget) to change as
12224          * a consequence of such changes.
12225          * <p>
12226          *
12227          * <i>Note:</i> If the currently touched point or the curve
12228          * containing it is deleted, GChart sets the touched point
12229          * reference to <tt>null</tt>. In that case, this option and
12230          * <tt>TOUCHED_POINT_CLEARED</tt> behave the same way.
12231          *
12232          *
12233          * @see #update update
12234          * @see #TOUCHED_POINT_CLEARED TOUCHED_POINT_CLEARED
12235          * @see #TOUCHED_POINT_UPDATED TOUCHED_POINT_UPDATED
12236          * 
12237          */
12238         public static final TouchedPointUpdateOption TOUCHED_POINT_LOCKED =
12239           new TouchedPointUpdateOption();
12240         /**
12241          * When this option is passed to the update method, the
12242          * touched point is updated so that it reflects whatever point
12243          * is underneath the mouse cursor after the update
12244          * completes.
12245          * <p>
12246          * 
12247          * For example, if the mouse is not hovering over any point
12248          * before the update, but the update repositions one of the
12249          * points so that it is now underneath the mouse cursor,
12250          * the hover feedback for that point will be displayed.
12251          * Similarly, if the update moves a point away from the
12252          * mouse cursor, previously displayed hover feedback will
12253          * be eliminated.
12254          * <p>
12255          *
12256          * @see #update update
12257          * @see #TOUCHED_POINT_CLEARED TOUCHED_POINT_CLEARED
12258          * @see #TOUCHED_POINT_LOCKED TOUCHED_POINT_LOCKED
12259          * 
12260          */
12261         public static final TouchedPointUpdateOption TOUCHED_POINT_UPDATED =
12262           new TouchedPointUpdateOption();
12263      }
12264    
12265                
12266      /** The x-axis of a GChart.
12267       *
12268       * @see GChart#getXAxis getXAxis
12269       */
12270      
12271      public class XAxis extends Axis {
12272         XAxis() {
12273            super();
12274            isHorizontalAxis = true;
12275            ticksId = XTICKS_ID;
12276            gridlinesId = XGRIDLINES_ID;
12277            axisId = XAXIS_ID;
12278            axisPosition = -1;
12279            setTickLocation(DEFAULT_TICK_LOCATION);
12280            setTickThickness(DEFAULT_TICK_THICKNESS);
12281            setTickLength(DEFAULT_TICK_LENGTH);
12282         }
12283         
12284         public double clientToModel(int clientCoordinate) {
12285            int xPixel =
12286                        Window.getScrollLeft() + clientCoordinate -
12287                        plotPanel.getAbsoluteLeft();
12288            double result = plotPanel.xChartPixelToX(xPixel);
12289            return result;
12290         }
12291         public int getAxisLabelThickness() {
12292            final int EXTRA_CHARHEIGHT = 2; // 1-char space above & below
12293            final int DEF_CHARHEIGHT = 1;
12294            int result = 0;
12295            if (null == getAxisLabel())
12296               result = 0;
12297            else if (GChart.NAI != axisLabelThickness) 
12298               result = axisLabelThickness;
12299            else if (getAxisLabel() instanceof HasHTML) {
12300               int charHeight = htmlHeight(
12301                  ((HasHTML) (getAxisLabel())).getHTML());
12302               result = (int) Math.round((EXTRA_CHARHEIGHT+charHeight) *
12303                          getTickLabelFontSize() *
12304                         TICK_CHARHEIGHT_TO_FONTSIZE_LOWERBOUND);
12305            }
12306            else
12307               result = (int) Math.round(
12308                         (EXTRA_CHARHEIGHT + DEF_CHARHEIGHT) *
12309                         getTickLabelFontSize() *
12310                         TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
12311            return result;
12312         }
12313         public double getDataMax() {
12314           double result = -Double.MAX_VALUE;
12315           int nCurves = getNCurves();
12316           for (int i = 0; i < nCurves; i++) {
12317              Curve c = getSystemCurve(i);
12318              if (!c.isVisible()) continue;
12319              int nPoints = c.getNPoints(); 
12320              for (int j = 0; j < nPoints; j++) {
12321                 result = maxIgnoreNaNAndMaxValue(result,
12322                                                  c.getPoint(j).getX());
12323              }
12324           }
12325           return result == -Double.MAX_VALUE ? Double.NaN : result;
12326         }
12327         public double getDataMin() {
12328           double result = Double.MAX_VALUE;
12329           int nCurves = getNCurves();
12330           for (int i = 0; i < nCurves; i++) {
12331              Curve c = getSystemCurve(i);
12332              if (!c.isVisible()) continue;
12333              int nPoints = c.getNPoints(); 
12334              for (int j = 0; j < nPoints; j++) {
12335                 result = minIgnoreNaNAndMaxValue(result,
12336                                                  c.getPoint(j).getX());
12337              }
12338           }
12339           return result == Double.MAX_VALUE ? Double.NaN : result;
12340         }
12341    
12342         public double getMouseCoordinate() {
12343           double result = plotPanel.xChartPixelToX(plotPanel.getXMouse());
12344           return result;        
12345         }
12346         
12347         public int getTickLabelThickness(boolean needsPopulation) { // overrides base class
12348           int result;
12349           if (tickLabelThickness != GChart.NAI)
12350             result = tickLabelThickness;
12351           else if (getTickCount() == 0)
12352              result = 0;
12353           else {
12354           // XXX: single line labels assumed; these have height
12355           // almost equal to the fontSize in pixels. Not really
12356           // right, since multi-line HTML can now be used, but user
12357           // can explicitly change tick label thickness with
12358           // multi-line, HTML based, ticks, so OK for now.   
12359              result = (int) Math.round(
12360                       TICK_CHARHEIGHT_TO_FONTSIZE_LOWERBOUND *
12361                       tickLabelFontSize);
12362           }
12363           return result;
12364         }
12365         
12366         public double modelToClient(double modelCoordinate) {
12367            double xPixel = plotPanel.xToChartPixel(modelCoordinate);
12368            double result = plotPanel.getAbsoluteLeft()
12369                            - Window.getScrollLeft() + xPixel;
12370            return result;
12371         }
12372         public double modelToPixel(double modelCoordinate) {
12373            double result = plotPanel.xToChartPixel(modelCoordinate);
12374            return result;
12375         }
12376         public double modelToPlotAreaPixel(double modelCoordinate) {
12377            double result = plotPanel.xToPixel(modelCoordinate);
12378            return result;
12379         }
12380         public double pixelToModel(int pixelCoordinate) {
12381            double result = plotPanel.xChartPixelToX(pixelCoordinate);
12382            return result;
12383         }
12384         public double plotAreaPixelToModel(int pixelCoordinate) {
12385            double result = plotPanel.xPixelToX(pixelCoordinate);
12386            return result;
12387         }
12388         public void setTickLength(int tickLength) {
12389           chartDecorationsChanged = true;
12390           this.tickLength = tickLength;
12391           getSystemCurve(ticksId).getSymbol().setHeight(  
12392              getActualTickLength());
12393         }
12394         public void setTickThickness(int tickThickness) {
12395           this.tickThickness = tickThickness;
12396           getSystemCurve(ticksId).getSymbol().setWidth(tickThickness);
12397         }
12398    
12399               
12400      } // end of class XAxis   
12401      /** The right, or "y2", axis of a GChart. 
12402       *
12403       * @see GChart#getY2Axis getY2Axis
12404       */
12405    
12406      public class Y2Axis extends Axis {
12407         Y2Axis() {
12408            super();
12409            isHorizontalAxis = false;
12410            ticksId = Y2TICKS_ID;
12411            gridlinesId = Y2GRIDLINES_ID;
12412            axisId = Y2AXIS_ID;
12413            axisPosition = 1;
12414            setTickLocation(DEFAULT_TICK_LOCATION);
12415            setTickThickness(DEFAULT_TICK_THICKNESS);
12416            setTickLength(DEFAULT_TICK_LENGTH);
12417         }
12418         public double clientToModel(int clientCoordinate) {
12419            int yPixel =
12420                        Window.getScrollTop() + clientCoordinate -
12421                        plotPanel.getAbsoluteTop();
12422            double result = plotPanel.yChartPixelToY2(yPixel);
12423            return result;
12424         }
12425         public double getDataMax() {
12426           double result = -Double.MAX_VALUE;
12427           int nCurves = getNCurves();
12428           for (int i = 0; i < nCurves; i++) {
12429              Curve c = getSystemCurve(i);
12430              if (!c.isVisible()) continue;
12431              if (c.getYAxis() == Y2_AXIS) {
12432                int nPoints = c.getNPoints(); 
12433                for (int j = 0; j < nPoints; j++) {
12434                   result = maxIgnoreNaNAndMaxValue(result,
12435                                                    c.getPoint(j).getY());
12436                }
12437              }
12438           }
12439           return result == -Double.MAX_VALUE ? Double.NaN : result;
12440         }
12441         public double getDataMin() {
12442           double result = Double.MAX_VALUE;
12443           int nCurves = getNCurves();
12444           for (int i = 0; i < nCurves; i++) {
12445              Curve c = getSystemCurve(i);
12446              if (!c.isVisible()) continue;
12447              if (c.getYAxis() == Y2_AXIS) {
12448                int nPoints = c.getNPoints(); 
12449                 for (int j = 0; j < nPoints; j++) {
12450                    result = minIgnoreNaNAndMaxValue(result,
12451                                                     c.getPoint(j).getY());
12452                }
12453              }
12454           }
12455           return result == Double.MAX_VALUE ? Double.NaN : result;
12456         }
12457    
12458         public double getMouseCoordinate() {
12459           double result = plotPanel.yChartPixelToY2(plotPanel.getYMouse());
12460           return result;        
12461         }
12462    
12463    
12464         public double modelToClient(double modelCoordinate) {
12465            double yPixel = plotPanel.yToChartPixel(modelCoordinate, true);
12466            double result = plotPanel.getAbsoluteTop()
12467                            - Window.getScrollTop() + yPixel;
12468            return result;
12469         }
12470         public double modelToPixel(double modelCoordinate) {
12471            double result = plotPanel.yToChartPixel(modelCoordinate, true);
12472            return result;
12473         }
12474         public double modelToPlotAreaPixel(double modelCoordinate) {
12475            double result = plotPanel.yToPixel(modelCoordinate, true);
12476            return result;
12477         }
12478         public double pixelToModel(int pixelCoordinate) {
12479            double result = plotPanel.yChartPixelToY2(pixelCoordinate);
12480            return result;
12481         }
12482         public double plotAreaPixelToModel(int pixelCoordinate) {
12483            double result = plotPanel.yPixelToY2(pixelCoordinate);
12484            return result;
12485         }
12486         public void setTickLength(int tickLength) {
12487           chartDecorationsChanged = true;
12488           this.tickLength = tickLength;
12489           getSystemCurve(ticksId).getSymbol().setWidth(
12490              getActualTickLength());
12491         }
12492         public void setTickThickness(int tickThickness) {
12493           this.tickThickness = tickThickness;
12494           getSystemCurve(ticksId).getSymbol().setHeight(tickThickness);
12495         }
12496         
12497      } // end of class Y2Axis
12498      /** The left y-axis of a GChart.
12499       *
12500       * @see GChart#getYAxis getYAxis
12501       *
12502       */
12503    
12504      public class YAxis extends Axis {
12505         YAxis() {
12506            super();
12507            isHorizontalAxis = false;
12508            ticksId = YTICKS_ID;
12509            gridlinesId = YGRIDLINES_ID;
12510            axisId = YAXIS_ID;
12511            axisPosition = -1;
12512            setTickLocation(DEFAULT_TICK_LOCATION);
12513            setTickThickness(DEFAULT_TICK_THICKNESS);
12514            setTickLength(DEFAULT_TICK_LENGTH);
12515         }
12516         public double clientToModel(int clientCoordinate) {
12517            int yPixel =
12518                        Window.getScrollTop() + clientCoordinate -
12519                        plotPanel.getAbsoluteTop();
12520            double result = plotPanel.yChartPixelToY(yPixel);
12521            return result;
12522         }
12523         public double getDataMax() {
12524           double result = -Double.MAX_VALUE;
12525           int nCurves = getNCurves();
12526           for (int i = 0; i < nCurves; i++) {
12527              Curve c = getSystemCurve(i);
12528              if (!c.isVisible()) continue;
12529              if (c.getYAxis() == Y_AXIS) {
12530                int nPoints = c.getNPoints(); 
12531                for (int j = 0; j < nPoints; j++) {
12532                   result = maxIgnoreNaNAndMaxValue(result,
12533                                                    c.getPoint(j).getY());
12534                }
12535              }
12536           }
12537           return result == -Double.MAX_VALUE ? Double.NaN : result;
12538         }
12539         public double getDataMin() {
12540           double result = Double.MAX_VALUE;
12541           int nCurves = getNCurves();
12542           for (int i = 0; i < nCurves; i++) {
12543              Curve c = getSystemCurve(i);
12544              if (!c.isVisible()) continue;
12545              if (c.getYAxis() == Y_AXIS) {
12546                int nPoints = c.getNPoints(); 
12547                for (int j = 0; j < nPoints; j++) {
12548                   result = minIgnoreNaNAndMaxValue(result,
12549                                                    c.getPoint(j).getY());
12550                }
12551              }
12552           }
12553           return result == Double.MAX_VALUE ? Double.NaN : result;
12554         }
12555    
12556         public double getMouseCoordinate() {
12557           double result = plotPanel.yChartPixelToY(plotPanel.getYMouse());
12558           return result;        
12559         }
12560    
12561    
12562         public double modelToClient(double modelCoordinate) {
12563            double yPixel = plotPanel.yToChartPixel(modelCoordinate, false);
12564            double result = plotPanel.getAbsoluteTop()
12565                            - Window.getScrollTop() + yPixel;
12566            return result;
12567         }
12568         public double modelToPixel(double modelCoordinate) {
12569            double result = plotPanel.yToChartPixel(modelCoordinate, false);
12570            return result;
12571         }
12572         public double modelToPlotAreaPixel(double modelCoordinate) {
12573            double result = plotPanel.yToPixel(modelCoordinate, false);
12574            return result;
12575         }
12576         public double pixelToModel(int pixelCoordinate) {
12577            double result = plotPanel.yChartPixelToY(pixelCoordinate);
12578            return result;
12579         }
12580         public double plotAreaPixelToModel(int pixelCoordinate) {
12581            double result = plotPanel.yPixelToY(pixelCoordinate);
12582            return result;
12583         }
12584         public void setTickLength(int tickLength) {
12585           chartDecorationsChanged = true;
12586           this.tickLength = tickLength;
12587           getSystemCurve(ticksId).getSymbol().setWidth(
12588              getActualTickLength());
12589         }
12590         public void setTickThickness(int tickThickness) {
12591           this.tickThickness = tickThickness;
12592           getSystemCurve(ticksId).getSymbol().setHeight(tickThickness);
12593         }
12594    
12595      } // end of class YAxis
12596    
12597      /*
12598       * Allows precise alignment of a text label before the
12599       * exact size of that label is known, by enclosing it
12600       * within a 1x1 grid.
12601       * <p>
12602       *
12603       * The external grid must be larger than the label or requested grid
12604       * alignment won't be realized. But larger that required containing
12605       * grids occlude mouse events from nearby elements. The
12606       * NonoccludingReusableAlignedLabel subclasses this class to solve
12607       * this problem.
12608       *
12609       *
12610       */ 
12611      
12612      private static class AlignedLabel extends Grid {
12613        AlignedLabel() {
12614          super(1,1);
12615          getCellFormatter().setWordWrap(0,0,false);
12616          setCellPadding(0);
12617          setCellSpacing(0);
12618          setBorderWidth(0);
12619        }    
12620      } 
12621    
12622        
12623      /*
12624       * This class' sole purpose is to work around a FF 2
12625       * performance limitation: chart update times increase as
12626       * O(N^2) after the number of direct ancestor child widgets in
12627       * a single AbsolutePanel exceeds around 500-1000, AND the
12628       * chart is updated in more than one browser-displayed stage
12629       * (e.g, via a series of incremental updates that successively
12630       * add more curve data, for purposes of user feedback). By
12631       * contrast, IE7 times grow as O(N) even if 3,000 child widgets
12632       * are added to a previously displayed chart (e.g. by adding a
12633       * bar curve with 3,000 bars on it).
12634       * 
12635       * <p>
12636       *
12637       * For solid-fill line chart support (LINE SymbolType
12638       * introduced in 2.2), thousands of widgets are often needed
12639       * and so these O(N^2) FF 2 times were just too slow.
12640       * 
12641       * <p>
12642       * 
12643       * Some kind of fixed hash table inside of FF 2 divs could
12644       * explain this switch from O(N) to O(N^2) performance.
12645       * <p>
12646       * 
12647       * Approach is to split up the large AbsolutePanel into a
12648       * series of child panels, each of which contains a number of
12649       * elements within the range where FF 2 updates are O(N).
12650       * <p>
12651       * 
12652       * Tried to keep it light-weight so IE7 isn't penalized too
12653       * much for having to workaround this FF 2 limitation. 
12654       * 
12655       */ 
12656    
12657      static class PartitionedAbsolutePanel extends Composite {
12658    /* Max number of widgets in each panel; chose a value as large as
12659       possible while remaining within the empirically
12660       determined FF 2 O(N) range. Here's the data (from a 3,000 element
12661       test based on the sin curve of the 2.1 live demo
12662       called GChartExample15c.java) upon which this choice was based:
12663    <p>
12664    
12665    <pre>
12666    
12667    Size    FF2    IE7
12668            (sec)  (sec) 
12669      1     ~14     16
12670      2      11     13 
12671     32      9      11   
12672     64      9      12
12673    128      9      11
12674    256      9      11
12675    512      10     11
12676    1024     15     11
12677    2048     27     11
12678    4096*    61     12
12679    
12680    </pre>
12681    <p>
12682    
12683    The two largest sizes are good approximations of FF2 times we got
12684    informally before the switch to partitioned AbsolutePanels with
12685    charts with the corresponding number of elements (2000 or 3000).
12686    The overhead of the partitioning itself is very low, as shown by
12687    the modest time increase even when each element is placed into
12688    its own sub-panel. Tests with a < 256 element chart suggested at
12689    most a couple of ms of time increases due to the introduction of
12690    partitioning.
12691    
12692    <p>
12693    
12694    I kind of expected to see >61 second times with a sub-panel size
12695    of 1, since the parent panel still has >3,000 elements in this
12696    case. Whatever the cause of the performance logjam (presumably in
12697    the FF2 heap somewhere?)  simply the fact that you introduce a
12698    parent AbsolutePanel that holds various child AbslolutePanels
12699    that hold the actual image widgets appears to work around most of
12700    the problem. If such a useless change makes FF2 materially
12701    faster, that seems like a performance bug in FF2 to me.<p>
12702    
12703    The root FF cause is apparently NOT image cache related, though,
12704    since turning off the image cache via about:config didn't change
12705    times for the last row of the table above.
12706    
12707    */
12708         
12709    /* Strange, reorganized DOM layout in GChart v2.5 and now cannot
12710     * reproduce these results in FF2 (partitioning gave just a modest 1
12711     * second boost in a 15 second, 4,000 point test). So, I was about to
12712     * drop back to a simple AbsolutePanel. BUT, in FF3, now, the 4,000
12713     * point test crashes unless I use PartitionedAbsolutePanel! I guess
12714     * Firefox 3 has problems with divs that have 4,000 elements, or at
12715     * least 4,000 image elements, on them, which the partioning fixes.
12716     * 
12717     */
12718         
12719        final int WIDGETS_PER_PANEL = 256;
12720        private AbsolutePanel root = new AbsolutePanel(); 
12721        private AbsolutePanel subPanel = null; // "selected" subPanel
12722        private int iSubPanel = -1;    // index of "selected" subPanel
12723        private int nWidgets = 0;     // total # over all subPanels
12724       
12725        PartitionedAbsolutePanel() {
12726           super();
12727           initWidget(root);
12728        }
12729    
12730        /* resets the partitioned panel to it's initial state */
12731        public void clear() {
12732           root.clear();
12733           subPanel = null;
12734           iSubPanel = -1;
12735           nWidgets = 0;
12736        }
12737        
12738        public int getWidgetCount() {
12739           return nWidgets;
12740        }
12741    
12742        // makes the subpanel containing the widget the selected one.
12743        private void selectSubPanel(int iWidget) {
12744           if (iSubPanel != iWidget/WIDGETS_PER_PANEL) {
12745              iSubPanel = iWidget/WIDGETS_PER_PANEL;
12746              subPanel = (AbsolutePanel) root.getWidget(iSubPanel);
12747           }
12748        }
12749           
12750        // adds a widget to end of this partioned absolute panel
12751        public void add(Widget w) {
12752           if (nWidgets % WIDGETS_PER_PANEL == 0) { 
12753              // last panel is full, time to add a new one
12754              subPanel = new AbsolutePanel();
12755              // Panel sits in upper left corner. Does nothing, can't
12756              // be seen. It's just a holder for other widgets.
12757              GChart.setOverflow(subPanel, "visible");
12758              subPanel.setPixelSize(0,0);
12759              root.add(subPanel, 0, 0);
12760           }
12761           selectSubPanel(nWidgets);
12762           subPanel.add(w);
12763           nWidgets++;
12764        }
12765    
12766        // returns widget at given index
12767        public Widget getWidget(int iWidget) {
12768           if (iWidget < 0 || iWidget >= nWidgets)
12769                 throw new IllegalArgumentException(
12770    "Invalid widget index: " + iWidget + 
12771    ". Valid range is: 0..." + (nWidgets-1));
12772    
12773           selectSubPanel(iWidget);
12774           Widget result = subPanel.getWidget(
12775                              iWidget % WIDGETS_PER_PANEL);
12776           return result;
12777        }
12778      
12779        // Remove very last widget from panel. 
12780        public boolean remove(int iWidget) {
12781           if (iWidget != nWidgets-1)
12782              throw new IllegalArgumentException(
12783    "iWidgets arg = " + iWidget + " nWidgets-1 (" + (nWidgets-1)+") is required.");
12784    
12785           selectSubPanel(iWidget);
12786           boolean result = subPanel.remove(iWidget % WIDGETS_PER_PANEL);
12787           if (iWidget % WIDGETS_PER_PANEL == 0) { 
12788           // if deleted widget is last widget overall, and first on
12789           // the selected panel, selected panel will now be empty.
12790             root.remove(subPanel);
12791             iSubPanel = -1;    // next selectSubPanel will reset these
12792             subPanel = null;
12793           }
12794           nWidgets--;
12795           return result;
12796        }
12797    
12798        
12799        
12800         // To assure that w is on the selected subPanel, this method
12801         // must only be passed a widget that is in the currently
12802         // selected subPanel. This can be assured by passing in a
12803         // widget that was just added via add(), or just retrieved
12804         // via getWidget (otherwise, an exception will be thrown).
12805          
12806        public void setWidgetPosition(Widget w, int left, int top) {
12807           subPanel.setWidgetPosition(w, left, top);
12808        }
12809        
12810      } // end of class PartitionedAbsolutePanel
12811    
12812    
12813      static class Rectangle { // a (pixel graphics coords) rectangle
12814         double x;  // x, y at upper left corner of rectangle
12815         double y;
12816         double width;  // distance from x to right edge
12817         double height; // distance from y to bottom edge
12818      }
12819     /*
12820      * AbsolutePanel that allows annotations it contains to be easily
12821      * reused, for increased efficiency.
12822      * 
12823      */      
12824      class AnnotationRenderingPanel extends PartitionedAbsolutePanel {
12825         int labelIndex = 0;                // to-be-added-next label index 
12826         private int lastVisibleLabel = -1; // just before 1st valid index
12827         /*
12828          * Returns the inner grid of the first reusuable, non-occluding
12829          * aligned label in this rendering panel.
12830          * 
12831          */ 
12832         AlignedLabel getFirstInnerAlignedLabel() {
12833            AlignedLabel result = null;
12834            if (labelIndex > 0) {
12835               NonoccludingReusuableAlignedLabel parent =
12836                  (NonoccludingReusuableAlignedLabel) getWidget(0);
12837               result = parent.getInnerGrid();
12838            }
12839            return result;
12840         }
12841    
12842         /** 
12843          * Provides support for reusing certain property specifications
12844          * that are likely to be the same, given how aligned labels in a
12845          * GChart get reused, and given certain assumptions about which
12846          * properties of the labels are most likely to remain unchanged
12847          * between updates.
12848          * <p>
12849          * 
12850          * Also applies a hidden outter grid technique to allow proper
12851          * alignment with labels of unknown size without occluding
12852          * mouse events of nearby elements.
12853          * <p>
12854          *
12855          * Very similar in intent to the ReusableImage, see that
12856          * class' header comment for more info.
12857          * 
12858          */
12859         class NonoccludingReusuableAlignedLabel extends AlignedLabel {
12860            int fontSize = GChart.NAI;
12861            String fontStyle = USE_CSS;
12862            String fontWeight = USE_CSS;
12863            String fontColor = USE_CSS;
12864            HasHorizontalAlignment.HorizontalAlignmentConstant hAlign;
12865            HasVerticalAlignment.VerticalAlignmentConstant vAlign;
12866            String labelText = null;
12867            boolean isHTML = false;
12868            Widget labelWidget = null;
12869            final AlignedLabel innerGrid = new AlignedLabel();
12870    
12871            AlignedLabel getInnerGrid() {
12872               return innerGrid;
12873            }
12874    
12875            NonoccludingReusuableAlignedLabel() {
12876               super();
12877               setWidget(0, 0, innerGrid);
12878               /*
12879                * The basic technique being used in the lines below is
12880                * illustrated by this excerpt from p 317 of "CSS, The
12881                * Definitive Guide" by Eric A. Meyer: <p>
12882                *
12883                * <pre>
12884                *   p.clear {visibility: hidden;}
12885                *   p.clear em {visibility: visible;}
12886                * </pre>
12887                * <p>
12888                * 
12889                * In the above example, emphasized (italic) text is
12890                * positioned exactly as it would have been in the
12891                * paragraph had the normal text been visible, except that
12892                * the normal text <i>isn't</i> visible. And, unlike
12893                * transparent text, the invisible text also won't capture
12894                * mouse events (essential for our aligned labels)
12895                * 
12896                * <p>
12897                *
12898                * With GChart's aligned label, we want the outter Grid (HTML
12899                * table) to be "not there" as far as visibility and
12900                * mouseovers, but still impact centering and
12901                * other alignment of the stuff in the visible, inner Grid. 
12902                * 
12903                * <p>
12904                *
12905                * Note that we cannot just make the Grid color transparent
12906                * (tried that first) because in that case the oversized
12907                * outter Grid required for alignment will still grab the
12908                * mouseover events inappropriately (wrong hovertext
12909                * problem).
12910                * 
12911                * <p>
12912                *
12913                * Not certain but, apparently, IE6 requires that, if
12914                * you want to apply this trick when the outer element
12915                * is a table you must use another table as the
12916                * inner element. At least, the div inner element approach
12917                * I used at first (that basically worked in Firefox), made
12918                * both parent and child invisible in IE6.
12919                * <p>
12920                *
12921                * In summary:
12922                * 
12923                * The upside: alignment without inappropriate event occlusion
12924                * The downside: the extra Grid element saps performance
12925                *
12926                */
12927    
12928               DOM.setStyleAttribute(getElement(),
12929                                     "visibility","hidden");
12930               DOM.setStyleAttribute(innerGrid.getElement(),
12931                                     "visibility", "visible");
12932            }
12933    
12934            /*
12935             * Sets properties only if they have changed; to replace
12936             * expensive DOM calls with cheap inequality tests.
12937             *
12938             * TODO: Investigate moving properties that are guaranteed to be
12939             * the same across all elements in the image panel (backgroundColor,
12940             * borderColor, borderWidth, borderStyle, image url, possibly more)
12941             * into a single style for the image panel as a whole, and just add
12942             * that styleName to each image, and set the properties once, in
12943             * the style. If this worked, it would save both time and space
12944             * (style would be internal and not intended for direct developer
12945             * access, since in some charts canvas, not styles, would be
12946             * responsible for these curve properties), and the same approach
12947             * could be applied to the labelPanel.
12948             * 
12949             */ 
12950    
12951            void setReusableProperties(
12952                                       int fontSize,
12953                                       String fontStyle,
12954                                       String fontWeight,
12955                                       String fontColor,
12956                                       HasHorizontalAlignment.HorizontalAlignmentConstant hAlign,
12957                                       HasVerticalAlignment.VerticalAlignmentConstant vAlign,
12958                                       String labelText,
12959                                       boolean isHTML,
12960                                       Widget labelWidget) {
12961    
12962               if (this.fontSize != fontSize) {
12963                  DOM.setIntStyleAttribute(innerGrid.getElement(), "fontSize", fontSize);
12964                  this.fontSize = fontSize;
12965               }
12966               if (this.fontStyle != fontStyle) {
12967                  DOM.setStyleAttribute(innerGrid.getElement(), "fontStyle", fontStyle);
12968                  this.fontStyle = fontStyle;
12969               }
12970               if (this.fontWeight != fontWeight) {
12971                  DOM.setStyleAttribute(innerGrid.getElement(), "fontWeight", fontWeight);
12972                  this.fontWeight = fontWeight;
12973               }
12974               if (this.fontColor != fontColor) {
12975                  DOM.setStyleAttribute(innerGrid.getElement(),"color", fontColor);
12976                  this.fontColor = fontColor;
12977               }
12978               if (this.hAlign != hAlign) {
12979                  getCellFormatter().setHorizontalAlignment(0,0,hAlign);
12980                  // without this, only IE6-quirks doesn't quite align right:
12981                  innerGrid.getCellFormatter().setHorizontalAlignment(0,0,hAlign);
12982                  this.hAlign = hAlign;
12983               }
12984               if (this.vAlign != vAlign) {
12985                  getCellFormatter().setVerticalAlignment(0,0,vAlign);
12986                  // without this, only IE6-quirks doesn't quite align right:
12987                  innerGrid.getCellFormatter().setVerticalAlignment(0,0,vAlign);
12988                  this.vAlign = vAlign;
12989               }
12990    
12991               if (null != labelWidget) {
12992                  if (this.labelWidget != labelWidget) {
12993                     innerGrid.setWidget(0,0,labelWidget);
12994                     this.labelWidget = labelWidget;
12995                     this.labelText = null;
12996                  }
12997               }
12998               else if (this.labelText != labelText || this.isHTML != isHTML) {
12999                  if (null == labelText || "" == labelText)
13000                     innerGrid.setText(0,0,"");
13001                  else if (!isHTML) {
13002                     innerGrid.setText(0,0,labelText);
13003                  }
13004                  else {
13005                     innerGrid.setHTML(0, 0, labelText);
13006                  }
13007                  this.isHTML = isHTML;
13008                  this.labelText = labelText;
13009                  this.labelWidget = null;
13010               }
13011            }
13012         } // end of class NonoccludingReusuableAlignedLabel
13013    
13014         AnnotationRenderingPanel() {
13015            super();
13016    /*
13017     * Because of event-occlusion that can occur on all browsers but IE,
13018     * annotation panels MUST by 0-sized/overflow:visible otherwise they
13019     * will short-circuit event processing needed for widget annotations
13020     * added by developer. Graphics rendering panels don't have this
13021     * constraint and thus can be clipped to the plot area.
13022     * 
13023     */ 
13024            GChart.setOverflow(this, "visible");
13025            this.setPixelSize(0,0);
13026         }
13027    
13028         void setLabelPosition(NonoccludingReusuableAlignedLabel lbl, int x, int y) {
13029    // workaround problem with special meaning of (-1,-1) to
13030    // setWidgetPosition (makes position off by one pixel).
13031            if (x == -1 && y == -1) x = 0;
13032            setWidgetPosition(lbl, x, y);
13033         }
13034    
13035         void beginRendering() {       
13036            labelIndex = 0;
13037         }  
13038    
13039         void endRendering() {
13040            // hide or remove labels no longer being used
13041            for (int iLabel = optimizeForMemory ?
13042                              (getWidgetCount()-1) :
13043                              lastVisibleLabel;
13044                 iLabel >= labelIndex;
13045                 iLabel--) {
13046               Widget w = getWidget(iLabel);
13047               if (optimizeForMemory) 
13048                  remove(iLabel);
13049               else 
13050                  w.setVisible(false);
13051            }
13052            lastVisibleLabel = labelIndex-1;
13053         }
13054    
13055         /*
13056          * Creates (or reveals), and configures, an aligned label. Works
13057          * very similarly to addOrRevealImage.
13058          *
13059          */
13060         NonoccludingReusuableAlignedLabel getNextOrNewAlignedLabel(
13061            int fontSize,
13062            String fontStyle,
13063            String fontWeight,
13064            String fontColor,
13065            HasHorizontalAlignment.HorizontalAlignmentConstant hAlign,
13066            HasVerticalAlignment.VerticalAlignmentConstant vAlign,
13067            String labelText,
13068            boolean isHTML,
13069            Widget labelWidget) {
13070            NonoccludingReusuableAlignedLabel result;
13071            if (labelIndex < getWidgetCount()) {       
13072               result = (NonoccludingReusuableAlignedLabel)
13073                        getWidget(labelIndex);
13074               if (null != result.labelWidget &&
13075                   labelWidget == result.labelWidget) {
13076    /*
13077     * DOM element actually stored in the label's Grid-cell, and what
13078     * the label "thinks" is stored there, could be inconsistent if,
13079     * for example, the same label widget reference was used to
13080     * define two different points' annotations. In that case, we
13081     * need to clear the widget reference that the label stores
13082     * thus making it consistent with what is really in the DOM.
13083     * <p>
13084     *
13085     * This code was added to fix the bug reproduced by
13086     * TestGChart53.java. See that test for more info.
13087     * 
13088     *
13089     */
13090                  Element e = labelWidget.getElement();
13091                  if (null == e ||
13092                      (e.getParentElement() != 
13093                      result.innerGrid.getCellFormatter().getElement(0,0)))
13094    // the widget' DOM parent isn't label's grid-cell (it was moved)
13095                     result.labelWidget = null;
13096               }
13097                   
13098               if (labelIndex > lastVisibleLabel)
13099                  result.setVisible(true);
13100            }
13101            else {
13102               result = new NonoccludingReusuableAlignedLabel();
13103               add(result);
13104            }
13105            result.setReusableProperties(fontSize, fontStyle, fontWeight,
13106                                         fontColor, hAlign, vAlign, labelText, isHTML, labelWidget);
13107    
13108            if (lastVisibleLabel < labelIndex)
13109               lastVisibleLabel = labelIndex;
13110            labelIndex++;
13111            return result;
13112         }
13113    
13114         protected void renderAnnotation(Annotation annotation,
13115                                         AnnotationLocation loc,
13116                                         double xCenter,
13117                                         double yCenter,
13118                                         double symWidth,
13119                                         double symHeight,
13120                                         Symbol symbol) {
13121    
13122            int widthUpperBound = annotation.getWidthUpperBound();
13123            int upLeftX = loc.getUpperLeftX(xCenter,
13124                             widthUpperBound, Math.abs(symWidth));
13125            int heightUpperBound = annotation.getHeightUpperBound();
13126            int upLeftY = loc.getUpperLeftY(yCenter,
13127                                heightUpperBound, Math.abs(symHeight));
13128    
13129    
13130            NonoccludingReusuableAlignedLabel alignedLabel =
13131               getNextOrNewAlignedLabel(
13132                                        annotation.getFontSize(),
13133                                        annotation.getFontStyle(),
13134                                        annotation.getFontWeight(),
13135                                        annotation.getFontColor(),
13136                                        loc.getHorizontalAlignment(),
13137                                        loc.getVerticalAlignment(),
13138                                        annotation.getText(), annotation.isHTML(),
13139                                        annotation.getWidget());
13140            // If positioning by top or left edges, explicit sizing isn't needed
13141            // (makes the bounding box tighter, which, for reasons unknown, makes
13142            // rendering around 10% faster on some browsers and usage scenarios).  
13143            if (loc.getHorizontalAlignment() !=
13144                HasHorizontalAlignment.ALIGN_LEFT)
13145               alignedLabel.setWidth(widthUpperBound + "px");
13146            else
13147               alignedLabel.setWidth("");
13148    
13149            if (loc.getVerticalAlignment() !=
13150                HasVerticalAlignment.ALIGN_TOP)
13151               alignedLabel.setHeight(heightUpperBound + "px");
13152            else
13153               alignedLabel.setHeight("");
13154    
13155            setLabelPosition(alignedLabel, upLeftX, upLeftY);
13156         }
13157    
13158    
13159      } // end of class AnnotationRenderingPanel
13160      
13161       /*
13162        * A rendering panel contains subpanels
13163        * for Image-element based graphics rendering, canvas-based graphics
13164        * rendering, and Grid-based compass-aligned label rendering.  <p>
13165        *
13166        * Each rendering panel is joined-at-the-hip with, and provides the
13167        * in-the-browser-realization of, a single corresponding GChart curve
13168        * The one exception to this rule are the system curves used
13169        * internally for rendering chart decorations, which, for reasons of
13170        * efficiency, all share the same rendering panel.  <p>
13171        * 
13172        * Rather than clearing the widgets contained in the rendering panel
13173        * and recreating and adding them back as needed (which is expensive)
13174        * the rendering panel can make widgets it employs for these purposes
13175        * invisible when not in use and visible again when they are needed
13176        * (which is usually at least twice as fast).  <p>
13177        * 
13178        * <p> In principle, this is less memory efficient, but in
13179        * practice, due to the fact that there is less likelyhood of
13180        * fragmentation with reuse than with relying of the garbage
13181        * collector, it could even be more memory efficient. 
13182        * 
13183        * <p>
13184        * 
13185        * When a canvas factory is specified by the developer, the panel
13186        * will include a single canvas widget, where most of the graphical
13187        * elements associated with the curve that uses this rendering panel
13188        * will be drawn.  GChart's canvas support is not yet up to the task
13189        * of rendering everything--it renders only those aspects of the
13190        * chart where canvas rendering provides the biggest quality/speed
13191        * advantages.  So, even if a canvas factory has been provided, many
13192        * aspects of a curve (e.g. the rectangles in bar charts) will still
13193        * be rendered with Image elements.
13194        * 
13195        */
13196      class GraphicsRenderingPanel extends AbsolutePanel {
13197         private GChartCanvasLite canvas = null;
13198         int x0 = 0;  // origin, in pixel coords, of upper left..
13199         int y0 = 0;  //   corner of rendering canvas widget
13200         int canvasWidth = 0; // width of last used rendering canvas
13201         int canvasHeight = 0; // height of last used rendering canvas
13202         private AbsolutePanel canvasPanel = new AbsolutePanel();
13203         private PartitionedAbsolutePanel imagePanel = new PartitionedAbsolutePanel();
13204         int imageIndex = 0;
13205         // helps minimize calls to setVisible (which can be expensive)
13206         private int lastVisibleImage = -1;
13207         // Add a canvas, if needed
13208         void maybeAddCanvas() {
13209            if (null != canvasFactory && null == canvas) {
13210               canvas = canvasFactory.create();
13211               if (null != canvas) {
13212                  if (canvas instanceof Widget) {
13213    /*
13214     * The next line is only needed for IE; it is needed to work-around a
13215     * GWTCanvas bug that improperly shifts the x-placement of rendered
13216     * graphics when a GChart is placed into a non-left-aligned Grid cell
13217     * (GChart uses Grid Widgets to implement its annotations feature, so a
13218     * GChart placed as an annotation on another GChart, as would occur with
13219     * an inset or popup chart, will end up within an aligned Grid). 
13220     * <p>
13221     * 
13222     * See also TestGChart46.java, which reproduces the GWTCanvas bug.
13223     *
13224     */ 
13225                     DOM.setElementAttribute(((Widget) canvas).getElement(),
13226                                             "align", "left");
13227                     canvasPanel.add((Widget) canvas, 0, 0);
13228                  }
13229                  else   
13230                    throw new IllegalStateException(
13231                      "Your canvas factory's create method did not return " +
13232                      "either null or a GWT Widget, as required. See the " + 
13233                      "GChart.setCanvasFactory method javadocs for details.");
13234               }
13235            }
13236         }
13237    
13238         
13239    /** 
13240      * Provides support for reusing certain property specifications
13241      * that are likely to be the same, given how images in a GChart
13242      * get reused, and given certain assumptions about which
13243      * properties of the image are most likely to remain unchanged
13244      * between updates.  For the most common scenarios, chart
13245      * updates are significantly faster due to replacing (relatively
13246      * expensive) DOM style attribute setting with (relatively
13247      * cheap) String reference or integer equality tests.
13248      * <p>
13249      *
13250      * For hovertext, the class also lets us defer actual generation
13251      * of the hovertext until they actually mouse over the image,
13252      * saving further time (it's surprisingly expensive just to
13253      * format the numbers and such used in hovertexts).
13254      * <p>
13255      *
13256      * TODO: Since we no longer use events on Image widgets,
13257      * see if we can switch to just using simpler HTML elements,
13258      * if that reduces the overhead associated with a Widget?
13259      * 
13260      * 
13261      */
13262       class ReusableImage extends Image {
13263         private String backgroundColor = USE_CSS;
13264         private String borderColor = USE_CSS;
13265         private String borderStyle= USE_CSS;
13266         // the capped border width, times two (to allow half-pixel widths)
13267         private int cappedBorderWidthX2 = GChart.NAI;
13268         private int width = GChart.NAI;
13269         private int height = GChart.NAI;
13270         int x = GChart.NAI;
13271         int y = GChart.NAI;
13272         String url = null;
13273         
13274         ReusableImage() {
13275            super();
13276            this.url = null;  
13277         }
13278    
13279               
13280         void setReusableProperties(String backgroundColor,
13281                                    String borderColor,
13282                                    String borderStyle,
13283                                    int borderWidth,
13284                                    double dWidth,
13285                                    double dHeight,
13286                                    double xD,
13287                                    double yD,
13288                                    String url) {
13289    
13290          // Round two edges, and define width to be their difference.  
13291          // (rounding this way assures bars align with gridlines, etc.)
13292          int newX = (int) Math.round(xD);
13293          int newW = (int) Math.round(xD + dWidth) - newX;
13294          int newY =  (int) Math.round(yD);
13295          int newH = (int) Math.round(yD + dHeight) - newY;
13296          int thickness = (newW < newH) ? newW : newH;
13297          // Don't allow borders that would exceed specified width or
13298          // height. So, if smaller of width, height is at least twice the
13299          // border width, border width is used as is, otherwise,
13300          // it's replaced with half the smaller of width, height:
13301          int newCappedBorderWidthX2 =    
13302             (2*borderWidth < thickness) ? 2*borderWidth : thickness;                        
13303    
13304    
13305    /*
13306     * Note: on a GWT absolute panel, the x,y position of the widget is the
13307     * upper left corner of the widget's border, so x, y need no adjustment
13308     * to account for an internal (positive) border. Negative (external)
13309     * borders expand rectangle equally in all directions, so x,y need to
13310     * shift back to the new upper left corner.  Transparent border
13311     * emulation sets border width to 0, and adjusts element size and
13312     * position to mimic border transparency (this rather odd feature is
13313     * required to workaround the IE6 "transparent border" bug)
13314     * 
13315     */ 
13316          if (TRANSPARENT_BORDER_COLOR == borderColor) {//transparency emulation
13317             if (newCappedBorderWidthX2 > 0) {
13318                // to emulate an internal transparent border using a 0 width
13319                // border, we need to shift the upper left corner by the
13320                // amount of border, and shrink the size by twice the amount
13321                // of the border.
13322                newX += newCappedBorderWidthX2/2; // shift upper left corner
13323                newY += newCappedBorderWidthX2/2; 
13324                newH -= newCappedBorderWidthX2; // shrink size
13325                newW -= newCappedBorderWidthX2; 
13326             }
13327             // else, external border is just eliminated, no adjustment needed
13328             newCappedBorderWidthX2 = 0;
13329             borderColor = "transparent"; // because DOM won't accept null
13330             if (backgroundColor == TRANSPARENT_BORDER_COLOR)
13331                backgroundColor = "transparent";
13332          }
13333          else if (newCappedBorderWidthX2 < 0) {
13334            newX += newCappedBorderWidthX2/2; // shift upper left corner back
13335            newY += newCappedBorderWidthX2/2; // to incorporate external border.
13336          }
13337          else {
13338            newH -= newCappedBorderWidthX2; // shrink size to incorporate
13339            newW -= newCappedBorderWidthX2; // impact of internal border.
13340          }
13341          
13342          if (cappedBorderWidthX2 != newCappedBorderWidthX2) {
13343             if (1 == (newCappedBorderWidthX2 % 2)) {
13344    // odd pixel 2 x borderWidth needs asymetical borders to fill rect
13345    // (only positive (internal) borders can have half-pixel widths)
13346                int floorBW = newCappedBorderWidthX2/2;
13347                int ceilBW = floorBW+1;
13348                // (top, right, bottom, left) == (floor, floor, ceil, ceil)
13349                // assures symbol is odd-pixel border-filled in all cases
13350                DOM.setStyleAttribute(getElement(),
13351                                   "borderWidth",
13352                                   floorBW+"px "+floorBW+"px "+
13353                                   ceilBW+"px " + ceilBW+"px ");
13354             }
13355             else {
13356               DOM.setStyleAttribute(getElement(),
13357                   "borderWidth", Math.abs(newCappedBorderWidthX2/2)+"px");
13358             }
13359             cappedBorderWidthX2 = newCappedBorderWidthX2;
13360          }
13361          
13362          if (GChart.NAI == this.x) { 
13363             // At first, use AbsolutePanel's official API 
13364             // (to insulate us from any future AbsolutePanel
13365             // changes)
13366             setImagePosition(this, newX, newY);
13367             this.x = newX;
13368             this.y = newY;
13369          }
13370          else { // for speed, just set the edge positions that changed
13371                 // (works, but bypasses AbsolutePanel's official API)
13372             if (this.x != newX) {
13373                DOM.setStyleAttribute(getElement(),"left", newX+"px");
13374                this.x = newX;
13375             }
13376             if (this.y != newY) {
13377                DOM.setStyleAttribute(getElement(),"top", newY+"px");
13378                this.y = newY;
13379             }
13380          }
13381    
13382          if (this.width != newW) {
13383            setWidth(newW + "px");
13384            this.width = newW;
13385          }
13386          if (this.height != newH) {
13387            setHeight(newH + "px");
13388            this.height = newH;
13389          }
13390      
13391          if (this.backgroundColor != backgroundColor) { 
13392             DOM.setStyleAttribute(getElement(), "backgroundColor",
13393                                   backgroundColor);
13394             this.backgroundColor =backgroundColor;
13395          }
13396          if (this.borderColor != borderColor) {
13397             DOM.setStyleAttribute(getElement(), "borderColor", borderColor);
13398             this.borderColor = borderColor;
13399          }
13400          if (this.borderStyle != borderStyle) {
13401             DOM.setStyleAttribute(getElement(), "borderStyle",
13402                                   borderStyle);
13403             this.borderStyle = borderStyle;
13404          }
13405    
13406          if (this.url != url) {
13407    /*
13408     * WARNING: Redundant setUrls cause leaks in FF 2.0.0.16. So, be
13409     * particularly careful not to accidentally "double set" a URL to
13410     * the exact same URL (I did this with a slightly less efficient
13411     * initialization of my images, and this caused a huge memory
13412     * leak that I hope to memorialize, and lay to rest forever, here.)
13413     *
13414     * Symptoms, in FF 2 only, are those that would occur AS IF the
13415     * extra setUrl increased the reference count on the (browser
13416     * cached) image file so that Firefox can't release either it, or
13417     * any of the img elements that reference it. A very big leak for
13418     * GChart, since just about everything in a GChart references the
13419     * exact same blank gif URL.
13420     *
13421     * Such symptoms did not occur in IE7, or in FF 2 if the cache
13422     * has been disabled via "about:config".
13423     *
13424     * Search for "massively leak" and below that comment you will
13425     * find two lines that, if uncommented, make the leak reappear.
13426     *  
13427     */ 
13428             setUrl(url);
13429             this.url = url;
13430          }      
13431        }
13432      } // end of class ReusableImage
13433    
13434    
13435    
13436      
13437       GraphicsRenderingPanel() {
13438           super();
13439    // Overflow of this panel is controlled when it is added
13440    //       GChart.setOverflow(this, "visible");
13441           GChart.setOverflow(canvasPanel, "visible");
13442           GChart.setOverflow(imagePanel, "visible");
13443           // these sub-panels have no size themselves, they are merely
13444           // there to segregate background, images, and labels.
13445           canvasPanel.setPixelSize(0,0);
13446           imagePanel.setPixelSize(0,0);
13447           this.add(canvasPanel, 0, 0);          
13448           this.add(imagePanel, 0, 0);
13449         }
13450    
13451         GChartCanvasLite getCanvas() {
13452           return canvas;
13453         }
13454    
13455         void setImagePosition(ReusableImage img, int x, int y) {
13456    // workaround problem of special meaning of (-1,-1) to
13457    // setWidgetPosition (makes position off by one pixel, though).
13458            if (x == -1 && y == -1) x = 0;
13459            imagePanel.setWidgetPosition(img, x, y);
13460         }
13461    
13462         // Tells panel you are ready to start drawing the curve on it
13463         void beginRendering(Rectangle canvasRegion) {
13464            if (null != canvas) {
13465               if (null == canvasRegion) {
13466                  // hold onto empty canvas for simplicity
13467                     canvas.resize(0, 0);
13468                     canvasWidth = canvasHeight = 0;
13469               }
13470               else {
13471                  int width = (int) Math.round(canvasRegion.width);
13472                  int height = (int) Math.round(canvasRegion.height);
13473    // if exactly same size, just clear...seems to save a little time
13474                  if (width == canvasWidth && height == canvasHeight)
13475                     canvas.clear(); // reuse same canvas
13476                  else { // size changed
13477                    canvas.resize(width, height);
13478                    canvasWidth = width;
13479                    canvasHeight = height;
13480                  }
13481                  x0 = (int) Math.round(canvasRegion.x);
13482                  y0 = (int) Math.round(canvasRegion.y);
13483    // workaround problem with special meaning of (-1,-1) to
13484    // setWidgetPosition (makes position off by one pixel).
13485                  if (x0 == -1 && y0 == -1) x0 = 0;
13486                  canvasPanel.setWidgetPosition((Widget) canvas, x0, y0);
13487               }
13488            }
13489            imageIndex = 0;
13490         }
13491         // Tells panel you are done drawing on it, and
13492         // it's OK to do any final cleanup/bookkeeping needed.
13493         void endRendering() {
13494           // hide or remove images no longer being used
13495            for (int iImage = optimizeForMemory ?
13496                              (imagePanel.getWidgetCount()-1) :
13497                              lastVisibleImage; 
13498                iImage >= imageIndex;
13499                iImage--) {
13500              Widget w = imagePanel.getWidget(iImage);
13501              if (optimizeForMemory)
13502                 imagePanel.remove(iImage);
13503              else
13504                DOM.setStyleAttribute(w.getElement(), "visibility", "hidden");
13505    // setVisible unreliable w Images in IE as shown in TestGChart41a.java
13506    //             w.setVisible(false);
13507           }
13508           lastVisibleImage = imageIndex - 1;            
13509         }
13510    
13511         /* Speedier, reusable, rendering-panel-managed images. In effect,
13512            turns image panel into a specialized memory manager. */
13513         void addOrRevealImage(String backgroundColor,
13514                               String borderColor,
13515                               String borderStyle,
13516                               int borderWidth,
13517                               double width,
13518                               double height,
13519                               double x, double y,
13520                               String url) {
13521           ReusableImage img;
13522           if (imageIndex <
13523               imagePanel.getWidgetCount()) { // reuse an old image       
13524             img = (ReusableImage) imagePanel.getWidget(imageIndex);
13525             if (imageIndex > lastVisibleImage)
13526    // "" visibility means "visible whenever the parent is visible"
13527                DOM.setStyleAttribute(img.getElement(), "visibility", "");
13528    // setVisible unreliable for Images in IE as shown in TestGChart41a.java
13529    //           img.setVisible(true);
13530           }
13531           else {                             // add a new image  
13532             img = new ReusableImage();
13533             imagePanel.add(img);
13534           }
13535    
13536           img.setReusableProperties(backgroundColor,
13537                                        borderColor,
13538                                        borderStyle,
13539                                        borderWidth,
13540                                        width,
13541                                        height,
13542                                        x, y, url);
13543                                       
13544           if (lastVisibleImage < imageIndex)
13545             lastVisibleImage = imageIndex;
13546           imageIndex++;
13547    
13548         }
13549    // Decided, for simplicitly, to just use HTML for rectanglular symbols. 
13550    // May change my mind again, so leaving code below in comments
13551         /* Fills a rectangle.
13552          * <p>
13553          * 
13554          * Needed because GChartCanvasLite does not include fillRect
13555          * <p>
13556          * 
13557          * TODO: Check if this slows us much compared to using canvas'
13558          * fillRect. We may also later need to use other canvas features
13559          * not in GChartCanvasLite (gradients? drawImage?) so should bundle
13560          * all such changes together, in one, final, GChart canvas
13561          * interface-related change to minimize interface-changing
13562          * aggravations for developers.
13563          * 
13564          */ 
13565    //     private void fillRect(GChartCanvasLite canvas, double x, double y, double width, double height) {
13566    //       canvas.beginPath();
13567    //       canvas.moveTo(x - rp.x0,y  - rp.y0);  
13568    //       canvas.lineTo(x+width - rp.x0, y - rp.y0);
13569    //       canvas.lineTo(x+width - rp.x0,
13570    //                     y+height  - rp.y0);
13571    //       canvas.lineTo(x  - rp.x0,
13572    //                     y+height - rp.y0);
13573    //       canvas.closePath();
13574    //       canvas.fill();
13575    //     }
13576         /*
13577          * Uses canvas to emulate a single Image-based rectangle,
13578          * assuming that the image URL points to a transparent GIF
13579          * 
13580          */
13581    //     void drawBorderedImage(String backgroundColor,
13582    //                            String borderColor,
13583    //                            String borderStyle,
13584    //                            int borderWidth,
13585    //                            double width,
13586    //                            double height,
13587    //                            double x, double y) {
13588    //        double xOut = x + ((borderWidth < 0) ? borderWidth : 0); 
13589    //        double yOut = y + ((borderWidth < 0) ? borderWidth : 0); 
13590    //        double xIn = x + ((borderWidth > 0) ? borderWidth : 0); 
13591    //        double yIn = y + ((borderWidth > 0) ? borderWidth : 0); 
13592    //        double wOut  = width + ((borderWidth < 0) ? (-2*borderWidth) : 0); 
13593    //        double hOut  = height + ((borderWidth < 0) ? (-2*borderWidth) : 0); 
13594    //        double wIn  = width + ((borderWidth > 0) ? (-2*borderWidth) : 0); 
13595    //        double hIn  = height + ((borderWidth > 0) ? (-2*borderWidth) : 0);
13596    //        if (TRANSPARENT_BORDER_COLOR != borderColor &&
13597    //            "transparent" != borderColor) {
13598    //          double absBW = Math.abs(borderWidth);
13599    //          canvas.setFillStyle(borderColor);
13600    //          // draw the four rectangles forming the outer perimeter 
13601    //          fillRect(canvas, xOut, yOut, absBW, hOut);
13602    //          fillRect(canvas, xOut, yOut, wOut, absBW);
13603    //          fillRect(canvas, xOut, yIn+hIn, wOut, absBW);
13604    //          fillRect(canvas, xIn+wIn, yOut, absBW, hOut);
13605    //        }
13606    //        if (TRANSPARENT_BORDER_COLOR != backgroundColor &&
13607    //            "transparent" != backgroundColor) {
13608    //          // draw the inside-the-border rectangle 
13609    //          canvas.setFillStyle(backgroundColor);
13610    //          fillRect(canvas, xIn, yIn, wIn, hIn);
13611    //        }
13612    //     }
13613         
13614      public void renderBorderedImage(String backgroundColor,
13615                                      String borderColor,
13616                                      String borderStyle,
13617                                      int borderWidth,
13618                                      double width,
13619                                      double height,
13620                                      double x, double y,
13621                                      String url) {
13622    //     if (null != canvas && url == getBlankImageURL() &&
13623    //         (borderStyle == USE_CSS || borderStyle.equals("solid"))) 
13624               /*
13625                * Use canvas to emulate a transparent, bordered image
13626                * (GChart can only render solid borders and blank image URLS
13627                * with canvas at this point)
13628                * 
13629                */ 
13630    //           drawBorderedImage(backgroundColor,
13631    //                            borderColor,
13632    //                            borderStyle,
13633    //                            borderWidth,
13634    //                            width,
13635    //                            height,
13636    //                            x, y);           
13637    //        else // use an actual image HTML element
13638               addOrRevealImage(backgroundColor,
13639                                 borderColor,
13640                                 borderStyle,
13641                                 borderWidth,
13642                                 width,
13643                                 height,
13644                                 x, y, url);
13645    
13646         }
13647    
13648       
13649       } // end of class GraphicsRenderingPanel
13650    
13651      // An AbsolutePanel that just turns the protected insert method public
13652      class InsertableAbsolutePanel extends AbsolutePanel {
13653         public void insert(Widget child,
13654                          com.google.gwt.user.client.Element container,
13655                          int beforeIndex,
13656                            boolean domInsert) {
13657            super.insert(child, container, beforeIndex, domInsert);
13658         }
13659      }
13660      
13661       class PlotPanel extends AbsolutePanel {
13662         private int topMargin;
13663         private int xAxisEnsembleHeight;
13664         private int xChartSize;
13665         private double xMax = Double.NaN;
13666         private double xMin = Double.NaN;
13667         private int y2AxisEnsembleWidth;
13668         private double y2Max = Double.NaN;
13669         private double y2Min = Double.NaN;
13670         private int yAxisEnsembleWidth;
13671         private int chartLegendThickness;
13672         private int chartFootnotesThickness;
13673         private int yChartSize;
13674         private double yMax = Double.NaN;
13675         private double yMin = Double.NaN;
13676    // Retains the last moved-to (Event.ONMOUSEMOVE) client mouse position, or NAI if
13677    // mouse moved away from chart entirely.     
13678         private int clientX = GChart.NAI;
13679         private int clientY = GChart.NAI;
13680         // Pixel coords of above mouse position, relative to top-left
13681         // corner of the GChart (mouse position in GChart's pixel coords)
13682         private int xMouse = GChart.NAI;
13683         private int yMouse = GChart.NAI;
13684         // first rendering panel is reserved for chart decorations,
13685         // and its overflow outside of the plot panel is never hidden
13686         final static int DECORATIVE_RENDERING_PANEL_INDEX = 0;
13687         private InsertableAbsolutePanel graphicsPanel =
13688            new InsertableAbsolutePanel();
13689         private InsertableAbsolutePanel annotationPanel =
13690            new InsertableAbsolutePanel();
13691         
13692         /*
13693          * Adds a sub-panel of this plot panel that contains the widgets
13694          * used to render the graphical parts of the given curve
13695          * <p>
13696          * 
13697          * This method must be called just after a new curve is added to
13698          * the chart, to add it's associated graphics rendering panel;
13699          * GChart assumes each curve (except internal decoration rendering
13700          * curves, which share a single rendering panel for efficiency)
13701          * already has an corresponding, unique, rendering panel available
13702          * and ready to go during updates.
13703          * 
13704          */ 
13705    
13706         void addGraphicsRenderingPanel(int rpIndex) {
13707            final boolean domInsert = true;
13708            GraphicsRenderingPanel w = new GraphicsRenderingPanel();
13709            if (DECORATIVE_RENDERING_PANEL_INDEX == rpIndex ||
13710                isHoverFeedbackRenderingPanel(rpIndex) ||
13711                !getClipToPlotArea()) {
13712             // chart decorations and hover feedback are never clipped
13713              w.setPixelSize(0, 0);
13714              GChart.setOverflow(w, "visible");
13715            }
13716            else {
13717              w.setPixelSize(getXChartSize(), getYChartSize());
13718              GChart.setOverflow(w, "hidden");
13719            }
13720            graphicsPanel.insert(w, graphicsPanel.getElement(), rpIndex, domInsert); 
13721            graphicsPanel.setWidgetPosition(w, 0, 0);
13722         }
13723         
13724         /*
13725          * Adds a sub-panel of this plot panel that contains the widgets
13726          * used to render the annnotations of the given curve
13727          * <p>
13728          * 
13729          * This method must be called just after a new curve is
13730          * added to the chart, to add it's associated annotation
13731          * rendering panel; GChart assumes each curve has a
13732          * correspondingly indexed rendering panel during updates.
13733          * 
13734          */ 
13735    
13736         void addAnnotationRenderingPanel(int rpIndex) {
13737            final boolean domInsert = true;
13738            AnnotationRenderingPanel w = new AnnotationRenderingPanel();
13739            annotationPanel.insert(w, annotationPanel.getElement(), rpIndex, domInsert); 
13740            annotationPanel.setWidgetPosition(w, 0, 0);
13741         }
13742    
13743         /*
13744          * Removes the rendering panel of the curve with the given
13745          * internal index on the curves list.
13746          * <p>
13747          *
13748          * This method must be called just before a curve is removed
13749          * from the chart, to remove the widgets used to render
13750          * that curve in the browser.
13751          *
13752          */
13753         void removeGraphicsRenderingPanel(int rpIndex) {
13754            graphicsPanel.remove(rpIndex); 
13755         }
13756         void removeAnnotationRenderingPanel(int rpIndex) {
13757            annotationPanel.remove(rpIndex); 
13758         }
13759    
13760         /*
13761          * Returns panel used to render the graphical and textual element of
13762          * the curve with the given internal index within the browser.
13763          * 
13764          */
13765         GraphicsRenderingPanel getGraphicsRenderingPanel(int rpIndex) {
13766            if (0 == graphicsPanel.getWidgetCount()) // for lazy addition
13767              // smaller,faster if all background curves put on single panel 
13768               for (int i = N_PRE_SYSTEM_CURVES-1; i < curves.size(); i++) {
13769                 int rpInd = getRenderingPanelIndex(i);
13770                 addGraphicsRenderingPanel(rpInd);
13771               }
13772            GraphicsRenderingPanel result = (GraphicsRenderingPanel)
13773                                    (graphicsPanel.getWidget(rpIndex));
13774            return result;
13775         }
13776         AnnotationRenderingPanel getAnnotationRenderingPanel(int rpIndex) {
13777            if (0 == annotationPanel.getWidgetCount()) // for lazy addition
13778              // smaller,faster if all background curves put on single panel 
13779              for (int i = N_PRE_SYSTEM_CURVES-1; i < curves.size(); i++) {
13780                 int rpInd = getRenderingPanelIndex(i);
13781                 addAnnotationRenderingPanel(rpInd);
13782              }
13783            AnnotationRenderingPanel result = (AnnotationRenderingPanel)
13784                                    (annotationPanel.getWidget(rpIndex));
13785            return result;
13786         }
13787         
13788         int getClientX() {return clientX;}
13789         void setClientX(int clientX, boolean isClick) {
13790    /*
13791     * Due to presumed bugs in FF2 and Chrome, space-bar clicking on
13792     * TestGChart25's "rotate" button produces bogus 0 and/or (in Chrome)
13793     * seemingly random negative return values from <tt>event.getClient[XY]
13794     * with the ONCLICK event. IE7 produces correct mouse coordinates for
13795     * Event.ONCLICK in this case. The bogus coordinates, if not corrected,
13796     * generate bogus "mouse moved off chart"-like actions (in TestGChart25,
13797     * Chrome produces inappropriate deselection of the hovered over point
13798     * after a space-bar invoked update)<p>
13799     *
13800     * Workaround is to just ignore any 0 or negative coordinates--thus
13801     * using the last valid coordinates seen by the chart's mouse tracking
13802     * code in lieu of the bogus ones.
13803     * 
13804     * <p>
13805     * 
13806     * The resulting 1px "partly-dead" band at the top and left edges
13807     * of the client area due to this workaround (0 is a valid client
13808     * coordinate) is unlikely to be a significant problem, since
13809     * clicked-on stuff is rarely clicked on right along the edges
13810     * of the client area.
13811     * <p>
13812     * 
13813     */
13814            if (clientX <= 0 && isClick)
13815               return;
13816            else if (clientX < 0)
13817       // some browsers (e.g. FF2) use -1 to indicate undefined mouse coords.
13818               clientX = GChart.NAI;
13819       
13820            this.clientX = clientX;  
13821            // computing this on-the-fly is VERY expensive, so we retain it
13822            // (the buffering can be wrong in unusual scrolling scenarios)
13823            xMouse = (GChart.NAI == clientX) ? GChart.NAI :
13824                        (Window.getScrollLeft() + clientX - getAbsoluteLeft());
13825         }
13826         int getClientY() {return clientY;}
13827         // See comments on analogous lines in setClientX above
13828         void setClientY(int clientY, boolean isClick) { 
13829            if (clientY <= 0 && isClick)
13830               return; 
13831            else if (clientY < 0)
13832               clientY = GChart.NAI;
13833            
13834            this.clientY = clientY; 
13835            yMouse = (GChart.NAI == clientY) ? GChart.NAI :
13836                     (Window.getScrollTop() + clientY -
13837                      getAbsoluteTop());
13838         }
13839    
13840         /*
13841          * In IE drop-down list boxes, when you enter the dropdown
13842          * part of the list, client coordinates suddenly become -1, -1
13843          * (presumably IE's way of saying it won't tell you what they are;
13844          * apparently the dropdown part of the list isn't in the DOM)
13845          * These repair methods replace such impossible client coordinates
13846          * with the last valid coordinates.
13847          * <p>
13848          * 
13849          * Without this patch, the geometric "within the hover widget"
13850          * test on GChartExample20a fails in IE7: as soon as user mouses
13851          * into the dropdown list, the entire hover widget is closed.  <p>
13852          *
13853          */ 
13854         private int repairBadClientX(int x) {
13855            if (x <= 0)  // 0 isn't strictly bad, but its one of the
13856                         // bad values that can pop up in some browsers.
13857               return clientX;
13858            else
13859               return x;
13860         }
13861         
13862         private int repairBadClientY(int y) {
13863            if (y <= 0)
13864               return clientY;
13865            else
13866               return y;
13867         }
13868         
13869         int getXMouse() {
13870             return xMouse;        
13871         }
13872         int getYMouse() {
13873           return yMouse;        
13874         }
13875    
13876         // Mouse x position relative to plot area upper left corner.
13877         int getXMousePlotArea() {
13878            int result = xMouse-yAxisEnsembleWidth;
13879             return result;
13880         }
13881         // Mouse y position relative to plot area upper left corner.
13882         int getYMousePlotArea() {
13883           int result = yMouse-topMargin;
13884           return result;        
13885         }
13886         
13887         PlotPanel() {
13888            super();
13889    // allows labels, symbols, that extend a tad off the
13890    // chart proper to still appear on the chart; AbsolutePanel
13891    // default is to truncate these. 
13892            GChart.setOverflow(this, "visible");
13893            GChart.setOverflow(graphicsPanel, "visible");
13894            GChart.setOverflow(annotationPanel, "visible");
13895           // these sub-panels have no size themselves, they are merely
13896           // there to segregate the graphical and annotation part of chart
13897            graphicsPanel.setPixelSize(0,0);
13898            annotationPanel.setPixelSize(0,0);
13899    // this order assures all the annotations are on top of all the graphics
13900            this.add(graphicsPanel, 0, 0);          
13901            this.add(annotationPanel, 0, 0);        
13902    // events for hover selection feedback, click event handling
13903            sinkEvents(Event.ONMOUSEMOVE | Event.ONMOUSEOUT |
13904                       Event.ONCLICK | Event.ONMOUSEOVER);
13905         }
13906         
13907         int getXAxisEnsembleHeight() {
13908            return xAxisEnsembleHeight;
13909         }
13910         double getXMax() {
13911            return xMax;
13912         }
13913         double getXMin() {
13914            return xMin;
13915         }
13916         int getY2AxisEnsembleWidth() {
13917            return y2AxisEnsembleWidth;
13918         }
13919         double getY2Max() {
13920            return y2Max;
13921         }
13922         double getY2Min() {
13923            return y2Min;
13924         }
13925         int getYAxisEnsembleWidth() {
13926            return yAxisEnsembleWidth;
13927         }
13928    
13929         int legendThickness() { return chartLegendThickness; }
13930         int chartFootnotesThickness() {return chartFootnotesThickness;}
13931         int chartTitleThickness() {return topMargin; }
13932         
13933         double getYMax() {
13934            return yMax;
13935         }
13936         double getYMin() {
13937            return yMin;
13938         }
13939    
13940         void reset(int xChartSize, int yChartSize,
13941                           boolean hasYAxis, boolean hasY2Axis,
13942                           Axis xAxis, Axis yAxis, Axis y2Axis) {
13943    
13944        // these must come first (getTickLabelThickness(false) needs them) 
13945            getXAxis().maybePopulateTicks();
13946            getYAxis().maybePopulateTicks();
13947            getY2Axis().maybePopulateTicks();
13948    
13949            this.xChartSize = xChartSize;
13950            this.yChartSize = yChartSize;
13951    
13952            Axis.AxisLimits axisLimits = xAxis.getAxisLimits(); 
13953            xMin = axisLimits.min;
13954            xMax = axisLimits.max;
13955            axisLimits = yAxis.getAxisLimits();
13956            yMin = axisLimits.min;
13957            yMax = axisLimits.max;
13958            axisLimits = y2Axis.getAxisLimits();
13959            y2Min = axisLimits.min;
13960            y2Max = axisLimits.max;
13961    
13962            topMargin = getChartTitleThickness(); 
13963            
13964            xAxisEnsembleHeight = xAxis.getAxisLabelThickness() + 
13965                                  xAxis.getTickLabelThickness(false) +
13966                                  xAxis.getTickSpace() +
13967                                  xAxis.getTickLabelPadding();
13968            yAxisEnsembleWidth = yAxis.getAxisLabelThickness() + 
13969                                 yAxis.getTickLabelThickness(false) +
13970                                 yAxis.getTickSpace() +
13971                                 yAxis.getTickLabelPadding();
13972            y2AxisEnsembleWidth = y2Axis.getAxisLabelThickness() + 
13973                                  y2Axis.getTickLabelThickness(false) +
13974                                  y2Axis.getTickSpace() +       
13975                                  y2Axis.getTickLabelPadding();
13976            
13977            chartLegendThickness = getLegendThickness();
13978            chartFootnotesThickness = getChartFootnotesThickness();
13979            
13980            setPixelSize(getXChartSizeDecoratedQuickly(),
13981                         getYChartSizeDecoratedQuickly());
13982    
13983            setWidgetPosition(graphicsPanel, yAxisEnsembleWidth, topMargin);
13984            setWidgetPosition(annotationPanel, yAxisEnsembleWidth, topMargin);
13985    
13986            // if there are any existing graphical rendering panels, bring
13987            // their clipping specs into agreement with the chartspecs
13988            for (int i = 0; i < getRenderingPanelCount(); i++) {
13989               GraphicsRenderingPanel grp = (GraphicsRenderingPanel) 
13990                       graphicsPanel.getWidget(i);
13991               if (DECORATIVE_RENDERING_PANEL_INDEX == i ||
13992                   isHoverFeedbackRenderingPanel(i) ||
13993                   !getClipToPlotArea()) {
13994                  grp.setPixelSize(0, 0);
13995                  GChart.setOverflow(grp, "visible");
13996               }
13997               else {
13998                  grp.setPixelSize(getXChartSize(), getYChartSize());
13999                  GChart.setOverflow(grp, "hidden");
14000               }
14001             }
14002         }
14003    
14004         double xToChartPixel(double x) {
14005            double result = Double.NaN;
14006            if (-Double.MAX_VALUE == x)
14007               result = yAxisEnsembleWidth;
14008            else if (Double.MAX_VALUE == x)
14009               result = yAxisEnsembleWidth+xChartSize-1.0;
14010            else if (!(x!=x)) { // x!=x is a faster isNaN
14011              result =             
14012              (yAxisEnsembleWidth * (xMax - x) +
14013              (yAxisEnsembleWidth+xChartSize-1.0) * (x - xMin))/
14014                      (xMax - xMin);
14015            }
14016            
14017            return result;
14018         }
14019    
14020         double xToPixel(double x) {
14021            double result = Double.NaN;
14022            if (-Double.MAX_VALUE == x)
14023               result = 0;
14024            else if (Double.MAX_VALUE == x)
14025               result = xChartSize-1.0;
14026            else if (!(x!=x)) { // x!=x is a faster isNaN
14027              result = (xChartSize-1.0) * (x - xMin)/(xMax - xMin);
14028            }
14029            return result;
14030         }
14031    
14032         
14033         double xChartPixelToX(int xPx) {
14034            double result = Double.NaN;
14035            if (GChart.NAI != xPx && xChartSize > 1) {
14036               result = xMin + (xMax - xMin) *
14037                       (xPx - yAxisEnsembleWidth)/(xChartSize-1.);
14038            } 
14039            return result;
14040         }
14041    
14042         double xPixelToX(int xPx) {
14043            double result = Double.NaN;
14044            if (GChart.NAI != xPx && xChartSize > 1) {
14045               result = xMin + (xMax - xMin) * xPx/(xChartSize-1.);
14046            } 
14047            return result;
14048         }
14049    
14050         
14051         double dxToPixel(double dx) {
14052    // xMax and xMin are at centers of their pixels, hence the -1
14053            double result = (dx * (xChartSize-1))/(xMax-xMin);
14054            return result;
14055         }
14056         
14057         double yToChartPixel(double y, boolean isY2) {
14058            double minY = isY2 ? y2Min : yMin;
14059            double maxY = isY2 ? y2Max : yMax;
14060            double result = Double.NaN;
14061            if (-Double.MAX_VALUE == y)
14062               result = yChartSize + topMargin - 1.0;
14063            else if (Double.MAX_VALUE == y)
14064               result = topMargin;
14065            else if (!(y!=y)) // x!=x is a faster isNaN
14066               result = (topMargin * (y - minY) +
14067                        ((yChartSize + topMargin - 1.0) *
14068                         (maxY - y)))/(maxY - minY);
14069            return result;
14070         }
14071    
14072         double yToPixel(double y, boolean isY2) {
14073            double minY = isY2 ? y2Min : yMin;
14074            double maxY = isY2 ? y2Max : yMax;
14075            double result = Double.NaN;
14076            if (-Double.MAX_VALUE == y)
14077               result = yChartSize - 1.0;
14078            else if (Double.MAX_VALUE == y)
14079               result = 0;
14080            else if (!(y!=y)) // x!=x is a faster isNaN
14081               result = (yChartSize - 1.0) * (maxY - y)/(maxY - minY);
14082            return result;
14083         }
14084         
14085         double yChartPixelToY(int yPx) {
14086            double result = Double.NaN;
14087            if (GChart.NAI != yPx && yChartSize > 1) {
14088              result = yMax + (yMin - yMax) *
14089                      (yPx - topMargin)/(yChartSize-1.);
14090            }
14091            return result;
14092         }
14093         double yPixelToY(int yPx) {
14094            double result = Double.NaN;
14095            if (GChart.NAI != yPx && yChartSize > 1) {
14096              result = yMax + (yMin - yMax) * yPx/(yChartSize-1.);
14097            }
14098            return result;
14099         }
14100         double yChartPixelToY2(int yPx) {
14101            double result = Double.NaN;
14102            if (GChart.NAI != yPx && yChartSize > 1) {
14103               result = y2Max + (y2Min - y2Max) *
14104                        (yPx - topMargin)/(yChartSize-1.);
14105            }
14106            return result;
14107         }
14108         double yPixelToY2(int yPx) {
14109            double result = Double.NaN;
14110            if (GChart.NAI != yPx && yChartSize > 1) {
14111               result = y2Max + (y2Min - y2Max) * yPx/(yChartSize-1.);
14112            }
14113            return result;
14114         }
14115         
14116         double dyToPixel(double dy, boolean isY2) {
14117            double minY = isY2 ? y2Min : yMin;
14118            double maxY = isY2 ? y2Max : yMax;
14119    // maxY and minY are at centers of their pixels, hence the -1
14120            double result = (dy * (yChartSize-1))/(maxY-minY);
14121            return result;
14122         }
14123    
14124    
14125         // keep track of last touched point & hover widget
14126         Curve.Point touchedPoint = null;
14127         HoverUpdateable touchedHoverWidget = null;
14128         // returns the inner aligned label of the opened hover annotation
14129         // (this is the one that directly holds the popup hover annotation)
14130         AlignedLabel getOpenedHoverContainer() {
14131            AlignedLabel result = null;
14132            Curve c = getSystemCurve(HOVER_ANNOTATION_ID);
14133            if (touchedPoint != null && c.isVisible()) {
14134               int internalIndex = getInternalCurveIndex(c);
14135               int rpIndex = getRenderingPanelIndex(internalIndex);
14136               AnnotationRenderingPanel arp =
14137                  getAnnotationRenderingPanel(rpIndex);
14138               result = arp.getFirstInnerAlignedLabel();
14139            }
14140            return result;
14141         }
14142    
14143         // the element associated with any opened hover container, else null
14144         Element getOpenedHoverElement() {
14145            AlignedLabel hoverContainer = getOpenedHoverContainer();
14146            Element result = (null == hoverContainer) ? null :
14147                             hoverContainer.getElement();
14148            return result;
14149         }
14150    
14151         // so if user calls update inside hoverUpdate, it won't recurse
14152         private boolean insideHoverUpdate = false;
14153        // so if user calls update inside hoverCleanup, it won't recurse
14154         private boolean insideHoverCleanup = false;
14155         /*
14156          * Configures GChart's special selection cursor and
14157          * hover annotation "system curves" so that they
14158          * provide appropriate feedback associated with "touching" the
14159          * given point with the mouse. When rendered, these curves
14160          * will:
14161          * <p>
14162          * 
14163          * <ol>
14164          * <li> Highlight the selected point in accord with specified hover
14165          * selection options
14166          * <p> 
14167          * <li> Display a point-specific hover annotation, in accord with
14168          * various hover annotation related options.
14169          * 
14170          * </ol>
14171          * 
14172          * <p>
14173          *
14174          * Also executes <tt>hoverCleanup</tt> on any hover widget
14175          * associated with the previously touched point, and
14176          * <tt>hoverUpdate</tt> on any hover widget associated with the
14177          * newly touched point, and maintains up-to-date references to the
14178          * last touched point and last touched hover widget.
14179          *
14180          */
14181         private void touch(Curve.Point p) {
14182            // Note: getTouchedPoint always returns NEW touched point
14183              Curve.Point prevTouchedPoint = touchedPoint;
14184              touchedPoint = p;
14185              Curve cAnnotation = getSystemCurve(HOVER_ANNOTATION_ID);
14186              Curve cCursor = getSystemCurve(HOVER_CURSOR_ID);
14187              Curve cTouched = (null == p) ? null : p.getParent();
14188              
14189              if (null != touchedHoverWidget) {
14190                  // free up resources allocated to previous hover widget
14191                  if (!insideHoverCleanup) {
14192                     try {
14193                        insideHoverCleanup = true;                    
14194                        touchedHoverWidget.hoverCleanup(prevTouchedPoint);
14195                     }
14196                     finally {insideHoverCleanup = false;}
14197                  }
14198                  
14199              }
14200    
14201              // with hoverCleanup out of the way, switch to new hover widget
14202              touchedHoverWidget = (null == cTouched) ? null :
14203                                   cTouched.getSymbol().getHoverWidget(); 
14204    
14205              if (null == touchedHoverWidget) {
14206                 if (null != p) {
14207                    // no hover-widget, just use expanded hover-template
14208                    String hovertext = p.getHovertext();
14209    
14210                    cAnnotation.getPoint(0).setAnnotationText(
14211                       hovertext,
14212                       cTouched.getSymbol().getHoverAnnotation().widthUpperBound,
14213                       cTouched.getSymbol().getHoverAnnotation().heightUpperBound);
14214                 }
14215              }
14216              else {
14217                 // touched curve has custom hover widget; update it, etc.
14218                 if (!insideHoverUpdate) {
14219                    try {
14220                       insideHoverUpdate = true;                    
14221                       touchedHoverWidget.hoverUpdate(p);
14222                    }
14223                    finally {insideHoverUpdate = false;}
14224                 }
14225                 cAnnotation.getPoint(0).setAnnotationWidget(
14226                    (Widget) touchedHoverWidget,
14227                    cTouched.getSymbol().getHoverAnnotation().widthUpperBound,
14228                    cTouched.getSymbol().getHoverAnnotation().heightUpperBound);
14229              }
14230              
14231              if (null == p) {    // no longer touching anything
14232                 cAnnotation.setVisible(false);
14233                 cCursor.setVisible(false);
14234              }
14235              else { // touching something, show that
14236    
14237                 if (!cTouched.getSymbol().getHoverAnnotationEnabled()) {
14238                    cAnnotation.setVisible(false);
14239                 }
14240                 else {
14241                   cAnnotation.setVisible(true);
14242                   cAnnotation.setYAxis(cTouched.getYAxis()); 
14243                   cAnnotation.getPoint(0).setX(p.getX());
14244                   cAnnotation.getPoint(0).setY(p.getY());
14245                   cAnnotation.getSymbol().copy(cTouched.getSymbol());
14246                   // the symbol isn't needed, so make it transparent
14247                   // and zap any images (can't make it 0-sized since
14248                   // annotation placement is size-dependent)
14249                   cAnnotation.getSymbol().setImageURL(
14250                      GChart.DEFAULT_BLANK_IMAGE_URL_FULLPATH);
14251                   cAnnotation.getSymbol().setBackgroundColor("transparent");
14252                   cAnnotation.getSymbol().setBorderColor(TRANSPARENT_BORDER_COLOR);
14253                   if (null !=
14254                       cTouched.getSymbol().getHoverAnnotationSymbolType())
14255                      cAnnotation.getSymbol().setSymbolType(
14256                        cTouched.getSymbol().getHoverAnnotationSymbolType());
14257                   // else just stick with the touched symbol's type
14258                   
14259                   // copy the hover annotations specs (including
14260                   // hover widget ref or HTML defined above)
14261                   // to the annotation curve's point
14262                   cAnnotation.getPoint(0).setAnnotationFontColor(
14263                      cTouched.getSymbol().getHoverFontColor());
14264                   cAnnotation.getPoint(0).setAnnotationFontSize(
14265                      cTouched.getSymbol().getHoverFontSize());
14266                   cAnnotation.getPoint(0).setAnnotationFontStyle(
14267                      cTouched.getSymbol().getHoverFontStyle());
14268                   cAnnotation.getPoint(0).setAnnotationFontWeight(
14269                      cTouched.getSymbol().getHoverFontWeight());
14270                   cAnnotation.getPoint(0).setAnnotationLocation(
14271                      cTouched.getSymbol().getHoverLocation());
14272                   cAnnotation.getPoint(0).setAnnotationXShift(
14273                      cTouched.getSymbol().getHoverXShift());
14274                   cAnnotation.getPoint(0).setAnnotationYShift(
14275                      cTouched.getSymbol().getHoverYShift());
14276                 }
14277                 
14278                 if (!cTouched.getSymbol().getHoverSelectionEnabled()) {
14279                    cCursor.setVisible(false);
14280                 }
14281                 else {
14282                   cCursor.setVisible(true);
14283                   cCursor.setYAxis(cTouched.getYAxis()); 
14284                 // place cursor curve's point where touched point is:
14285                   cCursor.getPoint(0).setX(p.getX());
14286                   cCursor.getPoint(0).setY(p.getY());
14287                 // cursor gets (mostly) same props as touched symbol   
14288                   cCursor.getSymbol().copy(cTouched.getSymbol());
14289                   if (null != cTouched.getSymbol().getHoverSelectionSymbolType())
14290                     cCursor.getSymbol().setSymbolType(
14291                        cTouched.getSymbol().getHoverSelectionSymbolType());
14292                   double fillSpacing =
14293                      cTouched.getSymbol().getHoverSelectionFillSpacing();    
14294                   if (!(fillSpacing != fillSpacing))
14295                     cCursor.getSymbol().setFillSpacing(fillSpacing);
14296                   int fillThickness =
14297                     cTouched.getSymbol().getHoverSelectionFillThickness();
14298                   if (GChart.NAI != fillThickness)
14299                     cCursor.getSymbol().setFillThickness(fillThickness);
14300                   if (GChart.NAI != cTouched.getSymbol().getHoverSelectionHeight())
14301                     cCursor.getSymbol().setHeight(
14302                        cTouched.getSymbol().getHoverSelectionHeight());
14303                   if (GChart.NAI != cTouched.getSymbol().getHoverSelectionWidth())
14304                     cCursor.getSymbol().setWidth(
14305                        cTouched.getSymbol().getHoverSelectionWidth());
14306                   cCursor.getSymbol().setImageURL(cTouched.getSymbol().getHoverSelectionImageURL());
14307                   cCursor.getSymbol().setBackgroundColor(
14308                      cTouched.getSymbol().getHoverSelectionBackgroundColor());
14309                   cCursor.getSymbol().setBorderColor(
14310                      cTouched.getSymbol().getHoverSelectionBorderColor());
14311                   cCursor.getSymbol().setBorderStyle(
14312                      cTouched.getSymbol().getHoverSelectionBorderStyle());
14313                   int borderWidth =
14314                         cTouched.getSymbol().getHoverSelectionBorderWidth();
14315                   cCursor.getSymbol().setBorderWidth(borderWidth);
14316                 }
14317               }
14318           }
14319    
14320           /*
14321            * Is the given target element contained within the given container?
14322            * (tried isOrHasChild but was getting exceptions in FF2 I could
14323            *  not track to their source, so I just stuck with this)
14324            */  
14325         private boolean isContainedIn(Element container, EventTarget et) {
14326            Element part =
14327              (null == et || !Element.is(et)) ? null : Element.as(et);
14328            /*
14329             * In Chrome and FF2, the next line makes dropdown lists inside
14330             * hover widgets work more correctly when they click on the
14331             * dropdown part of the list. Otherwise, hover widget can close
14332             * inappropriately. As tested in GChartExample20a.java
14333             * 
14334             */ 
14335             if (null == part) return true;
14336             try {
14337               for (Element ancestor = part;
14338                    ancestor != null && container != null;
14339                    ancestor = ancestor.getParentElement()) 
14340                  if (container == ancestor) return true;
14341             }
14342             catch (Exception e) {
14343    /*
14344     * In FF2, we get the error "Error: uncaught exception: Permission
14345     * denied to get property HTMLDivElement.parentNode" if a TextBox is
14346     * placed into the chart x axis label and you mouse over that textbox
14347     * (as reported by secnoc in issue #24, which has additional useful info
14348     * about the likely cause of this problem; TestGChart44.java reproduces
14349     * this behavior if this try/catch is removed).
14350     *
14351     * Returning true, which will act as if the element is contained in the
14352     * GChart, has "cruft possibilities" as in some cases a hoverwidget may
14353     * not get closed when user mouses completely out of the GChart, onto a
14354     * TextBox, but should otherwise be a lesser evil than false or an
14355     * uncaught exception.
14356     * 
14357     */
14358               return true;
14359    
14360             }
14361             return false;
14362           }
14363    
14364              
14365         /*
14366          * Is given mouse client point geometrically "inside" container?
14367          * <p>
14368          * 
14369          * Note: Certain widgets, like drop-down lists, SuggestBox, etc.
14370          * implicitly create popups that are not children of the chart, and
14371          * thus when the user moves into these popups, a mouseout that
14372          * looks like they are leaving the hover widget or chart, and
14373          * thus inappropriately hides it can often make hover widgets
14374          * involving such elements unusable.
14375          * 
14376          * <p>
14377          *
14378          * But, by adding a geometric condition to define "being out of
14379          * the chart or hover widget", hover widgets that use such popups
14380          * can avoid such inappropriate hiding as long as the popups remain
14381          * geometrically within either the chart as a whole, or the hover
14382          * widget itself.  For example a form with drop-down lists is OK
14383          * as long as the drop down lists don't extrude off the form
14384          * when they drop down.<p>
14385          *
14386          * Unfortunately, this creates another problem, in that hover
14387          * widgets that extrude off the chart can remain open when the user
14388          * mouses off the chart in such a way that the mouseout occurs at a
14389          * point geometrically within the hover widget. We consider this
14390          * small amount of "hover cruft" a lesser evil than not being able
14391          * to use dropdown lists and such within hover widgets at all.
14392          * <p>
14393          *
14394          * Often a more natural solution, rather than wrestling with such
14395          * "geometrical popup containment", is a click-invoked modal
14396          * dialog. But that has a distinctly different feel than the more
14397          * nearly modeless hover widget, so I thought there was room/need
14398          * for both approaches.
14399          * 
14400          */ 
14401           private boolean isGeometricallyContainedIn(Element container,
14402                                                      int clientX, int clientY) {
14403              
14404              if (null == container)
14405                 throw new IllegalArgumentException(
14406                     "Container cannot be null");
14407              boolean result = false; 
14408    
14409    
14410              /*
14411               * Equations below shrink container 1px around its perimeter,
14412               * to account for apparent roundoff errors in FF2 associated
14413               * with window scrolling. FF2 mouseout events get dropped
14414               * (about half the time, with random scroll position choices
14415               * hence my roundoff suspicions--problem did not occur in
14416               * Chrome or IE7) without this 1px shrinkage. End users can't
14417               * discriminate a 1px mouse shift anyway, so there is no
14418               * real downside (except that you had to read this comment)
14419               * to using this workaround.
14420               * 
14421               */
14422              int y = Window.getScrollTop() + repairBadClientY(clientY);
14423              int absTop = container.getAbsoluteTop();
14424              if (absTop < y &&
14425                  y+1 < absTop + container.getOffsetHeight()) {
14426                 int x = Window.getScrollLeft() + repairBadClientX(clientX);
14427                 int absLeft = container.getAbsoluteLeft();
14428                 if (absLeft < x && 
14429                     x+1 < absLeft + container.getOffsetWidth()) 
14430                    result = true;
14431              }
14432              
14433              return result;
14434           }
14435    
14436           // Touches the underlying object at the last event's mouse
14437           // position if it is different from the currently touched point,
14438           // or if retouch is true. Returns true if a touch occured.
14439           private boolean touchObjectAtMousePosition(boolean retouch) {
14440              boolean result = false;
14441              Curve.Point pointAtPosition = getClosestBrushTouchingPointNoCheck(
14442                 getXMousePlotArea(), getYMousePlotArea());
14443              if ((pointAtPosition != touchedPoint) || retouch) {
14444                 touch(pointAtPosition);
14445                 result = true;
14446              }
14447              return result;
14448           }
14449           // touch object at mouse, but only if it is a different one
14450           boolean touchObjectAtMousePosition() {
14451              boolean result = touchObjectAtMousePosition(false);
14452              return result;
14453           }
14454           // touch object at mouse, even if it is the same one as last time
14455           void retouchObjectAtMousePosition() {
14456              touchObjectAtMousePosition(true);
14457           }
14458    
14459           /*
14460            * Does the event occur over the currently opened hover
14461            * annotation?
14462            *
14463            * This method helps us to ignore mouse clicks and moves over a
14464            * "sticky-open" hover annotation, or one of its children. Idea
14465            * is to prevent the hover feedback from jumping to another
14466            * point while the user interacts with an opened hover widget.
14467            * <p>
14468            *
14469            * This is not just important for hover annotations that contain
14470            * buttons and such, but even for hover annotations based on
14471            * static text, since the user might want to select/copy that
14472            * text (involving mouse moves) for example.
14473            *
14474            *
14475            */ 
14476           private boolean isOverOpenedHoverAnnotation(Event event) {
14477              boolean result = false;
14478              Element hoverElement = getOpenedHoverElement();
14479              if (null != hoverElement) {
14480                 if (isContainedIn(hoverElement, event.getEventTarget()))
14481                     result = true;
14482                 else if (isGeometricallyContainedIn(hoverElement,
14483                                                    event.getClientX(),
14484                                                    event.getClientY()))
14485                    result = true;
14486              }
14487              return result;
14488           }
14489    
14490           /*
14491            * Does event (assumed a MOUSEOUT) take the mouse completely
14492            * outside of the current chart?
14493            * <p>
14494            * 
14495            * To be completely outside the chart, the event must be both not
14496            * associated with any child element of the chart (as represented
14497            * in the DOM) and geometrically outside of the chart's "box" and
14498            * the "box" of any currently opened hover annotation.  <p>
14499            *
14500            * Because a GChart is rendered with many DOM elements, moving
14501            * the mouse across it generates many MOUSEOUT events. This
14502            * method lets us focus on only those that take us completely off
14503            * the chart, and thus require the hover feedback to be turned
14504            * off.
14505            * <p>
14506            *
14507            *
14508            * 
14509            */ 
14510           private boolean takesUsCompletelyOutsideChart(Event event) {
14511              boolean result = true;
14512                                    
14513              if (isContainedIn(getElement(),
14514                                event.getRelatedEventTarget()))
14515    /* hoverElement is always a descendant of the main chart element due to
14516     * how GChart generates it, so if this branch isn't reached, toElement
14517     * is not contained in either chart or the opened hover annotation */
14518                 result = false;
14519              else if (isGeometricallyContainedIn(getElement(),
14520                                               event.getClientX(),
14521                                               event.getClientY()))
14522                 result = false;
14523              else {
14524                 Element hoverElement = getOpenedHoverElement();
14525                 if (null != hoverElement) {
14526                    if (isGeometricallyContainedIn(hoverElement,
14527                                                   event.getClientX(),
14528                                                   event.getClientY()))
14529                       result = false;
14530                 }
14531              }
14532    
14533              return result;
14534    
14535           }
14536    
14537           
14538           /**
14539            * Fired whenever a browser event is recieved.
14540            * <p>
14541            * 
14542            * GChart keeps track of browser mouse-moves, mouse-outs,
14543            * mouse-overs, and mouse clicks and will automatically provide
14544            * appropriate hover feedback whenever the mouse "touches"
14545            * rendered symbols on the chart. It also maintains a reference
14546            * to the "currently touched" point which you can retrieve via
14547            * the <tt>getTouchedPoint</tt> method.  <p>
14548            *
14549            * GChart never "eats" mouse events (it just watches them go by,
14550            * and keeps track of what the mouse-anchored brush is touching)
14551            * so containing Widgets can track and respond to the same mouse
14552            * event stream after GChart does, if they want to.
14553            *
14554            * <p>
14555            *
14556            * Each curve's symbol can configure how hover feedback is
14557            * displayed via the <tt>setHover*</tt> family of methods and the
14558            * related HoverUpdateable and HoverParameterInterpreter
14559            * interfaces. In addition, GChart implements the standard
14560            * GWT <tt>SourcesClickEvents</tt> interface, so you
14561            * can easily implement <tt>ClickListener.onClick</tt>
14562            * to be notified of user clicks on a GChart, using
14563            * <tt>getTouchedPoint</tt> to grab the clicked-on point.
14564            * <p>
14565            *
14566            * This method can only properly track events when
14567            * <tt>isUpdateNeeded</tt> returns false (implies DOM/GChart
14568            * specs are in synch) so if you want this tracking system to
14569            * work as intended, you need to be sure to always call
14570            * <tt>update</tt> after making a series of chart specification
14571            * changes, just before you give control back to the browser.
14572            *
14573            *
14574            * @param event the browser event that GChart will monitor
14575            * so as to maintain a reference to the "touched" point and
14576            * provide appropriate hover feedback.
14577            *  
14578            * @see #touch touch
14579            * @see #getTouchedPoint getTouchedPoint
14580            * @see #setHoverWidget setHoverWidget
14581            * @see #setHovertextTemplate setHovertextTemplate
14582            * @see #setHoverAnnotationEnabled setHoverAnnotationEnabled
14583            * @see #setHoverSelectionEnabled setHoverSelectionEnabled
14584            * @see HoverUpdateable HoverUpdateable
14585            * @see #setHoverParameterInterpreter setHoverParameterInterpreter
14586            * @see HoverParameterInterpreter HoverParameterInterpreter
14587            * @see #isUpdateNeeded isUpdateNeeded
14588            * @see #update update
14589            * 
14590            */
14591           public void onBrowserEvent(Event event) {
14592              // GWT docs say without this, 1.6+ event handlers won't work
14593              super.onBrowserEvent(event);
14594              
14595              /*
14596               * The tracking of the mouse position depends on if there are
14597               * opened hover annotations or not (mouse moves over such
14598               * annotations don't get tracked, and thus don't change the
14599               * "touched" point). However, all of that dependency can be
14600               * determined by the current DOM rendering of the chart--there
14601               * is no need to look at actual chart specs.  <p>
14602               *
14603               * So, when the DOM/chart specs are inconsistent (chart "needs
14604               * update") we continue to perform mouse tracking based on the
14605               * <i>last completed DOM rendering</i> of the chart (that is,
14606               * the last <tt>assembleChart</tt> call).  However, actual
14607               * changes to the chart are "frozen" (as assured by the
14608               * <tt>!isUpdateNeeded</tt> test below) so no DOM changes
14609               * occur automatically in response to mouse moves over things
14610               * (e.g. no changes to hover feedback occur--that's frozen,
14611               * too). In short, we track, but do not act.
14612               * 
14613               * <p>
14614               * 
14615               * We can think of it this way: mouse tracking remains
14616               * consistent with the <i>last DOM rendered</i> specification
14617               * and then it is as if all of the accumulated specification
14618               * changes are applied to the DOM at that point in time when
14619               * the next (developer invoked) update occurs. That means
14620               * there is exactly one point in time of "unpredictable
14621               * change" (points previously hovered over can disappear from
14622               * under the mouse since they have been deleted or moved,
14623               * etc.). But that discontinuity can be adequately managed by
14624               * the developer via the <tt>TouchedPointUpdateOption</tt>
14625               * argument to update. 
14626               * <p>
14627               *
14628               * Well, that's the theory. But GChart's mouse tracking has
14629               * only been tested for the case where <tt>update</tt> is
14630               * always called just before the developer ceeds control back
14631               * to the browser after making a series of chart spec changes.
14632               * So, the public docs warn developers to be sure that they do
14633               * that, too. But the hope is that specialized applications
14634               * where they don't call update until the user explicitly asks
14635               * for that (say, for a very busy chart with an editing
14636               * capability and a "refresh" button) will also work OK.
14637               * 
14638               * <p>
14639               *
14640               * Another important consequence of this "track but don't act"
14641               * approach is that it assures that only cheap/quick
14642               * operations can be triggered automatically by direct user
14643               * mousing. Potentially expensive "full chart" updates always
14644               * require a direct developer update invocation. So, if the
14645               * system "locks up while it's doing a lengthly update" there
14646               * will always be an actual developer line of code responsible
14647               * for that, not some mysteriously event-triggered call.
14648               *
14649               *
14650               */
14651              
14652              int eventId = DOM.eventGetType(event);
14653              /* Note that a click that closes a modal DialogBox can
14654               * generate a mouse location change without an ONMOUSEMOVE,
14655               * and a point that moves under the mouse due to an update
14656               * can generate a mouseover without a MOUSEMOVE */
14657              boolean isClick = (Event.ONCLICK == eventId);
14658              if ((Event.ONMOUSEMOVE == eventId ||
14659                   Event.ONMOUSEOVER == eventId || isClick) &&
14660                  !isOverOpenedHoverAnnotation(event)) {
14661                 // remember last "tracked" mouse location
14662    //             if (Event.ONCLICK == eventId)
14663    //                Window.alert("CLICK: event.getClientX()=" + event.getClientX() +
14664    //                             " event.getClientY()=" + event.getClientY() +
14665    //                             " event.getTarget()==this.getElement() is " +
14666    //                             (event.getTarget() == this.getElement()) +
14667    //                             " event.getCurrentTarget()="+event.getCurrentTarget() +
14668    //                             " event.getTarget()=" + event.getTarget());
14669    //             else if (Event.ONMOUSEOVER == eventId)
14670    //                Window.alert("MOUSEOVER: event.getClientX()=" + event.getClientX() +
14671    //                             " event.getClientY()=" + event.getClientY() +
14672    //                             " event.getCurrentTarget()="+event.getCurrentTarget() +
14673    //                             " event.getTarget()=" + event.getTarget());
14674                 if (getHoverTouchingEnabled() || isClick) {
14675                    setClientX(event.getClientX(), isClick); 
14676                    setClientY(event.getClientY(), isClick); 
14677                    if (!isUpdateNeeded() &&
14678                        touchObjectAtMousePosition(isClick))
14679                       assembleChart();
14680                 }
14681              }
14682              else if (Event.ONMOUSEOUT == eventId &&
14683                       getHoverTouchingEnabled() &&
14684                       takesUsCompletelyOutsideChart(event)) {
14685    //                Window.alert("MOUSEOUT: event.getClientX()=" + event.getClientX() +
14686    //                             " event.getClientY()=" + event.getClientY() +
14687    //                             " event.getCurrentTarget()="+event.getCurrentTarget() +
14688    //                             " event.getTarget()=" + event.getTarget());
14689                 setClientX(GChart.NAI, false); // mouse not over chart,
14690                 setClientY(GChart.NAI, false); // so position is undefined
14691                 if (!isUpdateNeeded()  && touchObjectAtMousePosition())
14692                    assembleChart();
14693              }
14694              
14695           }
14696    
14697           
14698         
14699         // Is chart's DOM rendering consistent with its specs? 
14700         boolean isValidated() {
14701            boolean result = true;
14702            for (int i = 0; result && i < curves.size(); i++)
14703               result = curves.get(i).isValidated();
14704            return result;
14705         }
14706    
14707         /*
14708          * Returns number of "rendering panels" that there actually
14709          * are right now.
14710          *
14711          * GChart's implementation assures that this number is exactly the
14712          * same for the graphics and annotation rendering panels. 
14713          * 
14714          */  
14715         int getRenderingPanelCount() {
14716            int result = graphicsPanel.getWidgetCount();
14717            return result;
14718         }
14719        int getXChartSize() {return xChartSize;} 
14720        int getYChartSize() {return yChartSize;} 
14721        // quickly returns decorated xChartsize as of the last plotPanel.reset
14722        int getXChartSizeDecoratedQuickly() {
14723           int result = xChartSize +
14724                        yAxisEnsembleWidth +
14725                        y2AxisEnsembleWidth +
14726                        chartLegendThickness;
14727           return result;
14728        }
14729        
14730        // quickly returns decorated yChartsize as of the last plotPanel.reset
14731        int getYChartSizeDecoratedQuickly() {
14732           int result = yChartSize +
14733                        xAxisEnsembleHeight +
14734                        topMargin +
14735                        chartFootnotesThickness;
14736           return result;
14737        }
14738    
14739    
14740       } // end of class PlotPanel
14741       
14742       
14743       // axis types (used to define which y-axis each curve is on)
14744       private static class YAxisId {}
14745      
14746       // case-independent index of next "break" tag in string (case of HTML
14747       // returned from HasHTML.getHTML can change with browser)
14748       private static int indexOfBr(String s, int iStart) {
14749         final String BR1 = "<br>";
14750         final String BR2 = "<BR>";
14751         final String BR3 = "<li>";  // recognize <li> as a break.
14752         final String BR4 = "<LI>";
14753         final String BR5 = "<tr>";  // recognize <tr> as a break.
14754         final String BR6 = "<TR>";
14755         int iBr1 = s.indexOf(BR1, iStart);
14756         int iBr2 = s.indexOf(BR2, iStart);
14757         int iBr3 = s.indexOf(BR3, iStart);
14758         int iBr4 = s.indexOf(BR4, iStart);
14759         int iBr5 = s.indexOf(BR5, iStart);
14760         int iBr6 = s.indexOf(BR6, iStart);
14761         int result1 = 0;
14762         int result2 = 0;
14763         int result3 = 0;
14764         int result = 0;
14765    
14766         if (-1 == iBr1)
14767            result1 = iBr2;
14768         else if (-1 == iBr2)
14769            result1 = iBr1;
14770         else
14771            result1 = Math.min(iBr1, iBr2);
14772    
14773         if (-1 == iBr3)
14774            result2 = iBr4;
14775         else if (-1 == iBr4)
14776            result2 = iBr3;
14777         else
14778            result2 = Math.min(iBr3, iBr4);
14779    
14780         if (-1 == iBr5)
14781            result3 = iBr6;
14782         else if (-1 == iBr6)
14783            result3 = iBr5;
14784         else
14785            result3 = Math.min(iBr5, iBr6);
14786    
14787    
14788         
14789         if (-1 == result1)
14790            result = result2;
14791         else if (-1 == result2)
14792            result = result1;
14793         else
14794            result = Math.min(result1, result2);
14795    
14796    
14797         if (-1 == result)
14798            result = result3;
14799         else if (-1 != result3)
14800            result = Math.min(result, result3);
14801    
14802         
14803         return result;
14804         
14805       }
14806       private static int indexOfBr(String s) {
14807          return indexOfBr(s, 0);
14808       }
14809       
14810    // Provides a character-based width estimate when simple tags
14811    // such as <b> and <i> are present in a multi-line,
14812    // "break"-delimited, string. Very approximate, but a useful
14813    // default.
14814    private static int htmlWidth(String sIn) {
14815       int iBr = indexOfBr(sIn);
14816       String s = (-1 == iBr) ? sIn : sIn.substring(0, iBr);
14817       final String LITERAL_PAT = "[&][#a-zA-Z]+[;]";
14818       s = s.replaceAll(LITERAL_PAT, "X"); // literals count as 1 char
14819       final String TAG_PAT = "[<][^>]+[>]";
14820       s = s.replaceAll(TAG_PAT, "");   // tags don't count at all
14821       return s.length();
14822    }
14823    
14824    // number of <br> delimited lines in an HTML string
14825    private static int htmlHeight(String s) {
14826       final int BR_LEN = "<br>".length();
14827       int iBr = 0;
14828       int result = 1;
14829       if (null != s) {
14830         for (iBr = indexOfBr(s);
14831              iBr != -1;
14832              iBr = indexOfBr(s, iBr+BR_LEN))
14833            result++;
14834       }
14835       return result;
14836    
14837    }
14838    
14839      /*
14840       * Annotates (labels) a chart symbol. Users access this class via
14841       * wrapper methods of the Point class, and via various tick-label
14842       * related methods of the Axis class.
14843       *
14844       */ 
14845      
14846       static class Annotation {
14847         String fontColor = DEFAULT_FONT_COLOR;
14848         int fontSize = DEFAULT_ANNOTATION_FONTSIZE;
14849         String fontStyle = "normal";
14850         String fontWeight = "normal"; 
14851         AnnotationLocation location = null;
14852         String text = null;
14853         Widget widget = null;    // may be used in lieu of text or HTML
14854         boolean isVisible = true;
14855         int xShift = 0;
14856         int yShift = 0;
14857         boolean isHTML = false; // no break tags ==> plain text
14858         // Estimated number of lines, width in chars, of annotation
14859         // text (not used by Widgets)
14860         int numberOfLinesHigh = 0;
14861         int numberOfCharsWide = 0;
14862         int widthUpperBound = GChart.NAI;
14863         int heightUpperBound = GChart.NAI;
14864         static final int HTML_LEN = "<html>".length();
14865         static final int BR_LEN = "<br>".length();
14866    
14867         /*
14868          * Computes parameters used to estimate the width and height
14869          * of the (invisible) enclosing 1x1 Grid of an annotation
14870          * (used to align, center, etc. the annotation) <p>
14871          *
14872          */ 
14873         private String analyzeHTML(String s) {
14874           String result = null;    
14875           if (null == s) {
14876              isHTML = false;
14877              numberOfLinesHigh = 0;
14878              numberOfCharsWide = 0;
14879           }
14880           else if (!s.startsWith("<html>")) { // no html==>plain text
14881              isHTML = false;
14882              numberOfLinesHigh = 1;
14883              numberOfCharsWide = s.length();
14884              result = s;
14885           }
14886           else {  // HTML
14887              isHTML = true;
14888              // <html> is just a flag, not a tag, so strip it out.
14889              result = s.substring(HTML_LEN);
14890              if (widthUpperBound == GChart.NAI)
14891                 numberOfCharsWide = htmlWidth(result);
14892                            
14893              if (heightUpperBound == GChart.NAI) 
14894                 numberOfLinesHigh = htmlHeight(result);
14895                 
14896           }
14897           return result;
14898           
14899         }
14900    
14901         // Returns number of chars in first <br>-delimited line of
14902         // given string. A very crude way to estimate (especially
14903         // HTML) width in characters, but user can give explicit
14904         // widths when the width estimates based on this char width
14905         // heuristic fail them.
14906         static int getNumberOfCharsWide(String s) {
14907            int result = 0;
14908            if (!s.startsWith("<html>")) {
14909               result = s.length();
14910            }
14911            else {
14912                result = htmlWidth(s); 
14913            }
14914            return result;
14915         }     
14916         public String getFontColor() {
14917           return fontColor;
14918         }
14919         public int getFontSize() {
14920            return fontSize;
14921         }
14922         
14923         public AnnotationLocation getLocation() {
14924            return location;
14925         }
14926         
14927         boolean isHTML() {return isHTML;}
14928    
14929         public String getText() {
14930            return (isHTML? ("<html>" + text) : text);
14931         }
14932    
14933         public boolean getVisible() {
14934           return isVisible;
14935         }
14936         public int getXShift() {
14937           return xShift;
14938         }
14939         public int getYShift() {
14940           return yShift;
14941         }
14942         
14943         public void setFontColor(String cssColor) {
14944            this.fontColor = cssColor;
14945         }
14946         public void setFontSize(int fontSize) {
14947            this.fontSize = fontSize;
14948         }
14949         public void setFontWeight(String cssWeight) {
14950            this.fontWeight = cssWeight;
14951         }
14952         public void setFontStyle(String cssStyle) {
14953            this.fontStyle = cssStyle;
14954         }
14955    
14956         String getFontWeight() { return fontWeight; }
14957         String getFontStyle() { return fontStyle; }
14958    
14959         public void setLocation(AnnotationLocation location) {
14960            this.location = location;
14961         }
14962    
14963         public void setText(String text, int widthUpperBound,
14964                             int heightUpperBound) {
14965            this.widthUpperBound = widthUpperBound;
14966            this.heightUpperBound = heightUpperBound;
14967            this.text = analyzeHTML(text);
14968            this.widget = null;
14969         }
14970         public void setText(String text) {
14971            setText(text, GChart.NAI, GChart.NAI);
14972         }
14973    
14974         public void setVisible(boolean isVisible) {
14975            this.isVisible = isVisible;
14976         }
14977    
14978         public void setWidget(Widget widget, int widthUpperBound,
14979                             int heightUpperBound) {
14980            this.widthUpperBound = widthUpperBound;
14981            this.heightUpperBound = heightUpperBound;
14982            this.text = null;
14983            this.widget = widget;
14984         }
14985         public void setWidget(Widget widget) {
14986           setWidget(widget, DEFAULT_WIDGET_WIDTH_UPPERBOUND,
14987                     DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
14988       }
14989         public Widget getWidget() {return widget;}
14990         
14991         public void setXShift(int xShift) {
14992            this.xShift = xShift;
14993         }
14994         public void setYShift(int yShift) {
14995            this.yShift = yShift;
14996         }
14997         
14998         int getHeightUpperBound() {
14999            int result = 0;
15000            if (heightUpperBound != GChart.NAI)
15001               result = heightUpperBound;
15002            else {
15003               result = (int) Math.ceil(fontSize *
15004                                        numberOfLinesHigh *
15005                                        CHARHEIGHT_TO_FONTSIZE_UPPERBOUND);
15006            }
15007            return result;
15008         }
15009    
15010         int getWidthUpperBound() {
15011            int result = 0;
15012            if (widthUpperBound != GChart.NAI)
15013               result = widthUpperBound;
15014            else {
15015               result = (int) Math.ceil(fontSize *
15016                        numberOfCharsWide * CHARWIDTH_TO_FONTSIZE_UPPERBOUND);
15017            }
15018            return result;
15019         }
15020         
15021      } // end of class Annotation
15022    
15023       /** Default size, in pixels, of text used to annotate individual
15024        ** plotted points on a curve.
15025        **
15026        ** @see Curve.Point#setFontSize Point.setFontSize
15027         */
15028       public static final int DEFAULT_ANNOTATION_FONTSIZE = 12;
15029    
15030       /**
15031        * Default pixel height of rectangular "brush" that defines
15032        * how close the mouse cursor must be to a rendered symbol for
15033        * it to be "touched" (which pops up its hover feedback).
15034        *
15035        * @see Symbol#setBrushHeight setBrushHeight
15036        * @see Symbol#setBrushWidth setBrushWidth
15037        * @see #DEFAULT_BRUSH_WIDTH DEFAULT_BRUSH_WIDTH
15038        */ 
15039       public static final int DEFAULT_BRUSH_HEIGHT = 1;
15040       /**
15041        *
15042        * Default pixel width of rectangular "brush" that defines how
15043        * close the mouse cursor must be to a rendered symbol for it
15044        * to be "touched" (which pops up its hover feedback).
15045        * 
15046        * @see Symbol#setBrushHeight setBrushHeight
15047        * @see Symbol#setBrushWidth setBrushWidth
15048        * @see #DEFAULT_BRUSH_HEIGHT DEFAULT_BRUSH_HEIGHT
15049        */ 
15050       public static final int DEFAULT_BRUSH_WIDTH = 1;
15051       
15052       /** Default color of border around the chart legend
15053        **
15054        ** @see #setLegendBorderColor setLegendBorderColor
15055        **
15056        **/
15057       public static final String DEFAULT_LEGEND_BORDER_COLOR = "black";
15058       /** Default width of border around the chart legend
15059        **
15060        ** @see #setLegendBorderWidth setLegendBorderWidth
15061        **
15062        **/
15063       public static final int DEFAULT_LEGEND_BORDER_WIDTH = 1;
15064       /** Default style of border around the chart legend
15065        **
15066        ** @see #setLegendBorderStyle setLegendBorderStyle
15067        **
15068        **/
15069       public static final String DEFAULT_LEGEND_BORDER_STYLE = "solid";
15070    
15071       /** Default color of background of the chart legend
15072        **
15073        ** @see #setLegendBackgroundColor setLegendBackgroundColor
15074        ** 
15075         */
15076       public static final String DEFAULT_LEGEND_BACKGROUND_COLOR = "transparent";
15077      /**
15078       ** The default color of any text appearing in a chart's
15079       ** legend, annotations, or tick labels.
15080       **
15081       ** @see #setLegendFontColor setLegendFontColor
15082       ** @see Axis#setTickLabelFontColor setTickLabelFontColor
15083       ** @see Curve.Point#setAnnotationFontColor setAnnotationFontColor
15084       ** 
15085       **/ 
15086       public final static String DEFAULT_FONT_COLOR ="black";
15087       /**
15088        ** Default style of axis label and legend fonts.
15089        ** 
15090        ** @see #setLegendFontStyle setLegendFontStyle
15091        ** @see Axis#setTickLabelFontStyle setTickLabelFontStyle
15092        ** @see Curve.Point#setAnnotationFontStyle
15093        ** setAnnotationFontStyle
15094        ** 
15095        **/
15096       public static final String DEFAULT_FONT_STYLE = "normal";
15097       /** Default weight of axis label and legend fonts.
15098        ** 
15099        ** @see #setLegendFontWeight setLegendFontWeight
15100        ** @see Axis#setTickLabelFontWeight setTickLabelFontWeight
15101        ** @see Curve.Point#setAnnotationFontWeight
15102        ** setAnnotationFontWeight
15103        ** 
15104        **/
15105       
15106       public static final String DEFAULT_FONT_WEIGHT = "normal";
15107    
15108     /**
15109        ** The default template string used to generate the hovertext
15110        ** displayed when the user hovers their mouse above a point
15111        ** on a curve (pie slices have a different default).
15112        ** 
15113        ** @see Symbol#setHovertextTemplate setHovertextTemplate
15114        ** @see #DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
15115        **    DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
15116        **    
15117        */
15118       public static final String DEFAULT_HOVERTEXT_TEMPLATE =
15119          GChart.formatAsHovertext("(${x}, ${y})");
15120       /**
15121        ** The default hover feedback location used to position the 
15122        ** hover feedback when the user hovers their mouse above a point
15123        ** on a curve (pie slices, and bar symbols have different
15124        ** defaults).
15125        ** 
15126        ** @see Symbol#setHoverLocation setHoverLocation
15127        ** @see #DEFAULT_PIE_SLICE_HOVER_LOCATION DEFAULT_PIE_SLICE_HOVER_LOCATION
15128        ** @see #DEFAULT_VBAR_BASELINE_HOVER_LOCATION DEFAULT_VBAR_BASELINE_HOVER_LOCATION
15129        ** @see #DEFAULT_VBARBOTTOM_HOVER_LOCATION DEFAULT_VBARBOTTOM_HOVER_LOCATION
15130        ** @see #DEFAULT_VBARTOP_HOVER_LOCATION DEFAULT_VBARTOP_HOVER_LOCATION
15131        ** @see #DEFAULT_HBAR_BASELINE_HOVER_LOCATION DEFAULT_HBAR_BASELINE_HOVER_LOCATION
15132        ** @see #DEFAULT_HBARLEFT_HOVER_LOCATION DEFAULT_HBARLEFT_HOVER_LOCATION
15133        ** @see #DEFAULT_HBARRIGHT_HOVER_LOCATION DEFAULT_HBARRIGHT_HOVER_LOCATION
15134        **  
15135        */
15136       public static final AnnotationLocation DEFAULT_HOVER_LOCATION =
15137          AnnotationLocation.NORTHWEST;
15138       /** The default fontsize of text that appears
15139        ** in the chart's legend (key).
15140        **
15141        ** @see Axis#setTickLabelFontSize setTickLabelFontSize
15142        ** @see #getXAxis getXAxis
15143        ** @see #getYAxis getYAxis
15144        ** @see #getY2Axis getY2Axis
15145        ** 
15146        **/ 
15147       public final static int DEFAULT_LEGEND_FONTSIZE = 12;
15148    
15149    
15150       /**
15151        ** The default background color used for the chart's plot area
15152        ** if none is specified.
15153        **
15154        ** @see #setPlotAreaBackgroundColor setPlotAreaBackgroundColor
15155        **
15156        **/ 
15157       public final static String DEFAULT_PLOTAREA_BACKGROUND_COLOR = "transparent";
15158       /**
15159        ** The default border color used for the chart's plot area
15160        ** if none is specified.
15161        **
15162        ** @see #setPlotAreaBorderColor setPlotAreaBorderColor
15163        **
15164        **/ 
15165       public final static String DEFAULT_PLOTAREA_BORDER_COLOR = "black";
15166       /**
15167        ** The default style of the border around the chart's plot area
15168        ** if none is specified.
15169        **
15170        ** @see #setPlotAreaBorderStyle setPlotAreaBorderStyle
15171        **
15172        **/ 
15173       public final static String DEFAULT_PLOTAREA_BORDER_STYLE = "solid";
15174       /**
15175        ** The default width of the border around the chart's plot area
15176        ** if none is specified.
15177        **
15178        ** @see #setPlotAreaBorderWidth setPlotAreaBorderWidth
15179        **
15180        **/ 
15181       public final static int DEFAULT_PLOTAREA_BORDER_WIDTH = 0;
15182       /**
15183        ** The default CSS background color used for symbols if none is
15184        ** specified.
15185        **
15186        ** @see Curve#getSymbol getSymbol
15187        ** @see Symbol#setBackgroundColor setBackgroundColor
15188        **/ 
15189       public static final String DEFAULT_SYMBOL_BACKGROUND_COLOR =
15190          "transparent";
15191       /**
15192        ** The default CSS border colors used for symbols if none are
15193        ** specified. These defaults are, in order of the curve's
15194        ** integer index: red, green, blue, fuchsia, aqua, teal,
15195        ** maroon, lime, navy, silver, olive, purple.  This sequence
15196        ** repeats if there are more than 12 curves.
15197        ** <p>
15198        ** 
15199        ** @see Curve#getSymbol getSymbol
15200        ** @see Symbol#setBorderColor setBorderColor
15201        ** 
15202        **/
15203    
15204       public static final String[] DEFAULT_SYMBOL_BORDER_COLORS =
15205         {"red", "green", "blue", 
15206          "fuchsia", "aqua", "teal",
15207          "maroon", "lime", "navy",
15208          "silver", "olive", "purple"};
15209       private static String[] defaultSymbolBorderColors =
15210          DEFAULT_SYMBOL_BORDER_COLORS;
15211       
15212       /**
15213        ** The default CSS border style used for symbols if none is
15214        ** specified; this default is "solid". 
15215        ** 
15216        ** @see Curve#getSymbol getSymbol
15217        ** @see Symbol#setBorderStyle setBorderStyle
15218        ** 
15219        **/ 
15220       public static final String DEFAULT_SYMBOL_BORDER_STYLE = "solid";
15221       /**
15222        ** The default CSS border width used for symbols if none is
15223        ** specified; this default is 1 pixel. 
15224        ** 
15225        ** @see Curve#getSymbol getSymbol
15226        ** @see Symbol#setBorderWidth setBorderWidth
15227        ** 
15228        **/ 
15229       public static final int DEFAULT_SYMBOL_BORDER_WIDTH = 1;
15230       /**
15231        ** The default spacing between discrete, rectangular, elements
15232        ** used to simulate continuous graphical elements. This
15233        ** default does not apply to bar chart symbol types or
15234        ** the LINE symbol type, which have their own default
15235        ** fill spacings.
15236        ** <p>
15237        ** 
15238        ** @see Curve#getSymbol getSymbol
15239        ** @see Symbol#setFillSpacing setFillSpacing
15240        ** @see Symbol#setFillThickness setFillThickness
15241        ** @see #DEFAULT_BAR_FILL_SPACING
15242        **      DEFAULT_BAR_FILL_SPACING
15243        ** @see #DEFAULT_LINE_FILL_SPACING
15244        **      DEFAULT_LINE_FILL_SPACING
15245        ** 
15246        **/ 
15247       public static final double DEFAULT_SYMBOL_FILL_SPACING = 4;
15248       /**
15249        ** The default "thickness" of the rectangular elements used to
15250        ** simulate continuous graphical objects, such as connecting
15251        ** lines in line charts. This default applies to all symbol
15252        ** types <tt>except</tt> for those representing pie slices,
15253        ** whose default is
15254        ** <tt>DEFAULT_PIE_SLICE_FILL_THICKNESS</tt>, and the LINE
15255        ** symbol type, whose default is DEFAULT_LINE_FILL_THICKNESS.
15256        **
15257        ** <p> Since this default thickness is 0 px, this implies
15258        ** that, except for pie slices and lines, no such continuous fill
15259        ** elements are generated by default. For example, if you
15260        ** want to have dotted connecting lines drawn between individual
15261        ** data points represented using the <tt>BOX_CENTER</tt>
15262        ** symbol type, you must explicitly specify a positive fill
15263        ** thickness (for solid connecting lines, the LINE symbol
15264        ** is usually far more efficient than using a fill thickness
15265        ** of 1px with the BOX_CENTER symbol).
15266        **
15267        ** @see #DEFAULT_PIE_SLICE_FILL_THICKNESS
15268        **      DEFAULT_PIE_SLICE_FILL_THICKNESS
15269        ** @see #DEFAULT_LINE_FILL_THICKNESS
15270        **      DEFAULT_LINE_FILL_THICKNESS
15271        ** @see Curve#getSymbol getSymbol
15272        ** @see Symbol#setFillSpacing setFillSpacing
15273        ** @see Symbol#setFillThickness setFillThickness
15274        ** 
15275        **/ 
15276       public static final int DEFAULT_SYMBOL_FILL_THICKNESS = 0;
15277    
15278    
15279       /**
15280        ** The default spacing between discrete, rectangular, elements
15281        ** used to simulate continuous filling of polygonal regions
15282        ** formed by connecting corresponding ends of successive
15283        ** bars in a bar chart.
15284        ** <p>
15285        ** 
15286        ** @see Curve#getSymbol getSymbol
15287        ** @see Symbol#setFillSpacing setFillSpacing
15288        ** @see Symbol#setFillThickness setFillThickness
15289        ** @see #DEFAULT_SYMBOL_FILL_SPACING DEFAULT_SYMBOL_FILL_SPACING
15290        ** 
15291        **/ 
15292       public static final double DEFAULT_BAR_FILL_SPACING = 0;
15293       
15294       /**
15295        ** The default thickness of connecting lines drawn on
15296        ** curves whose symbols have the LINE symbol type.
15297        **
15298        ** @see #DEFAULT_SYMBOL_FILL_THICKNESS
15299        **      DEFAULT_SYMBOL_FILL_THICKNESS
15300        ** @see Curve#getSymbol getSymbol
15301        ** @see Symbol#setFillSpacing setFillSpacing
15302        ** @see Symbol#setFillThickness setFillThickness
15303        ** 
15304        **/
15305       public static final int DEFAULT_LINE_FILL_THICKNESS = 1;
15306    
15307       
15308       /**
15309        ** The default spacing between discrete, rectangular, elements
15310        ** used to simulate continuously connected lines between
15311        ** successive points on a curve that uses the
15312        ** <tt>LINE</tt> symbol type.
15313        ** <p>
15314        ** 
15315        ** @see Curve#getSymbol getSymbol
15316        ** @see Symbol#setFillSpacing setFillSpacing
15317        ** @see Symbol#setFillThickness setFillThickness
15318        ** @see #DEFAULT_SYMBOL_FILL_SPACING DEFAULT_SYMBOL_FILL_SPACING
15319        ** 
15320        **/ 
15321       public static final int DEFAULT_LINE_FILL_SPACING = 0;
15322    
15323       /**
15324        ** The default "spacing" between corresponding edges of the
15325        ** rectangular elements used to simulate continuous fill of pie
15326        ** slices.  <p>
15327        **
15328        ** @see #DEFAULT_SYMBOL_FILL_SPACING
15329        **      DEFAULT_SYMBOL_FILL_SPACING
15330        ** @see #DEFAULT_PIE_SLICE_FILL_THICKNESS
15331        **      DEFAULT_PIE_SLICE_FILL_THICKNESS
15332        ** @see Curve#getSymbol getSymbol
15333        ** @see Symbol#setFillSpacing setFillSpacing
15334        ** @see Symbol#setFillThickness setFillThickness
15335        **
15336        **/ 
15337       public static final double DEFAULT_PIE_SLICE_FILL_SPACING = 4;
15338       /**
15339        ** The default "thickness" of the rectangular elements
15340        ** used to simulate continuous fill of pie slices. This
15341        ** thickness defines the height of horizontal pie slice
15342        ** shading bars, and the width of vertical shading bars.
15343        ** <p>
15344        **
15345        ** @see #DEFAULT_SYMBOL_FILL_THICKNESS
15346        **      DEFAULT_SYMBOL_FILL_THICKNESS
15347        ** @see #DEFAULT_LINE_FILL_THICKNESS
15348        **      DEFAULT_LINE_FILL_THICKNESS
15349        ** @see Curve#getSymbol getSymbol
15350        ** @see Symbol#setFillSpacing setFillSpacing
15351        ** @see Symbol#setFillThickness setFillThickness
15352        **
15353        **/ 
15354       public static final int DEFAULT_PIE_SLICE_FILL_THICKNESS = 2;
15355    
15356       /**
15357        ** The default hovertext template used by symbols when they have a
15358        ** symbol type of of the form PIE_SLICE_*.
15359        **
15360        ** @see Symbol#setHovertextTemplate setHovertextTemplate
15361        ** @see SymbolType#PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
15362        ** @see #DEFAULT_HOVERTEXT_TEMPLATE DEFAULT_HOVERTEXT_TEMPLATE
15363        ** 
15364        **/ 
15365       public static final String DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE =
15366          GChart.formatAsHovertext("${pieSliceSize}");
15367    
15368       /**
15369        ** The default hover feedback location used by symbols when they have a
15370        ** symbol type of of the form PIE_SLICE_*.
15371        **
15372        ** @see Symbol#setHoverLocation setHoverLocation
15373        ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
15374        ** 
15375        **/ 
15376       public static final AnnotationLocation DEFAULT_PIE_SLICE_HOVER_LOCATION =
15377         AnnotationLocation.OUTSIDE_PIE_ARC;
15378       /**
15379        ** The default height (including borders) used for
15380        ** symbols if none is specified; this default is 
15381        ** the same as for <tt>DEFAULT_SYMBOL_WIDTH</tt>
15382        ** 
15383        ** @see Curve#getSymbol getSymbol
15384        ** @see Symbol#setHeight setHeight
15385        ** @see #DEFAULT_SYMBOL_WIDTH DEFAULT_SYMBOL_WIDTH
15386        ** 
15387        **/ 
15388       public static final int DEFAULT_SYMBOL_HEIGHT = 7;
15389       
15390    /**
15391        ** The default symbol type for curve if none is
15392        ** specified; this default is BOX_CENTER
15393        **
15394        ** @see SymbolType#BOX_CENTER BOX_CENTER
15395        ** @see Symbol#setSymbolType setSymbolType
15396        **
15397        **/ 
15398       public static final SymbolType DEFAULT_SYMBOL_TYPE = SymbolType.BOX_CENTER;
15399    
15400      /**
15401        ** The default width (including borders) used for
15402        ** symbols if none is specified. 
15403        ** 
15404        ** @see Curve#getSymbol getSymbol
15405        ** @see Symbol#setWidth setWidth
15406        ** @see #DEFAULT_SYMBOL_WIDTH DEFAULT_SYMBOL_WIDTH
15407        **/ 
15408       public static final int DEFAULT_SYMBOL_WIDTH =
15409         DEFAULT_SYMBOL_HEIGHT;
15410       
15411       /**
15412       * The default number of tick marks on each Axis.
15413       *
15414       * @see Axis#setTickCount setTickCount
15415       * 
15416       */
15417      public static final int DEFAULT_TICK_COUNT = 10;
15418    
15419      /** The default color (a CSS color specification) of tick labels
15420       **
15421       ** @see Axis#setTickLabelFontColor setTickLabelFontColor
15422       ** @see #getXAxis getXAxis
15423       ** @see #getYAxis getYAxis
15424       ** @see #getY2Axis getY2Axis
15425       **/ 
15426       public final static String DEFAULT_TICK_LABEL_FONT_COLOR ="black";
15427    
15428      /** The default CSS font-style applied to tick labels
15429       **
15430       ** @see Axis#setTickLabelFontStyle setTickLabelFontStyle
15431       ** @see #getXAxis getXAxis
15432       ** @see #getYAxis getYAxis
15433       ** @see #getY2Axis getY2Axis
15434       **/ 
15435       public final static String DEFAULT_TICK_LABEL_FONT_STYLE ="normal";
15436    
15437      /** The default CSS font-weight applied to tick labels
15438       **
15439       ** @see Axis#setTickLabelFontWeight setTickLabelFontWeight
15440       ** @see #getXAxis getXAxis
15441       ** @see #getYAxis getYAxis
15442       ** @see #getY2Axis getY2Axis
15443       **/ 
15444       public final static String DEFAULT_TICK_LABEL_FONT_WEIGHT ="normal";
15445    
15446     
15447      /** The default fontsize (in pixels) of tick labels
15448        **
15449        ** @see Axis#setTickLabelFontSize setTickLabelFontSize
15450        ** @see #getXAxis getXAxis
15451        ** @see #getYAxis getYAxis
15452        ** @see #getY2Axis getY2Axis
15453        **/ 
15454       public final static int DEFAULT_TICK_LABEL_FONTSIZE = 12;
15455      /**
15456        ** The default GWT <tt>NumberFormat</tt> format string used to convert
15457        ** numbers to the text strings displayed as tick labels
15458        ** on X, Y, and Y2 axes.
15459        **
15460        ** @see Axis#setTickLabelFormat setTickLabelFormat
15461        ** @see #getXAxis getXAxis
15462        ** @see #getYAxis getYAxis
15463        ** @see #getY2Axis getY2Axis
15464        ** 
15465        **/ 
15466       public final static String DEFAULT_TICK_LABEL_FORMAT = "#.##";
15467    
15468       /**
15469       * The default length of tick marks, in pixels.
15470       *
15471       * @see Axis#setTickLength setTickLength
15472       */
15473      public static final int DEFAULT_TICK_LENGTH = 6;
15474    
15475    
15476       /**
15477       * The default tick location.
15478       *
15479       * @see Axis#setTickLocation setTickLocation
15480       */
15481      public static final TickLocation DEFAULT_TICK_LOCATION = TickLocation.OUTSIDE;
15482    
15483      
15484       /**
15485       * The default thickness of tick marks, in pixels.
15486       *
15487       * @see Axis#setTickThickness setTickThickness
15488       */
15489      public static final int DEFAULT_TICK_THICKNESS = 1; // pixel
15490    
15491       /**
15492        ** The default location used to position the hover feedback
15493        ** when the user hovers their mouse above a point on a curve
15494        ** that uses a VBAR_BASELINE_* symbol type.
15495        ** 
15496        ** @see Symbol#setHoverLocation setHoverLocation
15497        ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
15498        **    
15499        */
15500       public static final AnnotationLocation DEFAULT_VBAR_BASELINE_HOVER_LOCATION =
15501          AnnotationLocation.FARTHEST_FROM_HORIZONTAL_BASELINE;
15502       /**
15503        ** The default location used to position the hover feedback
15504        ** when the user hovers their mouse above a point on a curve
15505        ** that uses a VBAR_SOUTH* symbol type. 
15506        ** 
15507        ** @see Symbol#setHoverLocation setHoverLocation
15508        ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
15509        **    
15510        */
15511       public static final AnnotationLocation DEFAULT_VBARBOTTOM_HOVER_LOCATION =
15512          AnnotationLocation.NORTH;
15513    
15514       /**
15515        ** The default location used to position the hover feedback
15516        ** when the user hovers their mouse above a point on a curve
15517        ** that uses a VBAR_NORTH* symbol type. 
15518        ** 
15519        ** @see Symbol#setHoverLocation setHoverLocation
15520        ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
15521        **    
15522        */
15523       public static final AnnotationLocation DEFAULT_VBARTOP_HOVER_LOCATION =
15524          AnnotationLocation.SOUTH;
15525    
15526       /**
15527        ** The default location used to position the 
15528        ** hover feedback when the user hovers their mouse above a point
15529        ** on a curve that uses a HBAR_BASELINE_* symbol type.
15530        ** 
15531        ** @see Symbol#setHoverLocation setHoverLocation
15532        ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
15533        **    
15534        */
15535       public static final AnnotationLocation DEFAULT_HBAR_BASELINE_HOVER_LOCATION =
15536          AnnotationLocation.FARTHEST_FROM_VERTICAL_BASELINE;
15537    
15538       /**
15539        ** The default location used to position the 
15540        ** hover feedback when the user hovers their mouse above a point
15541        ** on a curve that uses an HBAR_*WEST symbol type.
15542        ** 
15543        ** @see Symbol#setHoverLocation setHoverLocation
15544        ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
15545        **    
15546        */
15547       public static final AnnotationLocation DEFAULT_HBARLEFT_HOVER_LOCATION =
15548          AnnotationLocation.EAST;
15549    
15550       /**
15551        ** The default location used to position the 
15552        ** hover feedback when the user hovers their mouse above a point
15553        ** on a curve that uses an HBAR_*EAST symbol type.
15554        ** 
15555        ** @see Symbol#setHoverLocation setHoverLocation
15556        ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
15557        **    
15558        */
15559       public static final AnnotationLocation DEFAULT_HBARRIGHT_HOVER_LOCATION =
15560          AnnotationLocation.WEST;
15561    
15562      /**
15563       ** The default upper bound on the width of widgets used
15564       ** in annotations and tick labels that GChart
15565       ** will assume for centering and similar alignment purposes.
15566       **
15567       ** @see Curve.Point#setAnnotationWidget setAnnotationWidget
15568       ** @see Axis#addTick(double,Widget,int,int) addTick
15569       ** 
15570       **/ 
15571      public static final int DEFAULT_WIDGET_WIDTH_UPPERBOUND = 400;
15572      /**
15573       ** The default upper bound on the height of widgets used
15574       ** in annotations and tick labels that GChart
15575       ** will assume for centering and similar alignment purposes.
15576       **
15577       ** @see Curve.Point#setAnnotationWidget setAnnotationWidget
15578       ** @see Axis#addTick(double,Widget,int,int) addTick
15579       ** 
15580       **/ 
15581      public static final int DEFAULT_WIDGET_HEIGHT_UPPERBOUND = 400;
15582      
15583      /**
15584       *  The default width of the area of the chart in
15585       *  which curves are displayed, in pixels.
15586       */
15587      public final static int DEFAULT_X_CHARTSIZE = 300; // pixels
15588      /**
15589       * The default height of the area of the chart in
15590       * which curves are displayed, in pixels.
15591       */
15592      public final static int DEFAULT_Y_CHARTSIZE = 300; // pixels
15593    
15594       /**
15595        ** In analogy to how it uses <tt>Double.NaN</tt> (Not a
15596        ** Number), GChart uses <tt>GChart.NAI</tt> (Not an Integer) to
15597        ** represent integers whose values have not been explicitly
15598        ** specified.
15599        ** 
15600        **/ 
15601       public static final int NAI = Integer.MIN_VALUE;
15602    
15603       /**
15604        * Due to a well-known bug (see, for example, <a
15605        * href="http://www.hedgerwow.com/360/dhtml/css-ie-transparent-border.html">
15606        * this explanation on Hedger Wang's blog</a>), though white
15607        * may not be black in IE6, transparent borders certainly are.
15608        * Besides this outright bug, different browsers define which
15609        * element's background color "shines through" a transparent border
15610        * differently. For example, in FF2, the background of the element
15611        * containing the border shines through, which makes setting the
15612        * border color to "transparent" equivalent to setting the border
15613        * color to equal the background color. In IE7, the color of
15614        * the chart's background "shines through"--which is more likely
15615        * what you intended when you set a symbol's border to transparent.
15616        * <p>
15617        *
15618        * To make it easy for you to eliminate such problems, and obtain a
15619        * consistently behaving transparent-border behavior cross-browser,
15620        * this special GChart-only "color" (recognized by all GChart border
15621        * color related methods <i>except</i>
15622        * <tt>GChart.setBorderColor</tt>) causes GChart to emulate a
15623        * transparent border by eliminating the border entirely (setting
15624        * it's width to 0) and changing the size and position of the element
15625        * so as to make it look like the border is still "taking up space".
15626        * 
15627        * <p>
15628        *
15629        * 
15630        * <blockquote><small> <i>Note:</i>The <tt>GChart.setBorderColor</tt>
15631        * method, which sets the color of the border around the entire
15632        * chart, does <i>not</i> support this keyword because GChart's
15633        * transparent border emulation relies on changing the size of, and
15634        * shifting the position of, the transparently bordered element. But,
15635        * the position of the GChart as a whole is determined not by GChart,
15636        * but by the enclosing page. Well-known CSS tricks, such as
15637        * described in the "hedgerwow" link above, can be used if you need a
15638        * truely transparent border around the entire chart. Or, just fake
15639        * it by setting the border color to equal the background color
15640        * of the containing page.  </small></blockquote>
15641        * 
15642        * <p>
15643        *
15644        * This differs from setting the border color to "transparent" (which
15645        * you can still do should you need the "standard non-standard"
15646        * transparent border color behavior) in subtle ways that can matter
15647        * in special cases. For example, because the element is smaller than
15648        * it is with "transparent", if you draw your symbols outside the
15649        * chart rectangle, GChart will not be able to track the mouse moves
15650        * inside the transparent region (yes, this is a fine point, but
15651        * there could be other differences I'm not aware of). In almost
15652        * every other case I can think of, though, setting the border color
15653        * to this special keyword instead of "transparent" will be the
15654        * simplest way to eliminate these inconsistent transparent border
15655        * problems from your charts.
15656        * <p>
15657        *
15658        * @see Symbol#setBorderColor setBorderColor
15659        * 
15660        */
15661       
15662       public static final String TRANSPARENT_BORDER_COLOR = null;
15663    
15664       /**
15665        ** A special value used to tell GChart that a property should
15666        ** be defined via CSS, not via an explicit Java API specification.
15667        ** 
15668        ** <p>
15669        ** 
15670        ** This value is mainly used by GChart's "CSS convenience methods"
15671        ** which make it possible to use the Java API to specify
15672        ** certain properties of a GChart that can also be specified
15673        ** via CSS. When the value of the Java property is set to
15674        ** <tt>USE_CSS</tt> GChart ignores the API specification
15675        ** and allows the standard "CSS cascade" to define the
15676        ** property. 
15677        **
15678        ** The discussion below elaborates on why GChart
15679        ** supports CSS convenience methods, and how the <tt>USE_CSS</tt>
15680        ** keyword fits into that support.
15681        **
15682        ** <p>
15683        **
15684        ** <blockquote><small>
15685        ** <b>CSS Convenience Methods and the
15686        ** <tt>GChart.USE_CSS</tt> keyword</b>
15687        ** <p>
15688        ** 
15689        ** Like all GWT Widgets, a GChart is both an object in a Java application,
15690        ** and an HTML element in a web page.
15691        ** 
15692        ** <p>
15693        ** This duality naturally divides the properties of a GChart into
15694        ** three categories: 
15695        ** 
15696        ** <ol>
15697        ** 
15698        **  <li>Those you can access only via the Java API.
15699        **  <li>Those you can access only via CSS and the DOM.
15700        **  <li>Those you can access both ways.
15701        ** </ol>
15702        ** <p>
15703        ** 
15704        ** I've used the following criteria to determine the access method
15705        ** appropriate for a given GChart property:
15706        ** <p>
15707        ** 
15708        ** <ol>
15709        ** 
15710        ** <li> Those properties that mainly define the chart
15711        **      itself--independent of its relationship to any containing web
15712        **      page--are exclusively accessed via the Java API
15713        ** 
15714        ** <p>
15715        ** 
15716        ** For example, the x,y data values of a curve have everything to do
15717        ** with the chart itself and nothing to do with the enclosing web
15718        ** page, so all the defining x,y data of a curve can only be
15719        ** accessed via the Java API. 
15720        ** <p>
15721        ** 
15722        ** <li>Those properties that mainly define how the chart fits into
15723        ** the enclosing web page and have nothing to do with the chart
15724        ** itself are accessed exclusively via CSS stylesheets or the
15725        ** GWT DOM class.
15726        ** 
15727        ** <p> For example, how big of a margin should be placed around a
15728        ** GChart is only about how the GChart fits into the enclosing web
15729        ** page, so you must define a GChart's margins using a CSS
15730        ** stylesheet (or via the GWT DOM class)--there is no
15731        ** <tt>GChart.setMargin</tt> method. 
15732        ** 
15733        ** <li>Finally, those properties that, in some situations are
15734        ** best viewed as part of the chart itself, and in other
15735        ** situations as defining how the chart fits into the enclosing
15736        ** web page can be accessed <i>either</i> via the Java API, or
15737        ** via CSS/DOM.  <p>
15738        ** 
15739        ** For example, if you are focused on assuring that the chart has
15740        ** a the same border as every other element on the page, the
15741        ** border around the chart as a whole can be viewed as relating
15742        ** to how the chart fits into the enclosing web page. On the
15743        ** other hand, if you are focused on assuring that, like the
15744        ** frame around a picture, the border looks good around that
15745        ** particular chart, it makes more sense to view it as a part of
15746        ** the chart itself. 
15747        ** 
15748        ** </ol>
15749        ** 
15750        ** <p>
15751        ** 
15752        ** The Java API methods for GChart properties in this third
15753        ** category are known as "CSS convenience methods" because, though
15754        ** you could do the same thing by exploiting the "GChart as HTML
15755        ** element" perspective, these methods save you the trouble of
15756        ** looking up CSS syntax, splitting up your chart's specification
15757        ** between Java code and a CSS stylesheet, invoking a rather
15758        ** hard-to-remember method call in the GWT DOM class, etc. 
15759        ** 
15760        ** <p> Specifications made via the GChart Java API always take
15761        ** precedence over those made via stylesheets or the DOM class.
15762        ** To instruct GChart that you want one of these properties to be
15763        ** defined via CSS or the DOM, set the associated Java API
15764        ** property to the special value <tt>GChart.USE_CSS</tt>. 
15765        ** 
15766        ** <p> Fortunately, since <tt>USE_CSS</tt> is the default value for
15767        ** every one of these CSS convenience properties, if you never use the
15768        ** Java API to set them, you can use CSS to control them
15769        ** just as you would for a GWT widget that did not support
15770        ** convenience properties.
15771        **
15772        **
15773        ** Unfortunately, these CSS defaults rarely produce a great
15774        ** looking chart out of the box; the example CSS snippet
15775        ** below defines all of these convenience properties and
15776        ** attaches them to GChart's default CSS selector (aka
15777        ** stylename) in a way that I think looks better. A comment
15778        ** to the right of each line contains the corresponding
15779        ** CSS convenience-method call that has the same effect.
15780        **
15781        ** <p>
15782        ** <pre>
15783        ** .gchart-GChart {
15784        **   background-color: #DDF;   /* setBackgroundColor("#DDF"); *&#47;
15785        **   border-width: 1px;         /* setBorderWidth("1px"); *&#47;
15786        **   border-color: black;       /* setBorderColor("black"); *&#47; 
15787        **   border-style: solid;       /* setBorderStyle("solid");  *&#47; 
15788        **   font-family: Arial, sans-serif;  /* setFontFamily("Arial, sans-serif"); *&#47;
15789        ** }
15790        ** </pre>
15791        **
15792        **
15793        ** Note that certain CSS convenience methods that could in
15794        ** principle have been added, such as those for defining the
15795        ** background image of a chart, were omitted because I
15796        ** thought they would almost never be used. Of course, you
15797        ** can always access these CSS properties "the old fashioned
15798        ** way" (via a CSS specification or methods of the GWT DOM class).
15799        ** 
15800        ** <p>
15801        ** </small></blockquote>
15802        **
15803        ** @see #setBorderColor(String) setBorderColor
15804        ** @see #setBorderStyle(String) setBorderStyle
15805        ** @see #setBackgroundColor(String) setBackgroundColor
15806        ** @see #setBorderWidth(String) setBorderWidth
15807        ** @see #setFontFamily(String) setFontFamily
15808        ** 
15809        ** 
15810        ** 
15811        **/
15812        /*
15813         * Setting a CSS property to "" generally clears the
15814         * attribute specification, restoring things to their initial
15815         * defaults (not sure if this always works, but it appears to
15816         * so far).
15817         * 
15818         */
15819        public final static String USE_CSS = "";
15820    
15821      /**
15822       ** Keyword used to indicate that a curve should be
15823       ** displayed on the left y-axis.
15824       **
15825       ** @see #Y2_AXIS Y2_AXIS
15826       ** @see GChart.Curve#setYAxis(GChart.YAxisId) setYAxis
15827       **
15828       **/ 
15829       public static final YAxisId Y_AXIS = new YAxisId();
15830    
15831       /**
15832       ** Keyword used to indicate that a curve should be
15833       ** displayed on the right (the so-called y2) y-axis.
15834       **
15835       ** @see #Y_AXIS Y_AXIS
15836       ** @see Curve#setYAxis setYAxis
15837       ** 
15838       **/ 
15839       public static final YAxisId Y2_AXIS = new YAxisId();
15840    
15841      /**
15842       ** The default URL GChart will use to access the blank image
15843       ** (specifically, a 1 x 1 pixel transparent GIF) it requires
15844       ** to prevent "missing image" icons from appearing in your
15845       ** charts.
15846       **  
15847       ** @see #setBlankImageURL setBlankImageURL
15848       ** 
15849       **/ 
15850      public final static String DEFAULT_BLANK_IMAGE_URL = "gchart.gif";
15851      /**
15852       ** The full path to the default GChart blank image
15853       ** (specifically, a 1 x 1 pixel transparent GIF) it requires
15854       ** to prevent "missing image" icons from appearing in your
15855       ** charts.
15856       ** <p>
15857       **
15858       ** Convenience constant equal to:
15859       **
15860       ** <pre>
15861       ** GWT.getModuleBaseURL()+GChart.DEFAULT_BLANK_IMAGE_URL
15862       ** </pre>
15863       **  
15864       ** @see #setBlankImageURL setBlankImageURL
15865       ** 
15866       **/ 
15867      public final static String DEFAULT_BLANK_IMAGE_URL_FULLPATH =
15868         GWT.getModuleBaseURL()+GChart.DEFAULT_BLANK_IMAGE_URL;
15869      private static final int DEFAULT_GRID_HEIGHT = 
15870          DEFAULT_TICK_THICKNESS;   
15871      private static final int DEFAULT_GRID_WIDTH =
15872          DEFAULT_TICK_THICKNESS;   
15873      private static final String GRID_BORDER_STYLE = "solid";
15874      private static final int GRID_BORDER_WIDTH = 1;
15875    
15876      /** The default color used for all axes, gridlines, and ticks.
15877       **
15878       ** @see #setGridColor setGridColor
15879       **
15880       */
15881      public static final String DEFAULT_GRID_COLOR = "black"; 
15882    
15883    
15884      /** The default thickness (height) of the rectangular region at
15885       ** the bottom of the chart allocated for footnotes, per
15886       ** <tt>&lt;br&gt;</tt> or <tt>&lt;li&gt;</tt> delimited HTML line. This
15887       ** default is only used when the footnote thickness is set to
15888       ** <tt>GChart.NAI</tt> (the default).
15889       ** 
15890       ** @see #setChartFootnotesThickness setChartFootnotesThickness
15891       **
15892       */
15893      public static final int DEFAULT_FOOTNOTES_THICKNESS = 15; 
15894    
15895      /**
15896       **
15897       ** The default thickness (height) of the rectangular region at
15898       ** the top of the chart allocated for the chart's title, per
15899       ** <tt>&lt;br&gt;</tt> or <tt>&lt;li&gt;</tt> delimited HTML line. This default
15900       ** is only used when the title thickness is set to
15901       ** <tt>GChart.NAI</tt>.
15902       ** 
15903       **
15904       ** @see #setChartTitleThickness setChartTitleThickness
15905       **
15906       */
15907      public static final int DEFAULT_TITLE_THICKNESS = 15; 
15908    
15909      
15910      
15911    // for purposes of estimating fixed space "band" around the plot
15912    // panel reserved for the tick labels:
15913      private static final double
15914          TICK_CHARHEIGHT_TO_FONTSIZE_LOWERBOUND = 1.0;   
15915      // a bit larger than the 0.6 rule-of-thumb 
15916      private static final double
15917          TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND = 0.7;
15918      // For estimating size of invisible "box" needed for alignment
15919      // purposes.  Note: when these are bigger, annotations remain
15920      // properly aligned longer as user zooms up font sizes.  But,
15921      // bigger bounding boxes can slow updates (not sure why,
15922      // maybe it's related to hit testing browser has to do)
15923      private static final double
15924            CHARHEIGHT_TO_FONTSIZE_UPPERBOUND = 2*1.5; 
15925      private static final double
15926           CHARWIDTH_TO_FONTSIZE_UPPERBOUND = 2*0.7; 
15927    
15928      private static final String TICK_BORDER_STYLE = GRID_BORDER_STYLE;
15929      private static final int TICK_BORDER_WIDTH = GRID_BORDER_WIDTH;
15930    
15931      private static void setBackgroundColor(
15932          UIObject uio, String cssColor) {
15933         DOM.setStyleAttribute(uio.getElement(),
15934                                "backgroundColor", cssColor);
15935      }
15936    
15937    //  private static void setBackground(
15938    //      UIObject uio, String cssColor) {
15939    //     DOM.setStyleAttribute(uio.getElement(),
15940    //                           "background", cssColor);
15941    //  }
15942       
15943      private static void setBorderColor(
15944           UIObject uio, String cssColor) {
15945          DOM.setStyleAttribute(uio.getElement(),
15946                                "borderColor", cssColor);
15947       }
15948    
15949    
15950    
15951    
15952      
15953      private static void setBorderStyle(
15954           UIObject uio, String cssBorderStyle) {
15955          DOM.setStyleAttribute(uio.getElement(),
15956                                "borderStyle", cssBorderStyle);
15957       }
15958      
15959    
15960      private static void setBorderWidth(
15961           UIObject uio, String cssBorderWidth) {
15962          DOM.setStyleAttribute(uio.getElement(),
15963                                "borderWidth", cssBorderWidth);
15964       }
15965    
15966      private static void setBorderWidth(
15967          UIObject uio, int borderWidth) {
15968          if (borderWidth != GChart.NAI)
15969             setBorderWidth(uio, borderWidth + "px");
15970          else
15971             setBorderWidth(uio, "");
15972       }
15973    
15974      private static void setFontFamily(
15975           UIObject uio, String cssFontFamily) {
15976          DOM.setStyleAttribute(uio.getElement(),
15977                                "fontFamily", cssFontFamily);
15978       }  
15979      
15980      private static void setFontSize(
15981           UIObject uio, int fontSize) {
15982          DOM.setIntStyleAttribute(
15983             uio.getElement(), "fontSize", fontSize);
15984       }
15985    
15986      private static void setFontStyle(
15987           UIObject uio, String fontStyle) {
15988          DOM.setStyleAttribute(uio.getElement(),
15989             "fontStyle", fontStyle);
15990       }
15991      private static void setFontWeight(
15992           UIObject uio, String fontWeight) {
15993          DOM.setStyleAttribute(uio.getElement(),
15994             "fontWeight", fontWeight);
15995       }
15996    
15997      private static void setColor(
15998           UIObject uio, String cssColor) {
15999          DOM.setStyleAttribute(uio.getElement(),
16000             "color", cssColor);
16001      }
16002    
16003      // valid layout strings are fixed, auto, and inherit
16004    //  private static void setTableLayout(
16005    //       UIObject uio, String layout) {
16006    //      DOM.setStyleAttribute(
16007    //         uio.getElement(), "table-layout", layout);
16008    //  }
16009      
16010    
16011      
16012    //   private static void setLineHeight(
16013    //       UIObject uio, String cssLineHeight) {
16014    //      DOM.setStyleAttribute(uio.getElement(),
16015    //                            "lineHeight", cssLineHeight);
16016    //   }
16017    
16018      //   private static void setTextAlign(
16019    //       UIObject uio, String cssTextAlign) {
16020    //      DOM.setStyleAttribute(
16021    //         uio.getElement(), "textAlign", cssTextAlign);
16022    //   }
16023    //  
16024    //   private static void setMargin(
16025    //       UIObject uio, String cssMargin) {
16026    //      DOM.setStyleAttribute(
16027    //         uio.getElement(), "margin", cssMargin);
16028    //   }
16029       private static void setPadding(
16030           UIObject uio, String cssPadding) {
16031          DOM.setStyleAttribute(uio.getElement(), "padding", cssPadding);
16032       }
16033       // valid choices are block, inline, list-item, or none
16034    //   private static void setDisplay(
16035    //       UIObject uio, String cssDisplay) {
16036    //      DOM.setStyleAttribute(
16037    //         uio.getElement(), "display", cssDisplay);
16038    //   }
16039       // choices are: visible, hidden, scroll or auto
16040       private static void setOverflow(
16041           UIObject uio, String cssOverflow) {
16042          DOM.setStyleAttribute(
16043             uio.getElement(), "overflow", cssOverflow);
16044       }
16045    //   private static void setTextLeading(
16046    //       UIObject uio, String cssTextLeading) {
16047    //      DOM.setStyleAttribute(
16048    //         uio.getElement(), "textTrailing", cssTextLeading);
16049    //   }
16050    //   private static void setVerticalAlign(
16051    //       UIObject uio, String cssVerticalAlign) {
16052    //      DOM.setStyleAttribute(
16053    //         uio.getElement(), "verticalAlign", cssVerticalAlign);
16054    //   }
16055    
16056       // returns the sign of the given number
16057       static int sign(double x) {
16058           int result = (x < 0) ? -1 : 1;
16059           return result;
16060       }
16061    
16062      // Validates multipliers used to simplify computing the
16063       // upper left corner location of symbols and labels to
16064       // properly reflect their alignment relative to the
16065       // plotted point or labeled symbol. 
16066      static void validateMultipliers(
16067          int widthMultiplier, int heightMultiplier) {
16068          if (!(widthMultiplier == 0 || Math.abs(widthMultiplier)==1) &&
16069              !(heightMultiplier == 0 ||Math.abs(heightMultiplier)==1))
16070             throw new IllegalArgumentException(
16071              "widthMultiplier, heightMultiplier args must both be " +
16072              "either 0, 1, or -1");
16073      }
16074    
16075     // is value within given limits, inclusive?
16076      static boolean withinRange(double x, double minLim, double maxLim) {
16077         // x!=x is a faster isNaN; NaN is considered in range 
16078         boolean result = (x!=x) ? true : (x >= minLim && x <= maxLim);
16079         return result;
16080      }
16081      
16082      private Widget chartFootnotes;
16083      private boolean chartFootnotesLeftJustified;
16084    
16085    // outer container needed so CSS-defined paddings don't interfere with positioning  
16086      private SimplePanel chartPanel = new SimplePanel();
16087    
16088      private String borderWidth = USE_CSS;
16089      private String borderStyle = USE_CSS;
16090      private String borderColor = USE_CSS;
16091      private String backgroundColor = USE_CSS;
16092      private static String blankImageURL = null;
16093      boolean chartDecorationsChanged = true;
16094      private Widget chartTitle;
16095      // collection of curves associated with this chart.  
16096      private ArrayList<Curve> curves = new ArrayList<Curve>();
16097      private String fontFamily = USE_CSS;
16098      private int footnotesThickness = GChart.NAI;
16099      private String legendBackgroundColor =
16100                                 DEFAULT_LEGEND_BACKGROUND_COLOR;
16101      private String legendBorderColor = DEFAULT_LEGEND_BORDER_COLOR;
16102      private int legendBorderWidth = DEFAULT_LEGEND_BORDER_WIDTH;
16103      private String legendBorderStyle = DEFAULT_LEGEND_BORDER_STYLE;
16104      private int legendThickness = GChart.NAI;
16105      
16106      private boolean isLegendVisible = true;
16107     
16108      private String legendFontColor = DEFAULT_FONT_COLOR;
16109      private int legendFontSize = DEFAULT_LEGEND_FONTSIZE;
16110      private String legendFontStyle = DEFAULT_FONT_STYLE;
16111      private String legendFontWeight = DEFAULT_FONT_WEIGHT;
16112    
16113     /*
16114      * Contains the plotting region, as well as axes, ticks, and
16115      * tick-labels associated with that region. Note that tickText
16116      * must be centered on the ticks--placing them on the same
16117      * AbsolutePanel as the ticks/plots facilitates this.
16118      * 
16119      */
16120      PlotPanel plotPanel =  new PlotPanel();
16121      private String padding = USE_CSS;
16122      private boolean optimizeForMemory = false;
16123      private boolean clipToPlotArea = false;
16124      private boolean clipToDecoratedChart = false;
16125      private int titleThickness = GChart.NAI;
16126      
16127      private Axis xAxis;  // must be created in constructor
16128      private Axis yAxis;  // because they use system curves
16129      private Axis y2Axis;
16130    
16131      private int xChartSize; //  pixel size of plotting region
16132      private int yChartSize;
16133    
16134      // # of system curves "underneath" (before in DOM-order) user's curves
16135      private static int N_PRE_SYSTEM_CURVES = 16;
16136      // # of system curves "on top of" (after in DOM-order) user's curves
16137      private static int N_POST_SYSTEM_CURVES = 2;
16138      private static int N_SYSTEM_CURVES = N_PRE_SYSTEM_CURVES+
16139                                           N_POST_SYSTEM_CURVES;
16140      // index of curve that holds correspondingly-named chart part
16141      // (sys curve indexes are negative & not directly developer-accessible)
16142      private final static int PLOTAREA_ID = 0-N_SYSTEM_CURVES;
16143      private final static int TITLE_ID = 1-N_SYSTEM_CURVES;
16144      private final static int YAXIS_ID = 2-N_SYSTEM_CURVES;
16145      private final static int YTICKS_ID = 3-N_SYSTEM_CURVES;
16146      private final static int YGRIDLINES_ID = 4-N_SYSTEM_CURVES;
16147      private final static int YLABEL_ID = 5-N_SYSTEM_CURVES;
16148      private final static int Y2AXIS_ID = 6-N_SYSTEM_CURVES;
16149      private final static int Y2TICKS_ID = 7-N_SYSTEM_CURVES;
16150      private final static int Y2GRIDLINES_ID = 8-N_SYSTEM_CURVES;
16151      private final static int Y2LABEL_ID = 9-N_SYSTEM_CURVES;
16152      private final static int LEGEND_ID = 10-N_SYSTEM_CURVES;
16153      private final static int XAXIS_ID = 11-N_SYSTEM_CURVES;
16154      private final static int XTICKS_ID = 12-N_SYSTEM_CURVES;
16155      private final static int XGRIDLINES_ID = 13-N_SYSTEM_CURVES;
16156      private final static int XLABEL_ID = 14-N_SYSTEM_CURVES;
16157      private final static int FOOTNOTES_ID = 15-N_SYSTEM_CURVES;
16158      private final static int HOVER_CURSOR_ID = 16-N_SYSTEM_CURVES;
16159      private final static int HOVER_ANNOTATION_ID = 17-N_SYSTEM_CURVES;
16160      
16161      // adds system curves GChart uses to render title, ticks, etc.
16162      private void addSystemCurves() {
16163         // Must be first: other methods assume sys curves exist
16164         for (int i = 0; i < N_SYSTEM_CURVES; i++) {
16165            Curve c = new Curve(i);
16166            curves.add(c);
16167    // Required rendering panels are added lazily, later on       
16168         }
16169    
16170         // define static (or default) properties, points on, system curves
16171         Curve c = getSystemCurve(PLOTAREA_ID);
16172         c.getSymbol().setSymbolType(SymbolType.BOX_SOUTHEAST);
16173         c.getSymbol().setBackgroundColor(DEFAULT_PLOTAREA_BACKGROUND_COLOR);
16174         c.getSymbol().setBorderColor(DEFAULT_PLOTAREA_BORDER_COLOR);
16175         c.getSymbol().setBorderStyle(DEFAULT_PLOTAREA_BORDER_STYLE);
16176         c.getSymbol().setBorderWidth(DEFAULT_PLOTAREA_BORDER_WIDTH);
16177         c.getSymbol().setHoverAnnotationEnabled(false);
16178         c.getSymbol().setHoverSelectionEnabled(false);
16179         c.addPoint(-Double.MAX_VALUE,Double.MAX_VALUE); 
16180    
16181         c = getSystemCurve(TITLE_ID);
16182         c.getSymbol().setSymbolType(SymbolType.ANCHOR_NORTHWEST);
16183         c.getSymbol().setHoverAnnotationEnabled(false);
16184         c.getSymbol().setHoverSelectionEnabled(false);
16185         c.addPoint(0,0); 
16186         c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);
16187    
16188         c = getSystemCurve(YAXIS_ID);
16189         c.getSymbol().setSymbolType(SymbolType.XGRIDLINE);
16190         c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
16191         c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
16192         c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
16193         c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
16194         c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
16195         c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
16196         c.getSymbol().setHoverAnnotationEnabled(false);
16197         c.getSymbol().setHoverSelectionEnabled(false);
16198         c.addPoint(-Double.MAX_VALUE,-Double.MAX_VALUE); 
16199    
16200         c = getSystemCurve(YTICKS_ID);
16201         c.getSymbol().setSymbolType(SymbolType.BOX_WEST);
16202         c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
16203         c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
16204         c.getSymbol().setBorderStyle(TICK_BORDER_STYLE);
16205         c.getSymbol().setBorderWidth(TICK_BORDER_WIDTH);
16206         c.getSymbol().setHoverAnnotationEnabled(false);
16207         c.getSymbol().setHoverSelectionEnabled(false);
16208         // points, annotation locations added when ticks are
16209         
16210         c = getSystemCurve(YGRIDLINES_ID);
16211         c.getSymbol().setSymbolType(SymbolType.YGRIDLINE);
16212         c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
16213         c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
16214         c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
16215         c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
16216         c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
16217         c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
16218         c.getSymbol().setHoverAnnotationEnabled(false);
16219         c.getSymbol().setHoverSelectionEnabled(false);
16220         
16221         c = getSystemCurve(YLABEL_ID);
16222         c.getSymbol().setSymbolType(SymbolType.ANCHOR_WEST);
16223         c.getSymbol().setHoverAnnotationEnabled(false);
16224         c.getSymbol().setHoverSelectionEnabled(false);
16225         c.addPoint(0,0); 
16226         c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);
16227    
16228         c = getSystemCurve(Y2AXIS_ID);
16229         c.setYAxis(Y2_AXIS);
16230         c.getSymbol().setSymbolType(SymbolType.XGRIDLINE);
16231         c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
16232         c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
16233         c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
16234         c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
16235         c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
16236         c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
16237         c.getSymbol().setHoverAnnotationEnabled(false);
16238         c.getSymbol().setHoverSelectionEnabled(false);
16239         c.addPoint(Double.MAX_VALUE,-Double.MAX_VALUE); 
16240    
16241         c = getSystemCurve(Y2TICKS_ID);
16242         c.setYAxis(Y2_AXIS);
16243         c.getSymbol().setSymbolType(SymbolType.BOX_EAST);
16244         c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
16245         c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
16246         c.getSymbol().setBorderStyle(TICK_BORDER_STYLE);
16247         c.getSymbol().setBorderWidth(TICK_BORDER_WIDTH);
16248         c.getSymbol().setHoverAnnotationEnabled(false);
16249         c.getSymbol().setHoverSelectionEnabled(false);
16250         
16251         c = getSystemCurve(Y2GRIDLINES_ID);
16252         c.setYAxis(Y2_AXIS);
16253         c.getSymbol().setSymbolType(SymbolType.YGRIDLINE);
16254         c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
16255         c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
16256         c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
16257         c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
16258         c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
16259         c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
16260         c.getSymbol().setHoverAnnotationEnabled(false);
16261         c.getSymbol().setHoverSelectionEnabled(false);
16262    
16263         c = getSystemCurve(Y2LABEL_ID);
16264         c.getSymbol().setSymbolType(SymbolType.ANCHOR_EAST);
16265         c.getSymbol().setHoverAnnotationEnabled(false);
16266         c.getSymbol().setHoverSelectionEnabled(false);
16267         c.addPoint(0,0); 
16268         c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);
16269    
16270         c = getSystemCurve(LEGEND_ID);
16271         c.getSymbol().setSymbolType(SymbolType.ANCHOR_EAST);
16272         c.getSymbol().setHoverAnnotationEnabled(false);
16273         c.getSymbol().setHoverSelectionEnabled(false);
16274         c.addPoint(0,0); 
16275         c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);
16276    
16277         c = getSystemCurve(XAXIS_ID);
16278         c.getSymbol().setSymbolType(SymbolType.YGRIDLINE);
16279         c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
16280         c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
16281         c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
16282         c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
16283         c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
16284         c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
16285         c.getSymbol().setHoverAnnotationEnabled(false);
16286         c.getSymbol().setHoverSelectionEnabled(false);
16287         c.addPoint(-Double.MAX_VALUE,-Double.MAX_VALUE); 
16288    
16289         // tick thickness and length get set in the axis constructors
16290         c = getSystemCurve(XTICKS_ID);
16291         c.getSymbol().setSymbolType(SymbolType.BOX_SOUTH);
16292         c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
16293         c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
16294         c.getSymbol().setBorderStyle(TICK_BORDER_STYLE);
16295         c.getSymbol().setBorderWidth(TICK_BORDER_WIDTH);
16296         c.getSymbol().setHoverAnnotationEnabled(false);
16297         c.getSymbol().setHoverSelectionEnabled(false);
16298         
16299         c = getSystemCurve(XGRIDLINES_ID);
16300         c.getSymbol().setSymbolType(SymbolType.XGRIDLINE);
16301         c.getSymbol().setBackgroundColor(DEFAULT_GRID_COLOR);
16302         c.getSymbol().setBorderColor(DEFAULT_GRID_COLOR);
16303         c.getSymbol().setBorderStyle(GRID_BORDER_STYLE);
16304         c.getSymbol().setBorderWidth(GRID_BORDER_WIDTH);
16305         c.getSymbol().setWidth(DEFAULT_GRID_WIDTH);
16306         c.getSymbol().setHeight(DEFAULT_GRID_HEIGHT);
16307         c.getSymbol().setHoverAnnotationEnabled(false);
16308         c.getSymbol().setHoverSelectionEnabled(false);
16309    
16310         c = getSystemCurve(XLABEL_ID);
16311         c.getSymbol().setSymbolType(SymbolType.ANCHOR_SOUTH);
16312         c.getSymbol().setHoverAnnotationEnabled(false);
16313         c.getSymbol().setHoverSelectionEnabled(false);
16314         c.addPoint(0,0); 
16315         c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);
16316    
16317         c = getSystemCurve(FOOTNOTES_ID);
16318         c.getSymbol().setSymbolType(SymbolType.ANCHOR_SOUTHWEST);
16319         c.getSymbol().setHoverAnnotationEnabled(false);
16320         c.getSymbol().setHoverSelectionEnabled(false);
16321         c.addPoint(0,0); 
16322         c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);
16323    
16324         c = getSystemCurve(HOVER_ANNOTATION_ID);
16325         c.setVisible(false); // initially no hover annotation
16326         c.getSymbol().setSymbolType(SymbolType.NONE);
16327         c.getSymbol().setHoverAnnotationEnabled(false);
16328         c.getSymbol().setHoverSelectionEnabled(false);
16329         c.addPoint(Double.NaN,Double.NaN); 
16330         c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);
16331    
16332         c = getSystemCurve(HOVER_CURSOR_ID);
16333         c.setVisible(false); // initially no hover selection
16334         c.getSymbol().setSymbolType(SymbolType.NONE);
16335         c.getSymbol().setHoverAnnotationEnabled(false);
16336         c.getSymbol().setHoverSelectionEnabled(false);
16337         c.addPoint(Double.NaN,Double.NaN); 
16338         c.getPoint().setAnnotationLocation(AnnotationLocation.CENTER);
16339    
16340         // external "curve count" should now be 0 (system curves don't count)
16341         if (getNCurves() != 0)
16342            throw new 
16343            IllegalStateException("getNCurves() != 0. Probably a GChart bug.");
16344    
16345      }
16346    
16347      /*
16348       * Updates the system curves that represent chart
16349       * decorations (axis labels, title, ticks, etc.).<p>
16350       *
16351       * Note that all x, y shifts are relative to the "anchoring"
16352       * symbol type locations defined once and for all in the
16353       * addSystemCurves method above.
16354       * 
16355       */ 
16356      private void updateDecorations(int xChartSizeDecorated) {
16357    
16358    
16359        // x-axis label
16360        getSystemCurve(XLABEL_ID).getPoint(0).setAnnotationWidget(
16361           getXAxis().getAxisLabel(), getXChartSize(),
16362           getXAxis().getAxisLabelThickness());
16363        getSystemCurve(XLABEL_ID).getPoint(0).setAnnotationYShift(
16364          - getXAxis().getTickLabelThickness(false)
16365          - getXAxis().getTickSpace()
16366          - getXAxis().getTickLabelPadding()
16367          - getXAxis().getAxisLabelThickness()/2); 
16368    
16369        // y-axis label
16370        getSystemCurve(YLABEL_ID).getPoint(0).setAnnotationWidget(
16371           getYAxis().getAxisLabel(),
16372           getYAxis().getAxisLabelThickness(), getYChartSize());
16373        getSystemCurve(YLABEL_ID).getPoint(0).setAnnotationXShift(
16374          - getYAxis().getTickLabelThickness(false)
16375          - getYAxis().getTickSpace()
16376          - getYAxis().getTickLabelPadding()
16377          - getYAxis().getAxisLabelThickness()/2); 
16378    
16379        // y2-axis label
16380        getSystemCurve(Y2LABEL_ID).getPoint(0).setAnnotationWidget(
16381           getY2Axis().getAxisLabel(),
16382           getY2Axis().getAxisLabelThickness(), getYChartSize());
16383        getSystemCurve(Y2LABEL_ID).getPoint(0).setAnnotationXShift(
16384          + getY2Axis().getTickLabelThickness(false)
16385          + getY2Axis().getTickSpace()
16386          + getY2Axis().getTickLabelPadding()
16387          + getY2Axis().getAxisLabelThickness()/2); 
16388    
16389        // legend
16390        Grid legend = null;
16391        if (isLegendVisible() && 0 < getNVisibleCurvesOnLegend()) 
16392          legend = createLegend(plotPanel);
16393        getSystemCurve(LEGEND_ID).getPoint(0).setAnnotationWidget(
16394           legend, getLegendThickness(), getYChartSize());
16395        getSystemCurve(LEGEND_ID).getPoint(0).setAnnotationXShift(
16396          + getY2Axis().getTickLabelThickness(false)
16397          + getY2Axis().getTickSpace()
16398          + getY2Axis().getTickLabelPadding()
16399          + getY2Axis().getAxisLabelThickness()
16400          + getLegendThickness()/2 ); 
16401    
16402        // title
16403        int shiftToLeftEdge =
16404           - getYAxis().getAxisLabelThickness()
16405           - getYAxis().getTickLabelThickness(false)
16406           - getYAxis().getTickSpace()
16407           - getYAxis().getTickLabelPadding();
16408        int shiftToHorizontalMidpoint =
16409             shiftToLeftEdge + xChartSizeDecorated/2; 
16410        getSystemCurve(TITLE_ID).getPoint(0).setAnnotationWidget(
16411           getChartTitle(), xChartSizeDecorated,
16412           getChartTitleThickness());
16413        getSystemCurve(TITLE_ID).getPoint(0).setAnnotationYShift(
16414           getChartTitleThickness()/2);
16415        getSystemCurve(TITLE_ID).getPoint(0).setAnnotationXShift(
16416           shiftToHorizontalMidpoint); 
16417    
16418        // footnotes
16419        getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationWidget(
16420           getChartFootnotes(), xChartSizeDecorated,
16421           getChartFootnotesThickness());
16422        getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationYShift(
16423          - getXAxis().getTickLabelThickness(false)
16424          - getXAxis().getTickSpace()
16425          - getXAxis().getTickLabelPadding()
16426          - getXAxis().getAxisLabelThickness()
16427          - getChartFootnotesThickness()/2 ); 
16428        if (getChartFootnotesLeftJustified()) {
16429          getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationXShift(
16430            shiftToLeftEdge); 
16431          getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationLocation(
16432               AnnotationLocation.EAST);
16433        }
16434        else { // footnotes centered
16435          getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationXShift(
16436            shiftToHorizontalMidpoint); 
16437          getSystemCurve(FOOTNOTES_ID).getPoint(0).setAnnotationLocation(
16438               AnnotationLocation.CENTER);
16439        }
16440    
16441    
16442        // add points to ticks and gridlines curves in accord with chart specs
16443        
16444        // x & y axis can be present even if no curves mapped to them
16445        getSystemCurve(XAXIS_ID).setVisible(getXAxis().getAxisVisible()); 
16446        getXAxis().populateGridlines();
16447        getSystemCurve(YAXIS_ID).setVisible(getYAxis().getAxisVisible()); 
16448        getYAxis().populateGridlines();
16449    
16450        // y2 axis is present only if at least 1 curve is on it.
16451        if (hasY2Axis()) {
16452          getY2Axis().populateGridlines();
16453          getSystemCurve(Y2AXIS_ID).setVisible(getY2Axis().getAxisVisible()); 
16454          getSystemCurve(Y2TICKS_ID).setVisible(true); 
16455          getSystemCurve(Y2GRIDLINES_ID).setVisible(true); 
16456        }
16457        else {
16458          getSystemCurve(Y2AXIS_ID).setVisible(false); 
16459          getSystemCurve(Y2TICKS_ID).setVisible(false); 
16460          getSystemCurve(Y2GRIDLINES_ID).setVisible(false); 
16461        }
16462        
16463      }
16464      
16465      /**
16466       * Instantiates a GChart with a curve display region of
16467       * the specified size.
16468       * 
16469       *
16470       * @param xChartSize the width of the curve display region, in pixels.
16471       * @param yChartSize the height of the curve display region, in pixels.
16472       *
16473       * @see #setXChartSize setXChartSize
16474       * @see #setYChartSize setYChartSize
16475       * @see #setChartSize setChartSize
16476       */
16477      public GChart(int xChartSize, int yChartSize) {
16478         super();
16479         addSystemCurves();  // must come first: later lines use system curves
16480         xAxis = new XAxis();  
16481         yAxis = new YAxis();  
16482         y2Axis = new Y2Axis();
16483         setXChartSize(xChartSize);
16484         setYChartSize(yChartSize);
16485         // Note: plotPanel (where real chart resides) won't get
16486         // added to chartPanel (top-level do-nothing container for
16487         // padding and such) until AFTER first update; FF2 has some
16488         // serious performance problems otherwise for common usage
16489         // scenarios with large widget-count pages.
16490         initWidget(chartPanel);
16491         /*
16492          * See the block comment at top of "class GChart" for a detailed
16493          * discussion/rational for GChart's (very minimal support) of
16494          * stylenames. Would like deeper support if I can ever figure out
16495          * how to do it without hamstringing future versions by locking
16496          * them into a particular implementation I might need to change
16497          * later on. In particular, I don't know how to provide such "deep"
16498          * stylenames that also work consistently with canvas-rendered
16499          * curves.
16500          */
16501         setStyleName("gchart-GChart");
16502      }
16503      /**
16504       * Convenience no-arg constructor equivalent to
16505       * <tt>GChart(DEFAULT_X_CHARTSIZE,DEFAULT_Y_CHARTSIZE)</tt>.
16506       *
16507       * @see #GChart(int,int) GChart(int,int)
16508       * 
16509       */
16510      public GChart() {
16511         this(DEFAULT_X_CHARTSIZE, DEFAULT_Y_CHARTSIZE);
16512      }
16513    
16514    
16515       /**
16516        *
16517        * Adds an object to handle click events on this chart, that
16518        * is, an object whose <tt>ClickHandler.onClick</tt> method will be
16519        * called whenever the user clicks on this chart.
16520        * 
16521        * <p>
16522        *
16523        * When implementing a class that handles GChart click
16524        * events, you'll need to know the following facts:
16525        * <p>
16526        * 
16527        * <ol>
16528        *
16529        *  <li>You can use the <tt>getSource</tt> method of the
16530        *  <tt>ClickEvent</tt> passed into your <tt>onClick</tt> handler
16531        *  to retrieve the <tt>GChart</tt> that was
16532        *  clicked on. For example:
16533        *  <p>
16534        *  
16535        *  <pre>
16536        *    // Deletes the clicked-on curve
16537        *    public void onClick(ClickEvent event) {
16538        *       GChart theGChart = (GChart) event.getSource();
16539        *       GChart.Curve c = theGChart.getTouchedCurve();
16540        *       if (null != c) {
16541        *          theGChart.removeCurve(c);
16542        *          // what you see in browser won't change without an update 
16543        *          theGChart.update(); 
16544        *       }
16545        *    }
16546        *  </pre> 
16547        *  <p>
16548        *  
16549        *  <li>The <tt>GChart</tt> methods <tt>getTouchedPoint</tt> and
16550        *  <tt>getTouchedCurve</tt> return either the point and
16551        *  curve that were clicked on, or <tt>null</tt> if the
16552        *  click didn't "touch" any points.
16553        *  
16554        *  <p>
16555        *  
16556        *</ol>
16557        * <p>
16558        *
16559        * The editable pie chart example on the GChart <a
16560        * href="http://gchart.googlecode.com/svn/trunk/live-demo/v2_6/com.googlecode.gchart.gchartdemoapp.GChartDemoApp/GChartDemoApp.html">
16561        * live demo page</a>
16562        * illustrates how to use this method to launch a popup modal
16563        * <tt>DialogBox</tt> whenever the user clicks on a point, and how to
16564        * change the selected point from within that dialog by using
16565        * GChart's <tt>touch</tt> method. 
16566        * <p>
16567        *
16568        * For a much simpler example that lets the user
16569        * delete points by clicking on them, see the Chart Gallery's  
16570        * <a href="package-summary.html#GChartExample18a">
16571        *  GChartExample18a</a>.
16572        * <p> 
16573        *
16574        * @param clickHandler the click handler that will handle
16575        *   click events on this chart.
16576        *
16577        * @return the handler's registration object. You need to retain a
16578        * reference to this registration object only if you may later need
16579        * to remove the handler (via the registration's
16580        * <tt>removeHandler</tt> method).  Most applications don't remove
16581        * handlers (handlers tend to be statically defined) and so can
16582        * ignore the value returned from this method.
16583        * 
16584        * @see #getTouchedPoint getTouchedPoint
16585        * @see #getTouchedCurve getTouchedCurve
16586        * @see #touch touch
16587        * @see #isUpdateNeeded isUpdateNeeded
16588        */ 
16589    
16590            public HandlerRegistration addClickHandler(ClickHandler clickHandler) {
16591                 HandlerRegistration result = 
16592                     addDomHandler(clickHandler, ClickEvent.getType());
16593                 return result;   
16594       }
16595       
16596    /**
16597     * Adds a new curve to the chart, at the end of the current
16598     * list of curves.
16599     * <p>
16600     *
16601     * @see #getCurve getCurve
16602     * @see #addCurve(int) addCurve(int)
16603     * @see #removeCurve removeCurve
16604     * @see #clearCurves clearCurves
16605     * @see #getNCurves getNCurves
16606     */
16607    
16608      public void addCurve() {
16609         addCurve(getNCurves());
16610      }
16611      /*
16612       * Given external, coded, index returns a curve's ArrayList index
16613       *
16614       * Basic order within the curves array is as follows:
16615       *
16616       *   o 6 decorative curves that hold title, etc 
16617       *   o "getNCurves()" user-created curves                    
16618       *   o 1 "Pop-up" hover annotation holding curve 
16619       *   o 1 Selection cursor holding curve          
16620       *
16621       * It's very important that the last two system curves come last, both
16622       * for performance (at the end means GChart's algorithms are able to
16623       * update only these curves when hover feedback changes) and to
16624       * ensure these elements are always on top of all other chart
16625       * elements, as required.
16626       * <p>
16627       *
16628       * The "external" system curve indexes are in a continuous range of
16629       * negative integers, which are mapped into the ArrayList
16630       * positions above via this code.
16631       *
16632       */ 
16633      int internalCurveIndex(int externalIndex) {
16634         int result;
16635         if (GChart.NAI == externalIndex)
16636    // -1 is the "no such curve" index used by an ArrayList
16637            result = -1; 
16638         else if (externalIndex < -N_POST_SYSTEM_CURVES)  
16639            // decoration related sys curves (before user's)
16640            result = externalIndex + N_SYSTEM_CURVES;
16641         else if (externalIndex < 0)
16642           // hover feedback related, after user curves (at the end)    
16643            result = curves.size()+externalIndex;
16644         else
16645           // + indexes mapped to ordinary user-created curves  
16646            result = externalIndex + N_PRE_SYSTEM_CURVES; 
16647         return result;
16648      }
16649    
16650      /* Given a curves ArrayList index returns external, coded, index
16651       *
16652       * Companion/inverse of preceeding method.
16653       *
16654       */ 
16655      int externalCurveIndex(int internalIndex) {
16656         int result;
16657         if (internalIndex < 0)
16658            result = GChart.NAI;
16659         else if (internalIndex < N_PRE_SYSTEM_CURVES)
16660            // one of the sys curves that comes before user's curves
16661            result = internalIndex - N_SYSTEM_CURVES;
16662         else if (internalIndex >= curves.size()-N_POST_SYSTEM_CURVES)
16663           // sys curves, like hover feedback, that come after user's
16664            result =  internalIndex - curves.size();
16665         else
16666           // ordinary user created curve 
16667            result = internalIndex - N_PRE_SYSTEM_CURVES;
16668         return result;
16669      }
16670      // does the external curve index represent a GChart-sys-defined curve?
16671      private boolean isSystemCurveIndex(int externalIndex) {
16672         boolean result = externalIndex < 0;
16673         return result;
16674      }
16675      /**
16676       * Adds a new curve to the chart, at the specified position
16677       * in the curve sequence. Existing curves at postions at
16678       * or greater than the specified position have their
16679       * positional indexes increased by 1.
16680       * <p>
16681       *
16682       * @see #getCurve getCurve
16683       * @see #addCurve() addCurve()
16684       * @see #removeCurve removeCurve
16685       * @see #clearCurves clearCurves
16686       * @see #getNCurves getNCurves
16687       */
16688    
16689    public void addCurve(int iCurve) {
16690      if (iCurve > getNCurves())
16691         throw new IllegalArgumentException(
16692           "iCurve = " + iCurve +"; iCurve may not exceed getNCurves() (" + getNCurves() + ")");
16693      else if (iCurve < 0)
16694        throw new IllegalArgumentException(
16695           "iCurve = " + iCurve +"; iCurve may not be negative.");
16696      int internalIndex = internalCurveIndex(iCurve);
16697      Curve c = new Curve(internalIndex);
16698      curves.add(internalIndex, c);
16699      // curves are initially added to the x, y axes.
16700      getXAxis().incrementCurves();
16701      getYAxis().incrementCurves();
16702      // adjust ArrayList indexes to account for newly added element
16703      for (int i = internalIndex+1; i < curves.size(); i++)
16704         curves.get(i).incrementIndex();
16705      if (0 != plotPanel.getRenderingPanelCount()) { // other panels are already there
16706         int rpIndex = getRenderingPanelIndex(internalIndex);
16707         plotPanel.addGraphicsRenderingPanel(rpIndex);
16708         plotPanel.addAnnotationRenderingPanel(rpIndex);
16709      }
16710      // otherwise, before 1st update: lazy-add panels when they're 1st used
16711      c.invalidate();
16712      if (getNCurves() > 0)
16713         setDefaultBorderColor(c, getNCurves()-1);
16714    
16715    }
16716    
16717      /**
16718       * Removes every curve this chart contains.
16719       * 
16720       * @see #getCurve getCurve
16721       * @see #addCurve() addCurve()
16722       * @see #addCurve(int) addCurve(int)
16723       * @see #removeCurve removeCurve
16724       * @see #getNCurves getNCurves
16725       */
16726    public void clearCurves() {
16727       for (int iCurve = getNCurves()-1; iCurve >= 0; iCurve--)
16728          removeCurve(iCurve);
16729    }
16730    
16731    /**
16732     * Convenience method that, given a plain text label, returns an
16733     * HTML snippet appropriate for use as an argument to the
16734     * <tt>setHovertextTemplate</tt> or <tt>setAnnotationText</tt>
16735     * methods, that will display the plain text label with
16736     * formatting appropriate for use with hovertext.
16737     * <p>
16738     * 
16739     *
16740     * When the string returned from this method is used as an
16741     * argument to <tt>setHovertextTemplate</tt>, the hovertext that
16742     * is generated is similar in format (light yellow background,
16743     * black 1px border) to what is produced via the
16744     * <tt>setTitle</tt> method (standard browser element hovertext).
16745     * <p>
16746     *
16747     * In detail, this method creates the returned string via the line:
16748     * <p>
16749     * 
16750     * <pre>
16751     * result =
16752         "&lt;html&gt;&lt;div style='background-color:#FFFFF0; border-color:black; border-style:solid; border-width:1px; padding:2px'&gt;"
16753         + plainTextLabel + "&lt;/div&gt;";
16754     * </pre>
16755     * 
16756     * <p>
16757     * This method is provided mainly to simplify transitioning from the
16758     * old hover feedback system to the new one introduced in v2.4.
16759     * <p>
16760     * 
16761     * It is expected that many pre v2.4 GChart applications will
16762     * find that simply wrapping a <tt>formatAsHovertext</tt> call
16763     * around existing hovertext templates will provide acceptable
16764     * (though somewhat different) hover feedback for existing
16765     * charts:
16766     *
16767     * <pre>
16768     *   getCurve().getSymbol().setHovertextTemplate(
16769     *     GChart.formatAsHovertext("(x,y) = (${x}, ${y})"));
16770     * </pre>
16771     * <p>
16772     * 
16773     * Note that GChart v2.4 automatically includes such a wrapping
16774     * <tt>formatAsHovertext</tt> call on all default hovertext
16775     * templates.
16776     * 
16777     * <p>
16778     *
16779     * See the {@link Symbol#setHoverAnnotationSymbolType
16780     * setHoverAnnotationSymbolType} method for a
16781     * code snippet that emulates not just the formatting,
16782     * but also the at-the-mouse positioning behaviour,
16783     * of setTitle-based hovertext.
16784     * 
16785     * <p>
16786     *
16787     *
16788     * @see Symbol#setHovertextTemplate setHovertextTemplate
16789     * @see Curve.Point#setAnnotationText setAnnotationText
16790     * @see Symbol#setHoverAnnotationSymbolType setHoverAnnotationSymbolType
16791     * 
16792     * @param plainTextLabel the plain text label that is to be
16793     *  HTML-wrapped to make it look like <tt>setTitle</tt>-based
16794     *  hovertext.
16795     *
16796     *
16797     */ 
16798    
16799    public static String formatAsHovertext(String plainTextLabel) {
16800       String result =
16801         "<html><div style='background-color:#FFFFF0; border-color:black; border-style:solid; border-width:1px 1px 1px 1px; padding:2px; text-align:left'>"
16802         + plainTextLabel + "</div>";
16803       return result;
16804    }
16805    
16806      /**
16807       ** Returns the background color of the chart as a whole.
16808       **
16809       ** @return the chart's background color, in a standard
16810       **   CSS color string format.
16811       **
16812       ** @see #setBackgroundColor(String) setBackgroundColor
16813       **
16814       **/ 
16815      public String getBackgroundColor() {
16816         return(backgroundColor);
16817      }
16818      /**
16819       ** Returns the color of the border around the chart as
16820       ** a whole.
16821       **
16822       ** @return the color of the chart's border, in a standard
16823       **   CSS color string format.
16824       **
16825       ** @see #setBorderColor(String) setBorderColor
16826       **
16827       **/ 
16828      public String getBorderColor() {
16829         return borderColor;
16830      }
16831    
16832      /**
16833       ** Returns the width of the border around the chart as a whole
16834       **  
16835       ** @return width of the border around the chart as a whole, as
16836       **   a CSS border width specification string (e.g. "1px").
16837       **
16838       ** @see #setBorderWidth(String) setBorderWidth
16839       **
16840       **/ 
16841      public String getBorderWidth() {
16842         return borderWidth;
16843      }
16844    
16845      /**
16846       ** Returns the style of the border around the chart as a whole
16847       **  
16848       ** @return cssStyle for the border around the chart as a whole
16849       **
16850       ** @see #setBorderStyle(String) setBorderStyle
16851       **
16852       **/ 
16853      public String getBorderStyle() {
16854         return borderStyle;
16855      }
16856    
16857      
16858         /** Returns the previously specified chart footnotes widget.
16859         *
16860         * @return widget representing chart's footnotes or <tt>null</tt> if none.
16861         *
16862         * @see #setChartFootnotes(Widget) setChartFootnotes(Widget)
16863         * @see #setChartFootnotes(String) setChartFootnotes(String)
16864         * @see #getChartTitle getChartTitle
16865         */ 
16866        public Widget getChartFootnotes() {
16867             return chartFootnotes;
16868         }
16869        /** Returns flag indicating if this chart's footnotes are
16870         *  left-justified or centered.
16871         *
16872         * @return true if footnotes are flush against the left edge
16873         * of the chart, false if they are horizontally centered
16874         * across the bottom edge of the chart.
16875         *
16876         * @see #setChartFootnotesLeftJustified setChartFootnotesLeftJustified
16877         * @see #setChartFootnotes(String) setChartFootnotes(String)
16878         * @see #setChartTitle setChartTitle
16879         */ 
16880        public boolean getChartFootnotesLeftJustified() {
16881             return chartFootnotesLeftJustified;
16882         }
16883        /** Returns the thickness (height) of the rectangular region
16884         ** at the bottom of the chart allocated for footnotes.
16885         ** <p>
16886         **
16887         ** The width of this region always equals the width of
16888         ** the entire GChart (including legend and axis labels).
16889         ** <p>
16890         **
16891         ** Your footnotes widget is always vertically centered
16892         ** in this region.
16893         ** <p>
16894         **
16895         ** 
16896         ** Your footnotes widget will either be horizontally
16897         ** centered in this region, or left justified in it,
16898         ** depending on the property defined by the
16899         ** <tt>setChartFootnotesLeftJustified</tt> method.
16900         ** 
16901         ** <p>
16902         ** 
16903         **
16904         ** This method always returns 0 if the footnotes widget
16905         ** is <tt>null</tt> (the default); the rectangular
16906         ** footnotes region is entirely eliminated in that case.
16907         ** <p>
16908         **
16909         ** @return the thickness (height) of the rectangular region
16910         ** at the bottom of the chart allocated for footnotes, in
16911         ** pixels.
16912         **
16913         ** @see #setChartFootnotesThickness(int) setChartFootnotesThickness
16914         ** @see #setChartFootnotesLeftJustified setChartFootnotesLeftJustified
16915         **/
16916         public int getChartFootnotesThickness() {
16917            int result = 0;
16918            final int EXTRA_HEIGHT = 3; // 1.5 lines padding above/below
16919            final int DEF_HEIGHT = 1;
16920            if (null == getChartFootnotes())
16921               result = 0;
16922            else if (GChart.NAI != footnotesThickness) 
16923               result = footnotesThickness;
16924            else if (getChartFootnotes() instanceof HasHTML)
16925               result = DEFAULT_FOOTNOTES_THICKNESS * (EXTRA_HEIGHT +
16926                 htmlHeight(((HasHTML) (getChartFootnotes())).getHTML()));
16927            else
16928               result = DEFAULT_FOOTNOTES_THICKNESS*
16929                        (DEF_HEIGHT + EXTRA_HEIGHT);
16930            return result;
16931         }
16932      /** Returns the previously specified widget representing the
16933       *  chart's title.
16934       *
16935       * @return widget representing chart's title or <tt>null</tt>
16936       * if none
16937       *
16938       * @see #setChartTitle(Widget) setChartTitle(Widget)
16939       * @see #setChartTitle(String) setChartTitle(String)
16940       * 
16941       */ 
16942        public Widget getChartTitle() {
16943           return chartTitle;
16944        }
16945    
16946         /**
16947          ** Returns the thickness (height) of the rectangular region at
16948          ** the top of the chart allocated for the title.
16949          ** <p>
16950          ** 
16951          ** This method always returns 0 if the title widget
16952          ** is <tt>null</tt> (the default); the rectangular
16953          ** title region is entirely eliminated in that case.
16954          ** <p>
16955          **
16956          ** Your title widget is always centered vertically and
16957          ** horizontally within this rectangular region.
16958          **
16959          **
16960          ** @return the thickness (height) of the rectangle
16961          ** that contains the chart's title, in pixels.
16962          **
16963          ** @see #setChartTitleThickness setChartTitleThickness
16964          **
16965          **/
16966         public int getChartTitleThickness() {
16967            int result = 0;
16968            final int EXTRA_HEIGHT = 3; // 1.5 lines above & below title
16969            final int DEF_HEIGHT = 1;
16970            if (null == getChartTitle())
16971               result = 0;
16972            else if (GChart.NAI != titleThickness) 
16973               result = titleThickness;
16974            else if (getChartTitle() instanceof HasHTML)
16975               result = DEFAULT_TITLE_THICKNESS * (EXTRA_HEIGHT +
16976                  htmlHeight(((HasHTML) (getChartTitle())).getHTML()));
16977            else
16978               result = DEFAULT_TITLE_THICKNESS*
16979                        (EXTRA_HEIGHT + DEF_HEIGHT);
16980            return result;
16981          }
16982    
16983        /**
16984         * Determines if this chart will clip any chart elements
16985         * that extend beyond the bounds of the decorated chart.
16986         * The decorated chart includes title, footnotes, etc.
16987         * as well as the plot area proper.
16988         *
16989         * @return true if off-the-decorated-chart elements are
16990         * clipped, false otherwise.
16991         *
16992         * @see #setClipToDecoratedChart setClipToDecoratedChart
16993         * @see #setClipToPlotArea setClipToPlotArea
16994         * @see #getXChartSizeDecorated getXChartSizeDecorated
16995         * @see #getYChartSizeDecorated getYChartSizeDecorated
16996         * 
16997         */
16998        public boolean getClipToDecoratedChart() {
16999           return clipToDecoratedChart;
17000        }
17001        
17002        /**
17003         * Returns true if graphical aspects of the
17004         * chart that fall outside of the plot area are being clipped
17005         * off, false otherwise.
17006         *
17007         * @return <tt>true</tt> if clipping to plot area, else
17008         * <tt>false</tt>.
17009         * 
17010         * @see #setClipToPlotArea setClipToPlotArea
17011         */
17012        
17013        public boolean getClipToPlotArea() {
17014           return clipToPlotArea;
17015        }
17016        
17017         // returns point closest to given plot-panel pixel coordinates
17018         Curve.Point getClosestBrushTouchingPointNoCheck(int x, int y) {
17019            
17020           Curve.Point result = null;
17021           // NAI means mouse is at some unknown, off-the-chart, position 
17022           if (x == GChart.NAI || y == GChart.NAI) return result; 
17023           double dBest = Double.MAX_VALUE; // dist. to closest symbol
17024           
17025           // fact that charts tend to have a small number of curves
17026           // allows us to use simple sequential search across curves
17027           int nCurves = getNCurves();
17028           for (int iCurve = 0; iCurve < nCurves; iCurve++) {
17029             Curve c = getSystemCurve(iCurve);  
17030             if (!c.isVisible()) continue;
17031             Symbol sym = c.getSymbol();
17032             if (!sym.getHoverAnnotationEnabled() &&
17033                 !sym.getHoverSelectionEnabled()) continue;
17034             SymbolType symType = sym.getSymbolType();
17035             boolean onY2 = c.onY2();
17036             int iClosest = c.getClosestTouchingPoint(x, y);
17037             if (GChart.NAI == iClosest) continue; // no hits on this curve
17038    
17039             double xPoint = symType.getCenterX(plotPanel, 
17040                                                sym, iClosest);
17041             double yPoint = symType.getCenterY(plotPanel, 
17042                                                sym, iClosest, onY2);
17043             double dx = sym.xScaleFactor*(x-xPoint);
17044             double dy = sym.yScaleFactor*(y-yPoint);
17045             // distance, squared, of mouse from symbol's "center"
17046             double d = dx*dx+dy*dy;
17047             if (d <= dBest) { // for ties, use later, "on top", point
17048                dBest = d;
17049                result = c.getPoint(iClosest);
17050             }
17051           }
17052           return result;
17053        }
17054        /**
17055         *
17056         * Returns the point that would be touched if the mouse were
17057         * moved to the given x,y plot-area pixel coordinates, or
17058         * <tt>null</tt> if the moving the mouse to these coordinates
17059         * would not have touched any points.<p>
17060         *
17061         * This method only works if the chart rendering is
17062         * up-to-date (if <tt>isUpdateNeeded</tt> returns
17063         * <tt>false</tt>). Otherwise, <tt>null</tt> is returned.
17064         * <p>
17065         *
17066         * <small> GChart's hit testing method works best if a
17067         * chart's points are approximately evenly distributed across
17068         * the plot area's x or y axis, across a small number of
17069         * curves. In particular, charts that have many points
17070         * bunched up into a small part of the plot area, or that
17071         * have many points completely outside of the plot area, or
17072         * that place each point into a separate curve, could
17073         * experience significantly worse that usual hit testing
17074         * performance. Though such cases are expected to be rare, in
17075         * the worst case, GChart could be reduced to a simple linear
17076         * search across all the chart's points during hit testing.
17077         * </small>
17078         * 
17079         * @param xPlotArea x-coordinate of trial mouse position, in
17080         * GChart's plot area pixel coordinates.
17081         * @param yPlotArea y-coordinate of trial mouse position, in
17082         * GChart's plot area pixel coordinates.
17083         *
17084         * @return reference to the point that would have been "touched"
17085         *   by the mouse, or <tt>null</tt> if positioning the mouse
17086         *   to these coordinates would not have touched any point.
17087         *   
17088         * @see Axis#getMouseCoordinate getMouseCoordinate
17089         * @see Axis#modelToPlotAreaPixel modelToPlotAreaPixel
17090         * @see #isUpdateNeeded isUpdateNeeded
17091         * @see #touch touch
17092         * 
17093         */
17094    
17095         public Curve.Point getClosestBrushTouchingPoint(int xPlotArea,
17096                                                         int yPlotArea) {
17097            Curve.Point result = null;
17098            if (!isUpdateNeeded()) {
17099               result = getClosestBrushTouchingPointNoCheck(xPlotArea, yPlotArea);
17100            }
17101            return result;
17102         }
17103         
17104    /** Convenience method equivalent to <tt>getCurve(getNCurves()-1)</tt>.
17105       *  <p>
17106       *  This method, when used in conjunction with no-arg <tt>addCurve</tt>,
17107       *  method, makes code blocks that create and define the
17108       *  properties of a chart's curves more readable/editable. For example:
17109          <pre>
17110          addCurve(); // add 1st curve
17111          getCurve().setYAxis(Y2_AXIS); // first setting for 1st curve
17112          //... other settings for first curve
17113          addCurve(); // add 2nd curve
17114          getCurve().setYAxis(Y_AXIS); // first setting for 2nd curve
17115          // ... other settings for 2nd curve    
17116          </pre>
17117       *<p> 
17118       * Note that using the no-arg methods in this way allows you to copy
17119       * entire groups of curve properties, unchanged, between such curve
17120       * related blocks.
17121       *
17122       *  @return the curve with the highest integer index. In other words,
17123       *    the curve with an index of <tt>getNCurves()-1</tt>.
17124       *
17125       *  @see #getCurve(int) getCurve(int)
17126       *  @see #getNCurves getNCurves
17127       *  @see #addCurve() addCurve()
17128       */
17129      public Curve getCurve() {
17130         int N = getNCurves();
17131         if (N < 1)
17132            throw new IllegalStateException(
17133    "You must add at least 1 curve before invoking getCurve()");
17134         Curve result = getSystemCurve(N-1);
17135         return result;
17136      }
17137    
17138         /**
17139         * Returns a reference to the curve at the specified
17140         * positional index.  Use the reference returned by this method to
17141         * modify properties of a curve (the symbol, data points, etc.)
17142         * 
17143         * <p>
17144         * @param iCurve index of the curve to be retrieved.
17145         * @return reference to the Curve at the specified position.
17146         *
17147         * @see #getCurve() getCurve()
17148         * @see #addCurve() addCurve()
17149         * @see #addCurve(int) addCurve(int)
17150         * @see #removeCurve removeCurve
17151         * @see #clearCurves clearCurves
17152         * @see #getNCurves getNCurves
17153         */
17154         public Curve getCurve(int iCurve) {
17155    
17156           if (iCurve >= getNCurves())
17157             throw new IllegalArgumentException(
17158                 "iCurve = " + iCurve +"; iCurve may not exceed getNCurves()-1 (" + (getNCurves()-1) + ")");
17159           else if (iCurve < 0)
17160               throw new IllegalArgumentException(
17161                 "iCurve = " + iCurve +"; iCurve may not be negative.");
17162                                                  
17163           Curve result = getSystemCurve(iCurve);
17164           return result;
17165         }
17166    
17167         // Version of getCurve that allows sys curve (negative id) access
17168         Curve getSystemCurve(int iCurve) {
17169            int internalIndex = internalCurveIndex(iCurve);
17170            Curve result = curves.get(internalIndex);
17171            return result;
17172         }
17173    
17174         /**
17175         * Returns the positional index (within this chart's list of
17176         * curves) of the specified curve.
17177         * <p>
17178         * 
17179         * Returns <i>GChart.NAI</i> if the specified curve is not found on
17180         * this GChart's curve list.
17181         * 
17182         * <p>
17183         * @param curve whose list position is to be retrieved
17184         * @return position of curve in GChart's curve list, or
17185         *        <i>GChart.NAI</i> if not on this chart's curve list.
17186         *
17187         * @see #getCurve() getCurve()
17188         * @see #getCurve(int) getCurve(int)
17189         * @see #addCurve() addCurve()
17190         * @see #addCurve(int) addCurve(int)
17191         * @see #removeCurve removeCurve
17192         * @see #clearCurves clearCurves
17193         * @see #getNCurves getNCurves
17194         */
17195         public int getCurveIndex(Curve curve) {
17196            int internalIndex = curve.getIndexOf();
17197            int result = externalCurveIndex(internalIndex);
17198            return result;
17199         }
17200         int getInternalCurveIndex(Curve curve) {
17201            int result = curve.getIndexOf();
17202            return result;
17203         }
17204         // maps all background curve indexes into first rendering panel
17205         int getRenderingPanelIndex(int internalCurveIndex) {
17206            int result = 0;
17207            if (N_PRE_SYSTEM_CURVES <= internalCurveIndex)
17208               result = internalCurveIndex - N_PRE_SYSTEM_CURVES + 1;        
17209            return result; 
17210         }
17211         
17212    
17213        /** Returns the font-family used in tick labels, point annotations,
17214         ** legends, and as the default in titles, footnotes, and
17215         ** axis labels.
17216         **
17217         ** @see #setFontFamily(String) setFontFamily
17218         **
17219         ** 
17220         **/
17221         public String getFontFamily() {
17222           return fontFamily;
17223         }
17224    
17225        /**
17226         ** Returns CSS color specification for all gridlines, axes,
17227         **   and tickmarks. 
17228         **
17229         ** @see #setGridColor setGridColor
17230         **
17231         ** @return the color, in CSS standard color format,
17232         **    used for all gridlines, axes, and tick marks.
17233         **
17234         **/
17235    
17236         public String getGridColor() {
17237           Curve cGridlines = getSystemCurve(XGRIDLINES_ID);
17238           String result = cGridlines.getSymbol().getBorderColor();
17239           return result; 
17240         }
17241    
17242         
17243      /**
17244       ** Returns the background color of the chart's legend.
17245       **
17246       ** @return the legend's background color, in a standard
17247       **   CSS color string format.
17248       **
17249       ** @see #setLegendBackgroundColor setLegendBackgroundColor
17250       **
17251       **/ 
17252      public String getLegendBackgroundColor() {
17253         return legendBackgroundColor;
17254      }
17255      /**
17256       ** Returns the border color of the chart's legend.
17257       **
17258       ** @return the color of the legend's border, in a standard
17259       **   CSS color string format, or else the special GChart keyword
17260       **   <tt>TRANSPARENT_BORDER_COLOR</tt>.
17261       **
17262       ** @see #setLegendBorderColor setLegendBordergroundColor
17263       ** @see #TRANSPARENT_BORDER_COLOR TRANSPARENT_BORDER_COLOR
17264       **
17265       **/ 
17266      public String getLegendBorderColor() {
17267         return legendBorderColor;
17268      }
17269    
17270      /**
17271       ** Returns the width of the chart's legend's border
17272       **  
17273       ** @return width of the legend's border, in pixels
17274       **
17275       ** @see #setLegendBorderWidth setLegendBorderWidth
17276       **
17277       **/ 
17278      public int getLegendBorderWidth() {
17279         return legendBorderWidth;
17280      }
17281    
17282      /**
17283       ** Returns the style of the chart's legend's border
17284       **  
17285       ** @return cssStyle of the legend's border
17286       **
17287       ** @see #setLegendBorderStyle setLegendBorderStyle
17288       **
17289       **/ 
17290      public String getLegendBorderStyle() {
17291         return legendBorderStyle;
17292      }
17293      
17294         /**
17295          ** Returns the color of the font used to display the labels
17296          **   within the legend (chart key)
17297          **   
17298          ** @return CSS color string defining the legend text's color
17299          **
17300          ** @see #setLegendFontColor setLegendFontColor
17301          **/ 
17302         public String getLegendFontColor() {
17303            return legendFontColor;
17304         }
17305      /**
17306       * Returns the CSS font size, in pixels, of text displayed
17307       * in the chart's legend (also know as a chart's key).
17308       * 
17309       * @return the (previously specified) font size of legend text
17310       *
17311       * @see #setLegendFontSize setLegendFontSize
17312       */
17313      public int getLegendFontSize() {
17314         return legendFontSize;
17315      }
17316         /**
17317          ** Returns the font-style in which this GChart's legend text
17318          ** will be rendered. 
17319          **
17320          ** @return font-style of legend text (italic, normal, etc.)
17321          **
17322          ** @see #setLegendFontStyle setLegendFontStyle
17323          **/ 
17324         public String getLegendFontStyle() {
17325            return legendFontStyle;
17326         }
17327         /**
17328          ** Returns true if legend text will be rendered in a bold,
17329          ** or false if in normal, weight font. 
17330          **
17331          ** @return if the legend's text is in bold or not.
17332          **
17333          ** @see #setLegendFontWeight setLegendFontWeight
17334          **/ 
17335         public String getLegendFontWeight() {
17336            return legendFontWeight;
17337         }
17338         /**
17339          ** Returns the thickness (width) of the rectangular region
17340          ** to the right of the y2-axis label allocated for the
17341          ** chart legend.<p>
17342          **
17343          ** The region extends vertically in parallel with the
17344          ** right edge of the plot area. The legend is always
17345          ** centered vertically and horizontally within this
17346          ** rectangular region.
17347          ** <p>
17348          **
17349          ** This method always returns 0 if the legend is not
17350          ** visible; the rectangular legend region is entirely
17351          ** eliminated in that case.
17352          **
17353          ** @return thickness (width) of legend key holding region,
17354          ** in pixels.
17355          **
17356          ** @see #setLegendThickness setLegendThickness
17357          **/ 
17358         public int getLegendThickness() {
17359            int result = 0;
17360            if (isLegendVisible() &&
17361                    0 < getNVisibleCurvesOnLegend()) {
17362               if (GChart.NAI == legendThickness)
17363                  result = getDefaultLegendThickness();
17364               else
17365                  result = legendThickness;
17366            }
17367            
17368            return result;
17369    
17370         }
17371      /**
17372       * Returns the number of curves on this chart. 
17373       *
17374       * @return the number of curves on this chart
17375       * 
17376       * @see #getCurve getCurve
17377       * @see #addCurve() addCurve()
17378       * @see #addCurve(int) addCurve(int)
17379       * @see #removeCurve removeCurve
17380       * @see #clearCurves clearCurves
17381       */ 
17382      public int getNCurves() {
17383         return curves.size() - N_SYSTEM_CURVES;
17384      }
17385      /** Returns the CSS string that specifies the width of the
17386       ** padding between the chart and it's external border
17387       ** <p>
17388       **
17389       ** @return the CSS string that defines the CSS padding property
17390       **   for the GChart as a whole.
17391       **
17392       ** @see #setPadding(String) setPadding
17393       **
17394       **/ 
17395      public String getPadding() {
17396        return padding;
17397      }
17398      
17399         /**
17400          ** Returns the background color of the area of the chart
17401          ** in which symbols representing curve data are displayed
17402          **   
17403          ** @return CSS color string defining the plot area's background
17404          **    color
17405          **
17406          ** @see #setPlotAreaBackgroundColor setPlotAreaBackgroundColor
17407          **/ 
17408         public String getPlotAreaBackgroundColor() {
17409            Curve c = getSystemCurve(PLOTAREA_ID);
17410            String result = c.getSymbol().getBackgroundColor();
17411            return result;
17412         }
17413    
17414         /**
17415          ** Returns the border color of the area of the chart
17416          ** in which symbols representing curve data are displayed
17417          **   
17418          ** @return CSS color string defining the color of the plot
17419          **    area's border
17420          **
17421          ** @see #setPlotAreaBorderColor setPlotAreaBorderColor
17422          **/ 
17423         public String getPlotAreaBorderColor() {
17424            Curve c = getSystemCurve(PLOTAREA_ID);
17425            String result = c.getSymbol().getBorderColor();
17426            return result;
17427        }
17428         /**
17429          ** Returns the width of the border around the area of the
17430          ** chart in which symbols representing curve data are
17431          ** displayed.
17432          **   
17433          ** @return width, in pixels, of the border around the plot area
17434          **
17435          ** @see #setPlotAreaBorderWidth setPlotAreaBorderWidth
17436          **/ 
17437         public int getPlotAreaBorderWidth() {
17438            Curve c = getSystemCurve(PLOTAREA_ID);
17439            int result = c.getSymbol().getBorderWidth();
17440            return result;
17441         }
17442    
17443         /**
17444          ** Returns the style of the border around the area of the
17445          ** chart in which symbols representing curve data are
17446          ** displayed (the so-called plot area).
17447          **   
17448          ** @return CSS style of the border around the plot area
17449          **
17450          ** @see #setPlotAreaBorderStyle setPlotAreaBorderStyle
17451          **/ 
17452         public String getPlotAreaBorderStyle() {
17453            Curve c = getSystemCurve(PLOTAREA_ID);
17454            String result = c.getSymbol().getBorderStyle();
17455            return result;
17456         }
17457         /**
17458          *
17459          * Returns the image URL that will be used to define the
17460          * plot area's background the next time <tt>update</tt> is called.
17461          * <p>
17462          *
17463          * @return url of image to be used as the background of the plot
17464          * area the next time that <tt>update</tt> is called.
17465          *
17466          * @see #setPlotAreaImageURL setPlotAreaImageURL
17467          * @see #update update
17468          * 
17469          */ 
17470         public String getPlotAreaImageURL() {
17471            Curve c = getSystemCurve(PLOTAREA_ID);
17472            String result = c.getSymbol().getImageURL();
17473            return result;
17474         }
17475         /**
17476          *
17477          * Returns a flag that tells if GChart is configured to
17478          * perform updates so that the chart uses less memory.
17479          *
17480          * @return <tt>true</tt> if GChart optimizes updates to
17481          * save memory, <tt>false</tt> (the default) if it optimizes
17482          * them to save time.
17483          *
17484          * @see #setOptimizeForMemory setOptimizeForMemory
17485          * 
17486          **/
17487         public boolean getOptimizeForMemory() {
17488            return optimizeForMemory;
17489         }
17490         
17491        
17492        /**
17493         * @deprecated
17494         *
17495         * Equivalent to <tt>!getClipToPlotArea()</tt>. Use that
17496         * method instead.
17497         *
17498         * @see #getClipToPlotArea getClipToPlotArea
17499         */
17500        
17501        public boolean getShowOffChartPoints() {
17502           return !getClipToPlotArea();
17503        }
17504    
17505    
17506        /** @deprecated
17507         **
17508         ** Equivalent to <tt>!getClipToDecoratedChart()</tt>. Use
17509         ** that method instead.
17510         **
17511         ** @see #getClipToDecoratedChart getClipToDecoratedChart
17512         **
17513         **/
17514        public boolean getShowOffDecoratedChartGlyphs() {
17515           return !getClipToDecoratedChart();
17516        }
17517    
17518        
17519        /**
17520       ** Returns a URL that points to a 1 x 1 pixel blank image
17521       ** file GChart requires to render its charts without
17522       ** producing missing image icons.
17523       **
17524       ** <p>
17525       ** 
17526       ** @return the URL of the file GChart needs to prevent
17527       ** missing image icons from appearing on your chart.
17528       **
17529       ** @see #setBlankImageURL setBlankImageURL
17530       **
17531       **/
17532    
17533         public static String getBlankImageURL() {
17534            return null == blankImageURL ? DEFAULT_BLANK_IMAGE_URL_FULLPATH :
17535                  blankImageURL;
17536         }
17537    
17538         private HoverParameterInterpreter hoverParameterInterpreter = null;
17539         /**
17540          * Returns this GChart's hover parameter interpreter.
17541          *
17542          * @see #setHoverParameterInterpreter setHoverParameterInterpreter
17543          *
17544          * @return the hover parameter interpreter used by this
17545          * GChart, or <tt>null</tt> if none.
17546          * 
17547          */ 
17548         public HoverParameterInterpreter getHoverParameterInterpreter() {
17549            return hoverParameterInterpreter; 
17550         }
17551    
17552         private boolean hoverTouchingEnabled = true;
17553    
17554         /**
17555          * Is it possible to select points and have their hover
17556          * annotations pop up, merely by "touching" them with
17557          * the mouse-attached "brush"?
17558          *
17559          * @return true (the default) if just hovering over a point can
17560          * select it, false if you must click on a point to select it.
17561          *
17562          * @see #setHoverTouchingEnabled setHoverTouchingEnabled
17563          *
17564          */
17565         public boolean getHoverTouchingEnabled() {
17566            return hoverTouchingEnabled;
17567         }
17568        /**
17569         * Returns the x-axis associated with this chart. Use the
17570         * returned reference to manipulate axis min and max,
17571         * number of ticks, tick positions, tick label formats, etc.
17572         * <p>
17573         * @return object representing the x-axis of this chart.
17574         *
17575         * @see #getYAxis getYAxis
17576         * @see #getY2Axis getY2Axis
17577         */
17578        public Axis getXAxis() {
17579           return xAxis;
17580        }
17581    
17582        /**
17583         * Returns the number of x-pixels in the region of the chart
17584         * used for curve display purposes.
17585         *
17586         * @return the number of x-pixels available for curve display.
17587         *
17588         * @see #setXChartSize setXChartSize
17589         * 
17590         */
17591        public int getXChartSize() {
17592           return xChartSize;
17593        }
17594    
17595        /**
17596         * Returns the number of x-pixels reserved for the chart as a
17597         * whole, including space reserved for decorations (title,
17598         * footnotes, axis labels, ticks, tick labels, legend key,
17599         * etc.).
17600         * <p>
17601         *
17602         * The returned size does not include the border or padding
17603         * around the chart as a whole. <p>
17604         *
17605         * You cannot directly set the decorated x chart size.
17606         * Instead, you must set the width of the plot area, and the
17607         * thicknesses of certain of the decoration-holding regions
17608         * (using methods linked to below) that, summed together,
17609         * define the total width of the chart.
17610         *
17611         * @return the width of the entire chart, in pixels.
17612         *
17613         * @see #setXChartSize setXChartSize
17614         * @see #getYChartSizeDecorated getYChartSizeDecorated
17615         * @see Axis#setAxisLabelThickness setAxisLabelThickness
17616         * @see Axis#setTickLabelThickness setTickLabelThickness
17617         * @see Axis#setTickLength setTickLength
17618         * @see Axis#setTickLocation setTickLocation
17619         * @see Axis#setTickLabelPadding setTickLabelPadding
17620         * @see Axis#setLegendThickness setLegendThickness
17621         * 
17622         */
17623        public int getXChartSizeDecorated() {
17624           int result = getXChartSize() +
17625                        getYAxis().getAxisLabelThickness() +
17626                        getYAxis().getTickLabelThickness() +
17627                        getYAxis().getTickSpace() +
17628                        getYAxis().getTickLabelPadding() +
17629                        getY2Axis().getAxisLabelThickness() +
17630                        getY2Axis().getTickLabelThickness() +
17631                        getY2Axis().getTickSpace() +
17632                        getYAxis().getTickLabelPadding() +
17633                        getLegendThickness();
17634           return result;
17635        }
17636    
17637    
17638        
17639        
17640        /**
17641         * Returns the y2-axis (right y axis) associated with this
17642         * chart. Use the returned reference to manipulate axis
17643         * min and max, number of ticks, tick positions, tick
17644         * label formats, etc.
17645         * 
17646         * <p>
17647         * @return object representing the y2-axis of this chart.
17648         *
17649         * @see #getYAxis getYAxis
17650         * @see #getXAxis getXAxis
17651         */
17652        public Axis getY2Axis() {
17653           return y2Axis;
17654        }
17655        /**
17656         * Returns the (left) y-axis associated with this chart. Use the
17657         * returned reference to manipulate axis min and max,
17658         * number of ticks, tick positions, tick label formats, etc.
17659         * <p>
17660         * @return object representing the y-axis of this chart.
17661         *
17662         * @see #getXAxis getXAxis
17663         * @see #getY2Axis getY2Axis
17664         */
17665        public Axis getYAxis() {
17666           return yAxis;
17667        }
17668        /**
17669         * Returns the number of y-pixels in the region of the chart
17670         * used for curve display purposes.
17671         *
17672         * @return the number of y-pixels available for curve display.
17673         *
17674         * @see #setYChartSize setYChartSize
17675         * 
17676         */
17677        public int getYChartSize() {
17678           return yChartSize;
17679        }
17680    
17681        /**
17682         * Returns the number of y-pixels reserved for the chart as a
17683         * whole, including space reserved for decorations (title,
17684         * footnotes, axis labels, ticks, tick labels, etc.).  <p>
17685         *
17686         * The returned size does not include the border or padding
17687         * around the chart as a whole. <p>
17688         *
17689         * You cannot directly set the decorated y chart size.
17690         * Instead, you must set sizes and thicknesses of the
17691         * plot area and certain of the decoration-holding regions
17692         * (using the methods linked-to below) that, when summed
17693         * together, define the height of the decorated chart.
17694         *
17695         * @return the height of the entire chart, in pixels.
17696         *
17697         * @see #setYChartSize setYChartSize
17698         * @see #getXChartSizeDecorated getXChartSizeDecorated
17699         * @see Axis#setAxisLabelThickness setAxisLabelThickness
17700         * @see Axis#setTickLabelThickness setTickLabelThickness
17701         * @see Axis#setTickLength setTickLength
17702         * @see Axis#setTickLocation setTickLocation
17703         * @see Axis#setTickLabelPadding setTickLabelPadding
17704         * @see #setChartTitleThickness setChartTitleThickness
17705         * @see #setChartFootnotesThickness setChartFootnotesThickness
17706         * 
17707         */
17708        public int getYChartSizeDecorated() {
17709           int result = getYChartSize() +
17710                        getXAxis().getAxisLabelThickness() +
17711                        getXAxis().getTickLabelThickness() +
17712                        getXAxis().getTickSpace() +
17713                        getXAxis().getTickLabelPadding() +
17714                        getChartTitleThickness() +
17715                        getChartFootnotesThickness();
17716           
17717           return result;
17718        }
17719    
17720    
17721    
17722        
17723         /**
17724         * Determines if this chart has a "y2" (right) y-axis.
17725         * <p>
17726         * Only charts that have at least one curve on the right
17727         * y axis will have a y2-axis.
17728         * 
17729         * @return true if the chart has a second y axis, false otherwise.
17730         *
17731         * @see Curve#setYAxis Curve.setYAxis
17732         */
17733        public boolean hasY2Axis() {
17734           boolean result = getY2Axis().getNCurvesVisibleOnAxis() > 0;
17735           return result;
17736        }
17737        /**
17738         * Determines if this chart has an ordinary, or left, y-axis.
17739         * <p>
17740         * Only charts that have at least one curve on the left
17741         * y axis will have a y-axis.
17742         * 
17743         * @return true if the chart has a left y axis, false otherwise
17744         * 
17745         * @see Curve#setYAxis Curve.setYAxis
17746         * 
17747         */
17748        public boolean hasYAxis() {
17749           boolean result = getYAxis().getNCurvesVisibleOnAxis() > 0;
17750           return result;
17751        }
17752        /**
17753         * Determines if the legend of this chart is visible.
17754         *
17755         *
17756         * @return true if the legend is visible, false otherwise.
17757         *
17758         * @see #setLegendVisible setLegendVisible
17759         */ 
17760        public boolean isLegendVisible() {return isLegendVisible;}
17761    
17762    
17763        /**
17764         *
17765         * Is the in-browser rendition of the chart inconsistent with
17766         * the current chart specs? In other words, is a call to
17767         * GChart's <tt>update</tt> method needed to bring the
17768         * browser's display into agreement with current chart specs?
17769         * <p>
17770         *
17771         * <i>Note:</i> Whenever this method returns
17772         * <tt>true</tt>, GChart "freezes" hover feedback, and
17773         * can no longer actively track the currently "touched"
17774         * point.  This is because GChart, to simplify its
17775         * bookkeeping, assumes in-browser (DOM) rendering and
17776         * current chart specs are in synch when determining the
17777         * point selection consequences of mouse events over the
17778         * chart.
17779         * 
17780         * @return true if a call to <tt>update</tt> is needed to
17781         * bring current chart specifications and browser-rendered
17782         * representation into synch, false otherwise.
17783         *
17784         * @see #update update 
17785         * @see #getTouchedPoint getTouchedPoint
17786         * 
17787         */
17788        public boolean isUpdateNeeded() {
17789          boolean result = chartDecorationsChanged || !plotPanel.isValidated();
17790          return result;
17791        }
17792    
17793       /**
17794         * Removes the curve at the specified positional index.
17795         * <p>
17796         * 
17797         * @param iCurve index of the curve to be removed
17798         * 
17799         * @see #removeCurve(Curve) removeCurve(Curve)
17800         * @see #getCurve getCurve
17801         * @see #addCurve() addCurve()
17802         * @see #addCurve(int) addCurve(int)
17803         * @see #clearCurves clearCurves
17804         * @see #getNCurves getNCurves
17805         */
17806       public void removeCurve(int iCurve) {
17807         if (iCurve >= getNCurves())
17808             throw new IllegalArgumentException(
17809                 "iCurve = " + iCurve +"; iCurve may not exceed getNCurves()-1 (" + (getNCurves()-1) + ")");
17810         else if (iCurve < 0)
17811           throw new IllegalArgumentException(
17812             "iCurve = " + iCurve +"; iCurve may not be negative.");
17813    
17814         invalidateDependentSlices(iCurve);
17815    
17816         /*
17817          * Simulate user moving away from point before it is deleted (this
17818          * assures that any required hoverCleanup gets called, and clears
17819          * the otherwise dangling reference to the touched point).
17820          * 
17821          */ 
17822         if (plotPanel.touchedPoint != null &&
17823             plotPanel.touchedPoint.getParent() == getSystemCurve(iCurve))
17824            plotPanel.touch(null);
17825    
17826         // remove the rendering panel that corresponds to this curve
17827         // (must keep the two lists in synch or 1-to-1 mapping breaks)
17828         int internalIndex = internalCurveIndex(iCurve);
17829         if (0 != plotPanel.getRenderingPanelCount()) {
17830           int rpIndex = getRenderingPanelIndex(internalIndex);
17831           plotPanel.removeGraphicsRenderingPanel(rpIndex);
17832           plotPanel.removeAnnotationRenderingPanel(rpIndex);
17833         }
17834         
17835         Curve c = curves.get(internalIndex);
17836         if (c.isVisible()) {
17837           getXAxis().decrementCurves();
17838           if (c.getYAxis() == Y_AXIS)
17839             getYAxis().decrementCurves();
17840           else
17841             getY2Axis().decrementCurves();
17842         }
17843         c.clearIndex(); 
17844         // else before 1st update, no rendering panels created yet
17845         curves.remove(internalIndex);
17846      // adjust ArrayList indexes to account for newly removed element
17847         for (int i = internalIndex; i < curves.size(); i++)
17848            curves.get(i).decrementIndex();
17849       }
17850    
17851    
17852       /**
17853        * Removes the given curve from this GChart.
17854        * <p>
17855        *
17856        * If the given curve is <tt>null</tt> or is not a curve on this GChart,
17857        * an exception is thrown.
17858        * 
17859        * <p>
17860        *
17861        * @param curve the curve to be removed.
17862        *
17863        * @see #removeCurve(int) removeCurve(int)
17864        * 
17865        */
17866       
17867       public void removeCurve(Curve curve) {
17868         if (null == curve)
17869           throw new IllegalArgumentException("Curve cannot be null.");    
17870         int index = getCurveIndex(curve);
17871         if (index == GChart.NAI) 
17872            throw new IllegalArgumentException("Curve is not one of this GChart's curves.");
17873            
17874         if (index < 0)
17875            throw new IllegalArgumentException("System curves cannot be removed (this should be impossible; a GChart bug is likely.)");
17876          else
17877            removeCurve(index);
17878       }
17879         
17880      /**
17881       ** Specifies the background color of the chart as a whole.
17882       ** 
17883       ** <p>
17884       ** The default background color is <tt>USE_CSS</tt>.
17885       ** <p>
17886       ** 
17887       ** For more information on standard CSS color
17888       ** specifications see the discussion in
17889       ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
17890       ** <p>
17891       **        
17892       ** @param cssColor the chart's background color, in a standard
17893       **   CSS color string format. 
17894       **
17895       **
17896       ** @see #USE_CSS USE_CSS
17897       ** 
17898       **/ 
17899      public void setBackgroundColor(String cssColor) {
17900         chartDecorationsChanged = true;
17901         backgroundColor = cssColor;
17902      }
17903    
17904        /**
17905         ** Specifies a URL that points to the transparent, 1 x 1 pixel,
17906         ** "blank GIF" that GChart uses in order to render your
17907         ** chart without adding spurious "missing image" icons to it.
17908         ** <p>
17909         **
17910         ** When GWT compiles an application that imports the GChart
17911         ** library, it automatically adds an appropriate blank
17912         ** image, <tt>gchart.gif</tt>, to the module base directory
17913         ** (this is the directory into which GWT also copies your
17914         ** compiled Javascript, all the files in your public
17915         ** directory, etc.).  <p>
17916         **
17917         ** By default, GChart uses the following blank image URL:
17918         ** <p>
17919         **
17920         ** <pre>
17921         **   GWT.getModuleBaseURL() + "gchart.gif"
17922         ** </pre>
17923         ** <p>
17924         ** 
17925         ** <small> Earlier versions used "gchart.gif" as this default url.
17926         ** <a href="http://groups.google.com/group/Google-Web-Toolkit/msg/4be3f19dc14f958a">
17927         ** This GWT forum post by Dean S. Jones</a> identified the
17928         ** need to add the <tt>GWT.getModuleBaseURL()</tt> prefix.
17929         ** </small>
17930         ** <p>
17931         ** 
17932         ** Note that this default adds a potentially very
17933         ** long URL to every <tt>img</tt> element added by GChart to
17934         ** render your chart, which can (in theory) more than double
17935         ** the memory required to represent your chart in the
17936         ** browser, because the absolute URLs can be of undetermined
17937         ** length.  In practice, browser memory usage increases of
17938         ** 10% have been observed with the on-line demo GChart and a
17939         ** typicial, 60-odd character absolute URL.  <p>
17940         **
17941         ** You have several alternatives to the above default that can
17942         ** often reduce the length of the URL and thus save browser
17943         ** memory:
17944         ** 
17945         ** <p>
17946         **
17947         ** <ol> <li>Simply copy <tt>gchart.gif</tt> from the module
17948         **   base directory into your host page's base directory, and
17949         **   then use <tt>setBlankImageURL("gchart.gif")</tt> to access
17950         **   this URL relatively.
17951         **   
17952         **   <li>If the relative path from the host page base
17953         **       directory to the module base directory is
17954         **       reasonably short, pass that alternative
17955         **       relative URL to this method (note that all
17956         **       relative URLs are interpreted relative to the base
17957         **       directory of the host page containing your GChart).
17958         **
17959         **   <li>Place a copy of <tt>gchart.gif</tt> into
17960         **       a directory whose absolute URL is very short,
17961         **       and then pass that short absolute URL to this method.
17962         **
17963         ** </ol>    
17964         ** <p>
17965         **
17966         ** <small> <i>Special note to anyone reading
17967         ** this who designed HTML's <tt>image</tt> tag:</i> If you
17968         ** had provided a <tt>src=none</tt> option, this method
17969         ** would not have to exist.
17970         ** </small>
17971         ** <p>
17972         **
17973         ** <i>Tip:</i> If you already have an appropriate blank
17974         ** gif on your site that is accessible from the host
17975         ** page via a reasonably short URL there is no need to
17976         ** copy <tt>gchart.gif</tt>. You can just pass that URL
17977         ** to this method.
17978         **
17979         ** <p>
17980         **
17981         ** <i>Note:</i> Though GChart uses this blank image by default,
17982         ** you can use the <tt>setImageURL</tt> method to specify a
17983         ** non-blank image for use in rendering a specific curve.
17984         ** <p>
17985         ** 
17986         ** 
17987         ** @param blankImageURL a URL that points to a 1 x 1 pixel
17988         ** transparent image that GChart needs to render your
17989         ** charts without adding a spurious "missing image" icon.
17990         **
17991         ** @see #getBlankImageURL getBlankImageURL
17992         ** @see #DEFAULT_BLANK_IMAGE_URL DEFAULT_BLANK_IMAGE_URL
17993         ** @see Symbol#setImageURL setImageURL
17994         ** 
17995         **/
17996    
17997        public static void setBlankImageURL(
17998           String blankImageURL) {
17999           if (blankImageURL != GChart.blankImageURL) {
18000              GChart.blankImageURL = blankImageURL;
18001    // Decided not to prefetch blank image URL because 1) pre-fetching
18002    // doesn't improve performance noticably in tested browsers, 
18003    // 2) there are reports of possible memory leaks associated with
18004    // its use in the GWT issue tracker, and 3) users can
18005    // easily do the prefetch on their own if they want to, and that
18006    // is really the right place to do a prefetch anyway.
18007    //          Image.prefetch(GChart.getBlankImageURL());
18008           }
18009        }
18010    
18011        /**
18012         * Defines this GChart's hover parameter interpreter.
18013         * <p>
18014         *
18015         * Hovertext template strings can include <tt>${</tt>...
18016         * <tt>}</tt> bracketed
18017         * references to built-in parameters such as <tt>${x}</tt>
18018         * and <tt>${y}</tt> that get get replaced with appropriate
18019         * string representations of the x or y values of the
18020         * hovered-over point in displayed hovertext. You can add
18021         * new, custom, named parameters, and/or redefine the
18022         * meaning of built-in parameters, by passing a hover parameter
18023         * interpreter to this method.
18024         * <p>
18025         * 
18026         * For sample code that shows you how to define a hover
18027         * parameter interpreter, see <tt>HoverParameterInterpreter</tt>. 
18028         *      
18029         * @see HoverParameterInterpreter HoverParameterInterpreter
18030         * @see Symbol#setHovertextTemplate setHovertextTemplate
18031         *
18032         * @param hpi the hover parameter interpreter to use with all
18033         * hovertext templates on this GChart (this interpreter is
18034         * responsible for replacing <tt>${</tt>...<tt>}</tt>
18035         * bracketed embedded parameter names in the hover text
18036         * template with appropriate HTML snippets representing the
18037         * value of that parameter at the hovered-over point).
18038         *
18039         */
18040        public void setHoverParameterInterpreter(HoverParameterInterpreter hpi) {
18041           hoverParameterInterpreter = hpi;
18042        }
18043    
18044        /**
18045         * Specifies if merely hovering over a point is sufficient to select
18046         * it and display its hover annotation (<tt>true</tt>), or if an
18047         * actual click is needed (<tt>false</tt>).  <p>
18048         *
18049         * With the default of <tt>true</tt>, points are auto-selected as
18050         * the user "touches" them with the mouse-attached "brush"--no
18051         * clicking is required.  <p>
18052         *
18053         * When hover touching is disabled, a GChart can be used in a manner
18054         * analogous to a single-selection (sorry there's no multi-selection
18055         * capability) listbox, with its click-selectable points playing the
18056         * role of the selectable list items.  Specifically, disabling hover
18057         * touching lets you move the mouse freely without any danger of
18058         * changing the selected point--the point even remains selected if
18059         * the mouse moves entirely off the chart. This is helpful when your
18060         * application follows the common pattern of "select the thing you
18061         * want to operate on, then issue a command that operates on that
18062         * thing". This option is also helpful if you use very
18063         * compute-intensive hover widgets, or if you simply prefer 
18064         * explictly-clicked-open/closed pop-up annotations.<p>
18065         *
18066         * <small> <i>How to Stop Leaky Clicks:</i> In IE7 and the hosted
18067         * mode browser, clicking ahead on a <tt>Button</tt> widget "leaks"
18068         * clicks upwards to the enclosing parent, even if you call
18069         * <tt>event.cancelBubble(true)</tt>. Such "leaky clicks" can
18070         * inappropriately change the selected point, when you really just
18071         * wanted to operate on it. This does not happen in Firefox 2, 3, or
18072         * Chrome, whose buttons properly "eat" the clicks--even when they
18073         * come in fast. To workaround the problem, you can place the
18074         * buttons into a hover widget (as shown in
18075         * <tt>GChartExample21.java</tt> in the chart gallery). This works
18076         * because GChart applies checks that ignore any mouse events that
18077         * occur within the rectangular region associated with the opened
18078         * hover widget.  </small> <p>
18079         * 
18080         * For an example that uses <tt>setHoverTouchingEnabled(false)</tt>
18081         * to allow the user to change the y-value of the selected point,
18082         * see the Chart Gallery's <a
18083         * href="package-summary.html#GChartExample21"> GChartExample21</a>.
18084         * 
18085         *
18086         * @param hoverTouchingEnabled <tt>true</tt> (the default) if you
18087         *   want users to be able to select points simply by hovering over
18088         *   them with their mouse, <tt>false</tt> if you want to
18089         *   require that they actually click on points to select them.
18090         *
18091         * @see #getHoverTouchingEnabled getHoverTouchingEnabled
18092         * @see Symbol#setBrushHeight setBrushHeight
18093         * @see #touch touch
18094         * @see #update update
18095         * @see HoverUpdateable HoverUpdateable
18096         * 
18097         */
18098        public void setHoverTouchingEnabled(boolean hoverTouchingEnabled) {
18099           this.hoverTouchingEnabled = hoverTouchingEnabled;
18100        }
18101      /**
18102       ** Specifies the color of the border around the chart as
18103       ** a whole.
18104       **
18105       ** <p>
18106       ** The default border color is <tt>USE_CSS</tt>.
18107       ** 
18108       ** <p>
18109       ** <blockquote><small>
18110       ** <i>Tip:</i> No border will appear if either <tt>borderStyle</tt>
18111       ** is <tt>none</tt>, <tt>borderWidth</tt> is <tt>0px</tt> or
18112       ** <tt>borderColor</tt> is <tt>transparent</tt>. Since
18113       ** these will often be the "CSS inherited" values,
18114       ** generally, it's best to set all three properties
18115       ** whenever you set any one of them.
18116       ** </small></blockquote>
18117       ** <p>
18118       ** 
18119       ** 
18120       ** For more information on standard CSS color
18121       ** specifications see the discussion in
18122       ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
18123       ** <p>
18124       **
18125       ** @param cssColor the color of the chart's border, in a standard
18126       **   CSS color string format.
18127       **
18128       ** @see #setBorderWidth(String) setBorderWidth
18129       ** @see #setBorderStyle(String) setBorderStyle
18130       ** @see #getBorderColor getBorderColor
18131       ** @see #USE_CSS USE_CSS
18132       **
18133       **/ 
18134      public void setBorderColor(String cssColor) {
18135         chartDecorationsChanged = true;
18136         if (borderColor == null ||
18137             borderColor == TRANSPARENT_BORDER_COLOR)
18138            throw new IllegalArgumentException(
18139      "null and TRANSPARENT_BORDER_COLOR are not allowed. This method requires a valid CSS color specification String.");
18140         borderColor = cssColor;
18141      }
18142    
18143      /**
18144       ** Sets style of the border around the chart as a whole.
18145       **
18146       ** <p>
18147       ** The default border style is <tt>USE_CSS</tt>.
18148       ** <p>
18149       ** 
18150       ** <p>
18151       ** <blockquote><small>
18152       ** <i>Tip:</i> No border will appear if either <tt>borderStyle</tt>
18153       ** is <tt>none</tt>, <tt>borderWidth</tt> is <tt>0px</tt> or
18154       ** <tt>borderColor</tt> is <tt>transparent</tt>. Since
18155       ** these will often be the "CSS inherited" values,
18156       ** generally, it's best to set all three properties
18157       ** whenever you set any one of them.
18158       ** </small></blockquote>
18159       ** <p>
18160       ** 
18161       **  
18162       ** @param borderStyle a CSS border style such as
18163       ** "solid", "dotted", "dashed", etc. 
18164       **
18165       ** @see #getBorderStyle getBorderStyle
18166       ** @see #setBackgroundColor(String) setBackgroundColor
18167       ** @see #setBorderColor(String) setBorderColor
18168       ** @see #setBorderWidth(String) setBorderWidth
18169       ** @see #USE_CSS USE_CSS
18170       **
18171       ** 
18172       **/
18173         public void setBorderStyle(String borderStyle) {
18174            chartDecorationsChanged = true;
18175            this.borderStyle = borderStyle;
18176         }
18177    
18178      /**
18179       ** Specifies the width of the border around the chart as a whole.
18180       **  
18181       ** <p>
18182       ** The default border width is <tt>USE_CSS</tt>.
18183       ** 
18184       ** <p>
18185       ** <blockquote><small>
18186       ** <i>Tip:</i> No border will appear if either <tt>borderStyle</tt>
18187       ** is <tt>none</tt>, <tt>borderWidth</tt> is <tt>0px</tt> or
18188       ** <tt>borderColor</tt> is <tt>transparent</tt>. Since
18189       ** these will often be the "CSS inherited" values,
18190       ** generally, it's best to set all three properties
18191       ** whenever you set any one of them.
18192       ** </small></blockquote>
18193       ** 
18194       ** @param cssWidth width of the border around the chart as a whole,
18195       **   expressed as a CSS border-width specification string, such
18196       **   as "1px".
18197       **
18198       ** @see #getBorderWidth getBorderWidth
18199       ** @see #setBorderStyle(String) setBorderStyle
18200       ** @see #setBorderColor(String) setBorderColor
18201       ** @see #USE_CSS USE_CSS
18202       **/ 
18203      public void setBorderWidth(String cssWidth) {
18204         chartDecorationsChanged = true;
18205         borderWidth = cssWidth;
18206      }
18207    
18208    
18209      /**
18210          * Convenience method equivalent to
18211          * <tt>setChartFootnotes(new HTML(html))</tt>.
18212          *
18213          * @param html HTML text used to define the chart's title.
18214          * 
18215          * @see #setChartFootnotes(Widget) setChartFootnotes(Widget)
18216          */
18217      public void setChartFootnotes(String html) {
18218        setChartFootnotes(new HTML(html));
18219      }
18220        /** Sets widget that appears just below the chart. 
18221         *  <p>
18222         *
18223         *  The widget will vertically centered within a band just
18224         *  below the x axis label that stretches along the entire
18225         *  bottom edge of the chart, and whose height is defined by
18226         *  <tt>setChartFootnotesThickness</tt>.
18227         *
18228         *  <p>
18229         *  
18230         *  The widget will either be left justified, or horizontally
18231         *  centered, within this band depending on the property
18232         *  defined by <tt>setChartFootnotesLeftJustified</tt>
18233         *  
18234         *  
18235         *  @param chartFootnotes widget representing the chart's footnotes
18236         *
18237         *  @see #setChartFootnotes(String) setChartFootnotes(String)
18238         *  @see #setChartFootnotesThickness setChartFootnotesThickness
18239         *  @see #getChartFootnotes getChartFootnotes
18240         *  @see #setChartFootnotesLeftJustified
18241         *  setChartFootnotesLeftJustified
18242         */ 
18243        public void setChartFootnotes(Widget chartFootnotes) {
18244           chartDecorationsChanged = true;
18245           this.chartFootnotes = chartFootnotes;
18246        }
18247        
18248        /** Defines if this chart's footnotes are left justified,
18249         *  or horizontally centered across the bottom edge of the
18250         *  chart.
18251         *  <p>
18252         *  Note that a chart's footnotes are always vertically
18253         *  centered within the band at the bottom of the chart
18254         *  reserved for chart footnotes. Use the
18255         *  <tt>setChartFootnotesThickness</tt> method to set the
18256         *  height of this band.
18257         *
18258         *  @param footnotesLeftJustified true to position chart footnotes
18259         *  flush against the left edge of the chart, false (the default) to
18260         *  center them horizontally across the chart's bottom edge.
18261         *
18262         *  @see #setChartFootnotes(String) setChartFootnotes(String)
18263         *  @see #getChartFootnotes getChartFootnotes
18264         *  @see #setChartFootnotesThickness
18265         */ 
18266        public void setChartFootnotesLeftJustified(boolean footnotesLeftJustified) {
18267           chartDecorationsChanged = true;
18268           chartFootnotesLeftJustified = footnotesLeftJustified;
18269        }
18270    
18271         /**
18272          ** Sets the thickness (height) of the rectangular region at
18273          ** the bottom of the chart allocated for the footnotes.
18274          ** <p>
18275          ** 
18276          ** The width of this region always equals the width of
18277          ** the entire GChart (including legend and axis labels).
18278          ** <p>
18279          **
18280          ** Your footnotes widget is always vertically centered
18281          ** in this region.
18282          ** <p>
18283          **
18284          ** 
18285          ** Your footnotes widget will either be horizontally
18286          ** centered in this region, or left justified in it,
18287          ** depending on the property defined by the
18288          ** <tt>setChartFootnotesLeftJustified</tt> method.
18289          ** <p>
18290          ** 
18291          ** This setting has no impact on chart layout if the
18292          ** footnotes widget is <tt>null</tt> (the default); the
18293          ** rectangular footnotes region is entirely eliminated, and
18294          ** in effect has a 0 thickness, in that case.
18295          ** <p>
18296          **
18297          ** If you set the footnotes thickness to <tt>GChart.NAI</tt>
18298          ** (the default) GChart will use a thickness based on
18299          ** the estimated number of (<tt>&lt;br&gt;</tt> or
18300          ** <tt>&lt;li&gt;</tt>
18301          ** delimited) lines.
18302          **
18303          ** @param thickness the thickness (height) of the rectangle
18304          ** that contains the footnotes, in pixels, or
18305          ** <tt>GChart.NAI</tt> to use the default thickness.
18306          **
18307          ** @see #getChartFootnotesThickness getChartFootnotesThickness
18308          ** @see #setChartFootnotesLeftJustified setChartFootnotesLeftJustified
18309          ** @see GChart#NAI GChart.NAI
18310          ** @see #DEFAULT_FOOTNOTES_THICKNESS
18311          ** DEFAULT_FOOTNOTES_THICKNESS
18312          ** 
18313          **/
18314         public void setChartFootnotesThickness(int thickness) {
18315            chartDecorationsChanged = true;
18316            this.footnotesThickness = thickness; 
18317         }
18318    
18319        /**
18320          * Convenience method equivalent to
18321          * <tt>setXChartSize(xChartSize); setYChartSize(yChartSize)</tt>.
18322          *
18323          * @param xChartSize number of x-pixels in the curve
18324          *   display area of the chart
18325          * @param yChartSize number of y-pixels in the curve
18326          *   display area of the chart
18327          * 
18328          * @see #setXChartSize setXChartSize
18329          * @see #setYChartSize setYChartSize
18330          * 
18331          */
18332        public void setChartSize(int xChartSize, int yChartSize) {
18333           setXChartSize(xChartSize);
18334           setYChartSize(yChartSize);
18335        }
18336        
18337        /**
18338          * Convenience method equivalent to
18339          * <tt>setChartTitle(new HTML(html))</tt>.
18340          *
18341          * @param html HTML text used to define the chart's title.
18342          * 
18343          * @see #setChartTitle(Widget) setChartTitle(Widget)
18344          */
18345      public void setChartTitle(String html) {
18346        setChartTitle(new HTML(html));
18347      }
18348    
18349        // returns x,y min/max over every plotted curve
18350        
18351        /**
18352         * Specifies the widget that appears centered just above the chart.
18353         *
18354         * @param chartTitle the widget to be used as this chart's title.
18355         *
18356         * @see #setChartTitle(String) setChartTitle(String)
18357         * @see #setChartTitleThickness setChartTitleThickness
18358         * @see #getChartTitle getChartTitle
18359         * 
18360         */
18361        public void setChartTitle(Widget chartTitle) {
18362           chartDecorationsChanged = true;
18363           this.chartTitle = chartTitle;
18364        }
18365    
18366         /**
18367          ** Sets the thickness (height) of the rectangular region at
18368          ** the top of the chart allocated for the title.
18369          ** <p>
18370          **
18371          ** Your title widget is always centered vertically and
18372          ** horizontally within this rectangular region. <p>
18373          **
18374          ** This setting has no impact on chart layout if the title
18375          ** widget is <tt>null</tt>, since the title-holding
18376          ** region is entirely eliminated in that case.
18377          **
18378          ** If you set the title thickness to <tt>GChart.NAI</tt>
18379          ** (the default) GChart will use a thickness that is
18380          ** based on the the number of <tt>&lt;br&gt;</tt> or
18381          ** <tt>&lt;li&gt;</tt> delimited HTML lines if the title Widget
18382          ** implements <tt>HasHTML</tt>.
18383          **
18384          ** @param thickness the thickness (height) of the rectangle
18385          ** that contains the title, in pixels, or
18386          ** <tt>GChart.NAI</tt> to use the default thickness.
18387          **
18388          ** @see #getChartTitleThickness getChartTitleThickness
18389          ** @see GChart#NAI GChart.NAI
18390          ** @see #DEFAULT_TITLE_THICKNESS
18391          ** DEFAULT_TITLE_THICKNESS
18392          **
18393          **/
18394         public void setChartTitleThickness(int thickness) {
18395            chartDecorationsChanged = true;
18396            this.titleThickness = thickness; 
18397         }
18398    
18399        /**
18400         * Specifies if this chart will clip any rendered chart elements
18401         * (including hover selection feedback and popup annotations)
18402         * that extends beyond the bounds of the decorated chart.
18403         * <p>
18404         * 
18405         * The decorated chart includes not just the plot area, but
18406         * space allocated for titles, footnotes, legend key, axis
18407         * labels, tick marks, etc. The size of this decorated chart
18408         * can be obtained via the <tt>getXChartSizeDecorated</tt>
18409         * and <tt>getYChartSizeDecorated</tt> methods.
18410         * <p>
18411         *
18412         * <small> Note that, in non-IE browsers, drawing a curve via
18413         * <tt>GWTCanvas</tt> that falls outside the bounds of the
18414         * decorated chart could occlude mouse events over elements
18415         * on the enclosing page <i>that fall within the smallest
18416         * bounding rectangle that contains the canvas-rendered
18417         * curve</i>. HTML rendering (IE's element-based VML used by
18418         * <tt>GWTCanvas</tt> is essentially HTML-like in this respect) only
18419         * creates such occlusions at the positions where the curve
18420         * is actually rendered.  </small>
18421         *
18422         * @param clipToDecoratedChart use <tt>true</tt> to clip
18423         * off-the-decorated-chart symbols, annotations, etc.  or
18424         * <tt>false</tt> (the default) to allow such chart elements to be
18425         * drawn outside of the rectangular region allocated for the
18426         * chart.
18427         *
18428         * @see #getClipToDecoratedChart getClipToDecoratedChart
18429         * @see #setClipToPlotArea setClipToPlotArea
18430         * @see #getXChartSizeDecorated getXChartSizeDecorated
18431         * @see #getYChartSizeDecorated getYChartSizeDecorated
18432         * @see #setCanvasFactory setCanvasFactory
18433         * 
18434         */
18435    
18436        public void setClipToDecoratedChart(boolean clipToDecoratedChart) {
18437          chartDecorationsChanged = true;
18438          invalidateAccessibleCurves(); 
18439          this.clipToDecoratedChart = clipToDecoratedChart;
18440        }
18441        /** Specifies if rendered graphics falling
18442         ** outside the plot area will be clipped off.
18443         *  <p>
18444         *  
18445         *  <i>Note:</i> This clipping only applies to the graphical parts
18446         *  of the rendered curves. It does not apply to any annotations,
18447         *  nor does it apply to the hover selection feedback. In
18448         *  particular, points that fall outside the plot area, though not
18449         *  visible, will still display their selection feedback and pop-up
18450         *  hover annotations when the user mouses over them.
18451         *  
18452         * @param clipToPlotArea <tt>false</tt> (the default) to display
18453         *   off-the-plot-area graphics,
18454         *   <tt>true</tt>
18455         *   to clip them off.
18456         *
18457         * @see #getClipToPlotArea getClipToPlotArea
18458         * @see #setClipToDecoratedChart setClipToDecoratedChart
18459         * 
18460         */ 
18461         public void setClipToPlotArea(boolean clipToPlotArea) {
18462           chartDecorationsChanged = true;
18463           invalidateAccessibleCurves(); 
18464           this.clipToPlotArea = clipToPlotArea;
18465         }
18466         /**
18467          * Sets the symbol border colors that are used by default for
18468          * newly created curves. The
18469          * array must contain one or more elements, each a standard
18470          * CSS color specification string (see the
18471          * <tt>setBackgroundColor</tt> link below for more
18472          * on CSS color specification strings) or the
18473          * special GChart keyword <tt>TRANSPARENT_BORDER_COLOR</tt>.
18474          * <p>
18475          *
18476          * GChart uses the first color in this array as the default border
18477          * color of the first curve added (via <tt>addCurve</tt>), the
18478          * second color for the second curve added, and so on. If more
18479          * curves are added than the number of elements in the default
18480          * border colors array, the sequence is repeated.
18481          *
18482          * <p>
18483          * <small>
18484          * Note that each curve/symbol's default color is "locked in" at the
18485          * point when that curve/symbol is first added, based on the
18486          * total number of curves at that time. 
18487          * </small>
18488          * 
18489          * <p>
18490          *
18491          * Because, by default, GChart uses a transparent symbol background
18492          * color, the default border color will usually, in effect, define
18493          * the default color of each curve. The default border color
18494          * also defines the
18495          * default color of point-to-point connecting lines in a line
18496          * chart.<p>
18497          *
18498          * If not explicitly specified via this method, GChart uses
18499          * <tt>GChart.DEFAULT_SYMBOL_BORDER_COLORS</tt> by default.
18500          * However, most people find the 
18501          * color sequence <a href=
18502          * "http://ui.openoffice.org/VisualDesign/OOoChart_colors_drafts.html#02">
18503          * used by OpenOffice's Charts</a> more aesthetically pleasing.
18504          * The <a
18505          * href="package-summary.html#GChartExample22a">World's Simplest
18506          * Line Chart Editor</a> example chart contains a line of
18507          * code that makes GChart use the OpenOffice defaults. 
18508          * <p>
18509          *
18510          * <small>This feature was added in response to an email from
18511          * <a href="http://www.profilercorp.com">Joe Cole</a>
18512          * and <a href="http://gwt-ext.com/forum/viewtopic.php?f=13&t=3465&start=3">
18513    this post</a> by Sanjiv Jivan.
18514          * They both pointed out the importance of changing GChart's
18515          * default colors.</small>
18516          * 
18517          * 
18518          * @param defaultBorderColors array of CSS color strings
18519          * whose successive elements define the initial symbol border colors
18520          * for curves in the order that they are added.
18521          * 
18522          * @see #DEFAULT_SYMBOL_BORDER_COLORS DEFAULT_SYMBOL_BORDER_COLORS
18523          * @see #TRANSPARENT_BORDER_COLOR TRANSPARENT_BORDER_COLOR
18524          * @see Symbol#setBackgroundColor setBackgroundColor
18525          * @see Symbol#setBorderColor setBorderColor
18526          * @see #addCurve addCurve
18527          * 
18528          */
18529    
18530         static public void setDefaultSymbolBorderColors(String[] defaultBorderColors) {
18531            if (null == defaultBorderColors)
18532               throw new IllegalArgumentException(
18533                  "defaultBorderColors array cannot be null.");
18534            else if (defaultBorderColors.length < 1)
18535               throw new IllegalArgumentException(
18536                  "defaultBorderColors array must have at least 1 element.");
18537            else 
18538               defaultSymbolBorderColors = defaultBorderColors;
18539    
18540         }
18541            
18542        /** Sets the font-family used in tick labels, point annotations,
18543         ** legends, titles, footnotes, and
18544         ** axis labels.
18545         ** <p>
18546         ** If not specified, the default value is <tt>USE_CSS</tt>.
18547         ** <p>
18548         ** 
18549         ** Note that titles, footnotes and axis labels are
18550         ** defined via externally created Widgets, which are free
18551         ** to override the font-family specified by this
18552         ** method.
18553         **
18554         ** @param fontFamily a CSS font-family specification, such
18555         **   as "Arial, sans-serif"
18556         **
18557         ** @see #getFontFamily getFontFamily
18558         ** @see #USE_CSS USE_CSS
18559         ** 
18560         **/
18561         public void setFontFamily(String fontFamily) {
18562           chartDecorationsChanged = true;
18563           this.fontFamily = fontFamily;
18564         }
18565    
18566    
18567        
18568        /**
18569         ** Specifies the single color used for all gridlines, axes
18570         ** lines, and tick marks. 
18571         **
18572         ** 
18573         ** <p>
18574         ** For more information on standard CSS color
18575         ** specifications see the discussion in
18576         ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
18577         ** <p>
18578         **        
18579         ** @param cssColor the color, in CSS standard color format,
18580         **    to be used for all gridlines, axes, and tick marks.
18581         **
18582         ** @see #getGridColor getGridColor
18583         ** @see #DEFAULT_GRID_COLOR DEFAULT_GRID_COLOR
18584         **
18585         **/
18586        public void setGridColor(String cssColor) {
18587    //TODO: support line style for dotted/dashed gridlines lines,       
18588    // allow tick and grid colors to be specified separately, etc.
18589           getSystemCurve(XGRIDLINES_ID).getSymbol().setBorderColor(cssColor);
18590           getSystemCurve(YGRIDLINES_ID).getSymbol().setBorderColor(cssColor);
18591           getSystemCurve(Y2GRIDLINES_ID).getSymbol().setBorderColor(cssColor);
18592           getSystemCurve(XAXIS_ID).getSymbol().setBorderColor(cssColor);
18593           getSystemCurve(YAXIS_ID).getSymbol().setBorderColor(cssColor);
18594           getSystemCurve(Y2AXIS_ID).getSymbol().setBorderColor(cssColor);
18595           getSystemCurve(XTICKS_ID).getSymbol().setBorderColor(cssColor);
18596           getSystemCurve(YTICKS_ID).getSymbol().setBorderColor(cssColor);
18597           getSystemCurve(Y2TICKS_ID).getSymbol().setBorderColor(cssColor);
18598        }
18599      /**
18600       ** Sets the background color of the chart's legend.
18601       **
18602       ** 
18603       ** <p>
18604       ** For more information on standard CSS color
18605       ** specifications see the discussion in
18606       ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
18607       ** <p>
18608       **        
18609       ** @param cssColor the legend's background color, in a standard
18610       **   CSS color string format.
18611       **
18612       ** @see #getLegendBackgroundColor getLegendBackgroundColor
18613       ** @see #DEFAULT_LEGEND_BACKGROUND_COLOR
18614       **       DEFAULT_LEGEND_BACKGROUND_COLOR
18615       **/ 
18616      public void setLegendBackgroundColor(String cssColor) {
18617         chartDecorationsChanged = true;
18618         legendBackgroundColor = cssColor;
18619      }
18620      /**
18621       ** Sets the border color of the chart's legend.
18622       **
18623       ** 
18624       ** <p>
18625       ** For more information on standard CSS color
18626       ** specifications see the discussion in
18627       ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
18628       ** <p>
18629       **        
18630       ** @param cssColor the color of the legend's border, in a standard
18631       **   CSS color string format, of the special GChart keyword
18632       **   <tt>TRANSPARENT_BORDER_COLOR</tt> for a transparent border.
18633       **   
18634       **
18635       ** @see #getLegendBorderColor getLegendBorderColor
18636       ** @see #DEFAULT_LEGEND_BORDER_COLOR DEFAULT_LEGEND_BORDER_COLOR
18637       ** @see #TRANSPARENT_BORDER_COLOR TRANSPARENT_BORDER_COLOR
18638       ** 
18639       **/ 
18640      public void setLegendBorderColor(String cssColor) {
18641         chartDecorationsChanged = true;
18642         legendBorderColor = cssColor;
18643      }
18644      /**
18645       ** Sets the width of the chart legend's border.
18646       **
18647       ** @param width the width of the legend's border, in pixels
18648       **
18649       ** @see #getLegendBorderWidth getLegendBorderWidth
18650       ** @see #DEFAULT_LEGEND_BORDER_WIDTH DEFAULT_LEGEND_BORDER_WIDTH
18651       **/ 
18652      public void setLegendBorderWidth(int width) {
18653         chartDecorationsChanged = true;
18654         legendBorderWidth = width;
18655      }
18656      /**
18657       ** Sets style of the border around the chart's legend (key).
18658       **
18659       ** <p>
18660       ** 
18661       ** <p>
18662       ** @param borderStyle a CSS border style such as
18663       ** "solid", "dotted", "dashed", etc. 
18664       **
18665       ** @see #getLegendBorderStyle getLegendBorderStyle
18666       ** @see #setLegendBackgroundColor setLegendBackgroundColor
18667       ** @see #setLegendBorderColor setLegendBorderColor
18668       ** @see #DEFAULT_LEGEND_BORDER_STYLE DEFAULT_LEGEND_BORDER_STYLE
18669       **/
18670         public void setLegendBorderStyle(String borderStyle) {
18671            chartDecorationsChanged = true;
18672            legendBorderStyle = borderStyle;
18673         }
18674         /**
18675          ** Specifies the color of the legend's font. Default is
18676          ** <tt>DEFAULT_FONT_COLOR</tt>.
18677          **
18678          ** 
18679          ** <p>
18680          ** For more information on standard CSS color
18681          ** specifications see the discussion in
18682          ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
18683          ** <p>
18684          **        
18685          ** @param cssColor color of the font used to display the
18686          **    labels in the legend.
18687          **
18688          ** @see #getLegendFontColor getLegendFontColor
18689          ** @see #DEFAULT_FONT_COLOR DEFAULT_FONT_COLOR
18690          ** 
18691          **/ 
18692         public void setLegendFontColor(String cssColor) {
18693            chartDecorationsChanged = true;
18694            legendFontColor = cssColor;
18695         }
18696    
18697        /**
18698         * Specifies the CSS font size, in pixels, of text displayed
18699         * in the chart's legend (also know as a chart's key).
18700         * <p>
18701         * This size also governs the size of the symbol icon
18702         * displayed in the legend.
18703         * <p>
18704         * Default is <tt>DEFAULT_LEGEND_FONTSIZE</tt>.
18705         * 
18706         * @param legendFontSize the font size of legend text
18707         * 
18708         * @see #getLegendFontSize getLegendFontSize
18709         * @see #DEFAULT_LEGEND_FONTSIZE DEFAULT_LEGEND_FONTSIZE
18710         * 
18711         */
18712        public void setLegendFontSize(int legendFontSize) {
18713           chartDecorationsChanged = true;
18714           this.legendFontSize = legendFontSize;
18715        }
18716         /**
18717          ** Specifies the cssStyle of the font used to render the
18718          ** legend's labels. Default is <tt>DEFAULT_FONT_STYLE</tt>.
18719          **
18720          ** @param cssStyle any valid CSS font-style, namely,
18721          **   normal, italic, oblique, or inherit.
18722          **
18723          ** @see #getLegendFontStyle getLegendFontStyle
18724          ** @see #DEFAULT_FONT_STYLE DEFAULT_FONT_STYLE
18725          **/ 
18726         public void setLegendFontStyle(String cssStyle) {
18727            chartDecorationsChanged = true;
18728            legendFontStyle = cssStyle;
18729         }
18730        
18731         /**
18732          ** Specifies the weight of the font used in the labels of the
18733          ** legend. Default is <tt>DEFAULT_FONT_WEIGHT</tt>.
18734          ** 
18735          ** @param cssWeight a CSS font-weight specification, such as
18736          **    bold, bolder, normal, light, 100, 200, ... or 900. 
18737          **
18738          ** @see #getLegendFontWeight getLegendFontWeight
18739          ** @see #DEFAULT_FONT_WEIGHT DEFAULT_FONT_WEIGHT
18740          **/ 
18741         public void setLegendFontWeight(String cssWeight) {
18742            chartDecorationsChanged = true;
18743            legendFontWeight = cssWeight;
18744         }
18745         /**
18746          ** Sets the thickness (width) of the rectangular region at
18747          ** the right of the chart allocated for the legend key.
18748          ** <p>
18749          **
18750          ** This setting has no impact on chart layout if the
18751          ** legend key is not visible, since the legend key's
18752          ** rectangular region is entirely eliminated in that
18753          ** case.
18754          ** 
18755          ** <p>
18756          **
18757          ** If the legend thickness is set to <tt>GChart.NAI</tt>
18758          ** (the default) GChart uses an heuristic to set the legend
18759          ** thickness based on the number of characters in each
18760          ** curve's legend label.
18761          **
18762          ** 
18763          ** @param legendThickness the thickness (width) of the rectangle
18764          ** that contains the legend key, in pixels, or
18765          ** <tt>GChart.NAI</tt> to use a built-in heurstic
18766          ** to determine the legend width.
18767          ** 
18768          ** @see #getLegendThickness getLegendThickness
18769          ** @see Curve#setLegendLabel setLegendLabel
18770          ** @see Y2Axis#setAxisLabelThickness Y2Axis.setAxisLabelThickness
18771          **
18772          **/
18773         public void setLegendThickness(int legendThickness) {
18774            chartDecorationsChanged = true;
18775            this.legendThickness = legendThickness; 
18776         }
18777         
18778        /**
18779         * Specifies if the legend is to be visible on this chart.
18780         * Legends are visible by default. However, a legend is only
18781         * generated if at least one curve's legend label has been
18782         * specified. 
18783         * 
18784         * @param isLegendVisible true to display the legend, false to
18785         * hide it.
18786         *
18787         * @see #isLegendVisible isLegendVisible
18788         * @see Curve#setLegendLabel setLegendLabel
18789         */
18790        public void setLegendVisible(boolean isLegendVisible) {
18791           chartDecorationsChanged = true;
18792           this.isLegendVisible = isLegendVisible;
18793        }
18794    
18795        /**
18796         * By default, this property is <tt>false</tt>, which means
18797         * that GChart will retain no-longer-needed Image and Grid
18798         * widgets (plus any user object references associated with
18799         * those widgets, such as those created via the
18800         * <tt>setAnnotationText</tt> and
18801         * <tt>setAnnotationWidget</tt> methods) between
18802         * <tt>updates</tt> in the expectation that they may be
18803         * needed by future updates.  This strategy often makes
18804         * updates faster, because building new Image and Grid
18805         * elements "from scratch" is very expensive.  However,
18806         * strictly speaking, GChart is holding onto memory it no
18807         * longer needs to render the chart <i>right now</i>--which
18808         * would normally be considered a memory leak if it were not
18809         * being done deliberately.  <p>
18810         * 
18811         * If <tt>optimizeForMemory</tt> is set to <tt>true</tt>,
18812         * GChart will (at the very next <tt>update()</tt> call) free
18813         * up any Image or Grid elements that are no longer required
18814         * to render the current chart.  Should a chart's size grow back
18815         * to a former size, the subsequent update would be slower,
18816         * though.
18817         * 
18818         * <p> Charts that use exactly the same number of Image and
18819         * Grid elements for each update (for example a bar chart
18820         * where the number of bars is fixed) will see no impact on
18821         * either memory use or update speeds by setting this
18822         * parameter.  Charts that have a highly variable number of
18823         * Image or Grid elements (for example, a chart whose number
18824         * of points varies randomly between 5 and 500) may see a
18825         * very large impact on speed (false is faster) or memory
18826         * (true is more compact).
18827         * <p>
18828         * 
18829         * The setting of this parameter never has any impact on the
18830         * speed or memory used on the <i>very first</i> chart
18831         * update.
18832         * <p>
18833         *
18834         * In one test using the future oil price simulation chart of
18835         * GChart's live demo (which has only small changes in the
18836         * number of elements required to render the chart between
18837         * updates) setting this parameter to true made the updates,
18838         * on average, around 10% slower, but also reduced the memory
18839         * footprint by around 2%.
18840         * 
18841         * @param optimizeForMemory <tt>true</tt> to optimize updates
18842         * to use less memory, <tt>false</tt> (the default) to
18843         * optimize them to use less time.
18844         *
18845         * @see #update update
18846         *
18847         */
18848        public void setOptimizeForMemory(boolean optimizeForMemory) {
18849           this.optimizeForMemory = optimizeForMemory;
18850        }
18851      /**
18852       ** Specifies the amount of padding to add just inside of the
18853       ** chart's border, as a CSS padding specification string.
18854       ** <p>
18855       **  
18856       ** <p>
18857       ** The default padding is <tt>USE_CSS</tt>.
18858       ** 
18859       ** <p>
18860       **
18861       ** @param cssPadding the width of the padding, as a CSS padding
18862       **   specification string
18863       **   (e.g. use "1px" to introduce a 1 pixel padding 
18864       **   just between the chart' border and the chart itself) 
18865       **
18866       ** @see #getPadding getPadding
18867       ** @see #setBorderWidth setBorderWidth
18868       ** @see #setBorderStyle(String) setBorderStyle
18869       ** @see #setBorderColor(String) setBorderColor
18870       ** @see #USE_CSS USE_CSS
18871       **/ 
18872      public void setPadding(String cssPadding) {
18873         chartDecorationsChanged = true;
18874         padding = cssPadding;
18875      }
18876    
18877         /**
18878          ** Specifies the background color of the area of the chart
18879          ** in which symbols representing curve data are displayed
18880          **   
18881          ** 
18882          ** <p>
18883          ** For more information on standard CSS color
18884          ** specifications see the discussion in
18885          ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
18886          ** <p>
18887          **        
18888          ** @param cssColor CSS color string defining the plot
18889          **    area's background color
18890          **
18891          ** @see #getPlotAreaBackgroundColor getPlotAreaBackgroundColor
18892          **/ 
18893         public void setPlotAreaBackgroundColor(String cssColor) {
18894            Curve c = getSystemCurve(PLOTAREA_ID);
18895            c.getSymbol().setBackgroundColor(cssColor);
18896         }
18897    
18898         /**
18899          ** Specifies the color of the border around the area of the
18900          ** chart in which symbols representing curve data are
18901          ** displayed.
18902          **   
18903          ** 
18904          ** <p>
18905          ** For more information on standard CSS color
18906          ** specifications see the discussion in
18907          ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
18908          ** <p>
18909          **        
18910          ** @param cssColor CSS color string defining the color of
18911          **    the plot area's border
18912          **
18913          ** @see #getPlotAreaBorderColor getPlotAreaBorderColor
18914          **/ 
18915         public void setPlotAreaBorderColor(String cssColor) {
18916            Curve c = getSystemCurve(PLOTAREA_ID);
18917            c.getSymbol().setBorderColor(cssColor);
18918         }
18919         /**
18920          ** Specifies the width of the border around the area of the
18921          ** chart in which symbols representing curve data are
18922          ** displayed.
18923          **   
18924          ** @param width the width, in pixels, of the border around
18925          **   the plot area
18926          **
18927          ** @see #getPlotAreaBorderWidth getPlotAreaBorderWidth
18928          **/ 
18929         public void setPlotAreaBorderWidth(int width) {
18930            Curve c = getSystemCurve(PLOTAREA_ID);
18931            c.getSymbol().setBorderWidth(width);
18932         }
18933      /**
18934       ** Sets style of the border around the chart's plot area
18935       ** (the rectangular area where the curves are drawn).
18936       **
18937       ** <p>
18938       ** 
18939       ** <p>
18940       ** @param borderStyle a CSS border style such as
18941       ** "solid", "dotted", "dashed", etc. 
18942       **
18943       ** @see #getPlotAreaBorderStyle getPlotAreaBorderStyle
18944       ** @see #setPlotAreaBackgroundColor setPlotAreaBackgroundColor
18945       ** @see #setPlotAreaBorderColor setPlotAreaBorderColor
18946       **/
18947         public void setPlotAreaBorderStyle(String borderStyle) {
18948            Curve c = getSystemCurve(PLOTAREA_ID);
18949            c.getSymbol().setBorderStyle(borderStyle);
18950         }
18951    
18952         /**
18953          * Sets the image URL that defines the background of
18954          * the GChart plot area. The GChart plot area is the
18955          * rectangular region defined by the x and y axes of
18956          * the plot, but does not include those axes (or
18957          * their ticks).
18958          * <p>
18959          * Note that by default, or if this URL is set to <tt>null</tt>,
18960          * GChart will use the URL returned by
18961          * <tt>getBlankImageURL</tt>.
18962          * <p>
18963          * 
18964          * <small><b>Ideas/tips for using the plot area background
18965          * URL:</b>
18966          * <blockquote>
18967          * <ol>
18968          *  <li> It's often best to
18969          *  exactly match the width and height of the image
18970          *  with the GChart plot area width and height
18971          *  (defined via (via <tt>setChartSize</tt>). Otherwise,
18972          *  the image will be scaled up or down to fit the
18973          *  plot area, which usually doesn't look that great.
18974          *
18975          *  <li>Note that since a Google Chart API url is just
18976          *  an image url to GChart, you can easily use a
18977          *  Google Chart API url to define the background of an
18978          *  otherwise client-side chart. For example, you
18979          *  might place a static 3-D pie chart behind
18980          *  a rapidly changing client-side GChart bar chart.
18981          *  
18982          * <li> Note that this method's image will appear <i>behind</i>
18983          * every gridline and curve on the chart.  To overlay
18984          * images <i>on top of</i> the gridlines or other curves, or
18985          * even to place them outside of the plot area, use a
18986          * dedicated curve and its symbol's <tt>setImageURL</tt>
18987          * method, or simply embed such images within HTML-defined
18988          * point annotations.
18989          * </ol>
18990          *
18991          * </blockquote></small>
18992          *
18993          * @see #getPlotAreaImageURL getPlotAreaImageURL
18994          * @see #setBlankImageURL setBlankImageURL
18995          * @see GChart.Symbol#setImageURL setImageURL
18996          * 
18997          * @param imageURL URL of the image used as the background
18998          * of the plot area.  
18999          *
19000          */
19001         
19002         public void setPlotAreaImageURL(String imageURL) {
19003            Curve c = getSystemCurve(PLOTAREA_ID);
19004            c.getSymbol().setImageURL(imageURL);
19005         }
19006    
19007         /** @deprecated
19008          ** 
19009          ** Equivalent to
19010          ** <tt>setClipToPlotArea(!showOffChartPoints)</tt>.
19011          ** Use that method instead.
19012          ** <p>
19013          **
19014          ** <small>
19015          ** As of GChart 2.5, the clip-to-plot-area algorithm no
19016          ** longer drops the entire symbol if it's x,y coordinates
19017          ** are outside of the plot area; instead, it clips them
19018          ** off in the traditional "<tt>overflow: hidden</tt>" manner.
19019          ** Though unlikely you would need to, there is no easy way
19020          ** to recreate the previous behavior. <p>
19021          **
19022          ** This change was made so that both rectangular HTML and
19023          ** continuous, canvas-rendered
19024          ** chart elements would be clipped in a consistent and
19025          ** sensible way.
19026          ** </small>
19027          **
19028          ** @see #setClipToPlotArea setClipToPlotArea
19029          ** 
19030          **/ 
19031         public void setShowOffChartPoints(boolean showOffChartPoints) {
19032            setClipToPlotArea(!showOffChartPoints);
19033        }
19034    
19035    
19036        /** @deprecated
19037         **
19038         ** Equivalent to
19039         ** setClipToDecoratedChart(!showOffDecoratedChart), please
19040         ** use that method instead.
19041         **
19042         ** @see #setClipToDecoratedChart setClipToDecoratedChart
19043         **/ 
19044        public void setShowOffDecoratedChartGlyphs(boolean showOffDecoratedChartGlyphs) {
19045           setClipToDecoratedChart(!showOffDecoratedChartGlyphs);
19046        }
19047    
19048         /**
19049          * Returns the curve that the mouse "brush" is currently
19050          * "touching" (the so-called "hovered over" point), or <tt>null</tt>
19051          * if none.
19052          * <p>
19053          *
19054          * Convenience method equivalent to (when the touched point is
19055          * not <tt>null</tt>) <tt>getTouchedPoint().getParent()</tt>.
19056          * See <tt>getTouchedPoint</tt> for full details.
19057          * <p>
19058          *
19059          * 
19060          * See the <tt>setBrushHeight</tt> method for the rules
19061          * GChart uses to determine the currently touched point.
19062          * <p>
19063          * 
19064          * 
19065          * @return a reference to the curve that the mouse "brush"
19066          * is currently "touching".
19067          *
19068          * @see #getTouchedPoint getTouchedPoint
19069          * @see Symbol#setBrushHeight setBrushHeight
19070          * @see Symbol#setHoverSelectionSymbolType
19071          *      setHoverSelectionSymbolType
19072          *
19073          */
19074         public Curve getTouchedCurve() {
19075            Curve result = null;
19076            if (null != getTouchedPoint())
19077               result = getTouchedPoint().getParent();
19078            return result;
19079         }
19080    
19081         /**
19082          * Returns the point that the mouse "brush" is currently
19083          * "touching" (the so-called "hovered over" point), or <tt>null</tt>
19084          * if none.
19085          * 
19086          * <p>
19087          *  <small> <i>Fine-print:</i> If the chart clicked on needs an
19088          *  update, this method returns the touched point <i>as
19089          *  of the last time the chart's in-browser (DOM) display was
19090          *  up-to-date</i>. If you don't assure that your chart's DOM display
19091          *  is up-to-date via other means (e.g. updating right after you
19092          *  change its specifications) a quick check with the
19093          *  <tt>isUpdateNeeded</tt> method and a subsequent <tt>update</tt>
19094          *  before accessing the touched point can be a good strategy.
19095          *  <p> </small>
19096          *   
19097          * 
19098          * See the <tt>setBrushHeight</tt> method for the rules
19099          * GChart uses to determine the currently touched point.
19100          * <p>
19101          *
19102          * <small>
19103          * <i>Warning:</i> The currently touched point, on FF2 (but not in
19104          * IE7) can be changed (or set to <tt>null</tt>) by invoking
19105          * <tt>Window.alert</tt>. Though I originally expected that such
19106          * a modal alert box would "eat" all mouse events (and it does
19107          * just that in IE7) in FF2 (and possibly other browsers)
19108          * some mouse events on the alert box are also passed on up to
19109          * the GChart. It's best for applications that need to "lock on"
19110          * to the <i>initially</i> touched point to grab a
19111          * reference to the touched point <i>before</i> performing any
19112          * activity that allows the user to interact with the
19113          * browser in ways that could possibly generate GChart-visible
19114          * mouse events.
19115          * </small>
19116          * <p>
19117          * 
19118          * @return a reference to the point that the mouse "brush"
19119          * is currently "touching".
19120          *
19121          * @see #getTouchedCurve getTouchedCurve
19122          * @see #touch touch
19123          * @see Symbol#setBrushHeight setBrushHeight
19124          * @see Symbol#setHoverSelectionSymbolType
19125          *      setHoverSelectionSymbolType
19126          * @see #isUpdateNeeded isUpdateNeeded
19127          * @see #update update
19128          * @see Axis#getMouseCoordinate getMouseCoordinate
19129          * @see Axis#clientToModel clientToModel
19130          * @see Axis#modelToClient modelToClient
19131          * @see Axis#pixelToModel pixelToModel
19132          * @see Axis#modelToPixel modelToPixel
19133          * 
19134          */
19135         public Curve.Point getTouchedPoint() {
19136            return plotPanel.touchedPoint;
19137         }
19138    
19139         
19140        /**
19141         * Sets the number of pixels, in the horizontal
19142         * dimension, available for curve display. Note that
19143         * this curve display area does <i>not</i> include the
19144         * axes themselves, their tick marks, their labels, etc.
19145         * 
19146         * <p>
19147         *
19148         * <i>Note</i>: Most modern display devices use "square"
19149         * pixels, that is, pixels whose width and height are
19150         * the same. GChart tacitly assumes square pixels in
19151         * many of its default settings.
19152         * 
19153         * 
19154         * @param xChartSize the number of x-pixels in the chart region
19155         *   used for curve display.
19156         *
19157         * @see #getXChartSize getXChartSize
19158         * @see #getXChartSizeDecorated getXChartSizeDecorated
19159         * @see #setYChartSize setYChartSize
19160         * 
19161         */
19162        public void setXChartSize(int xChartSize) {        
19163           chartDecorationsChanged = true;
19164           this.xChartSize = xChartSize;
19165           Curve c = getSystemCurve(PLOTAREA_ID);
19166           c.getSymbol().setWidth(xChartSize);
19167        }
19168    
19169        /**
19170         * Sets the number of pixels, in the vertical dimension,
19171         * available for curve display. Note that this curve
19172         * display region of the chart does <i>not</i> include
19173         * the axes themselves, their tick marks, labels, etc.
19174         * 
19175         * <p>
19176         *
19177         * <i>Note</i>: Most modern display devices use "square"
19178         * pixels, that is, pixels whose width and height are
19179         * the same. GChart tacitly assumes square pixels in
19180         * many of its default settings.
19181         * 
19182         * @param yChartSize the number of y-pixels in the chart region
19183         *   used for curve display.
19184         *
19185         * @see #getYChartSize getYChartSize
19186         * @see #getYChartSizeDecorated getYChartSizeDecorated
19187         * @see #setXChartSize setXChartSize
19188         * 
19189         */
19190        public void setYChartSize(int yChartSize) {
19191          chartDecorationsChanged = true;
19192          this.yChartSize = yChartSize;
19193          Curve c = getSystemCurve(PLOTAREA_ID);
19194          c.getSymbol().setHeight(yChartSize);
19195        }
19196    
19197        /**
19198         * Simulates the user "touching" a point with the mouse, by
19199         * performing those operations that occur when the user "hovers
19200         * over" the specified point. In detail, this method does the
19201         * following:<p>
19202         * 
19203         * <ol>
19204         *
19205         *  <li> The specified point is made the currently "touched point"
19206         *  (this is the reference returned by <tt>getTouchedPoint</tt>). <p>
19207         *
19208         *  <li>If the previously touched point had a hover widget,
19209         *  that hover widget's <tt>hoverCleanup</tt> method is called.<p>
19210         *
19211         *  <li>If the touched point has an associated hover widget, that
19212         *  widget's <tt>hoverUpdate</tt> method is called.<p>
19213         *
19214         *  <li> Any hover selection feedback or hover annotation on
19215         *  any previously touched point is removed.<p>
19216         *
19217         *  <li>Any hover annotation for the newly touched point is
19218         *  displayed as per the various hover annotation related
19219         *  specifications (e.g.  <tt>setHoverLocation</tt>) associated with
19220         *  the symbol used to render the point.<p>
19221         *
19222         *  <li> Any selection feedback for the newly touched point is
19223         *  displayed in accord with the hover selection feedback
19224         *  specificiations (e.g.  <tt>setHoverSelectionBorderColor</tt>)
19225         *  associated with the symbol used to render the point.<p>
19226         *  
19227         *  </ol>
19228         *
19229         * Using <tt>null</tt> as the point to touch simulates
19230         * the user moving the mouse into a region where it is not
19231         * touching any point (for example, off the chart entirely).
19232         * <p>
19233         * 
19234         * Note that, as with all chart specification changes, you must
19235         * invoke <tt>update</tt> before the point selection and other
19236         * changes associated with this method will appear on the chart.
19237         * <p>
19238         *
19239         * <i>Tip:</i> The touched point can sometimes be used in lieu of a
19240         * point selection capability (which GChart lacks). For example, a
19241         * dialog box that allowed users to choose data points by their
19242         * names could "touch" the point associated with a user-selected
19243         * name in order to highlight it on the chart.
19244         * 
19245         * @param pointToTouch this method will perform appropriate
19246         *   operations (as described above) in order to simulate the user
19247         *   "touching" this point with their mouse.
19248         *
19249         * @see #getTouchedPoint getTouchedPoint
19250         * @see #getTouchedCurve getTouchedCurve
19251         * @see HoverUpdateable#hoverUpdate hoverUpdate
19252         * @see HoverUpdateable#hoverCleanup hoverCleanup
19253         * @see Symbol#setHoverWidget setHoverWidget
19254         * @see Symbol#setHoverLocation setHoverLocation
19255         * @see Symbol#setHoverSelectionBorderColor
19256         * setHoverSelectionBorderColor
19257         * @see Axis#getMouseCoordinate getMouseCoordinate
19258         * @see Axis#clientToModel clientToModel
19259         * @see Axis#modelToClient modelToClient
19260         * @see Axis#pixelToModel pixelToModel
19261         * @see Axis#modelToPixel modelToPixel
19262         * 
19263         */
19264        public void touch(Curve.Point pointToTouch) {
19265           plotPanel.touch(pointToTouch);
19266        }
19267          /**
19268           ** Builds a chart that reflects current user-specified
19269           ** chart specs (curve data, symbol choices, etc.)
19270           ** <p>
19271           ** 
19272           ** Before any of the chart specifications of the other
19273           ** methods of this class will actually be visible
19274           ** on the chart, you must call this method. 
19275           ** <p>
19276           ** 
19277           ** Typically, for efficiency, you would call this
19278           ** method only after you had made all of the desired
19279           ** chart specifications via the other methods.
19280           **
19281           ** <p>
19282           **
19283           ** By default, updates are optimized for speed, and this
19284           ** can end up wasting (usually not too much, though there
19285           ** are exceptions) memory.  To optimize for memory
19286           ** instead, use the <tt>setOptimizeForMemory</tt> method.
19287           ** <p>
19288           **
19289           ** For a discussion of Client-side GChart update times and
19290           ** how minimize them, see 
19291           ** <a
19292           ** href="{@docRoot}/com/googlecode/gchart/client/doc-files/tipsformakingupdatesfaster.html">
19293           ** Tips for Making Client-side GChart Updates Faster</a>.
19294           ** <p>
19295           ** 
19296           ** <i>Note</i> Hover feedback is disabled whenever the currently
19297           ** rendered chart does not match current chart specs, that is,
19298           ** whenever <tt>isUpdateNeeded</tt> returns <tt>true</tt>.  Thus,
19299           ** to assure that hover feedback remains operational once your
19300           ** code returns control to the browser, be sure to call
19301           ** <tt>update()</tt> after making a series of changes to your
19302           ** chart's properties.
19303           ** <p>
19304           **
19305           ** Understanding how <tt>update</tt> impacts visibility and size:
19306           ** <p>
19307           ** <blockquote>
19308           ** <small>
19309           ** Due to an implementation-related limitation,
19310           ** <tt>visibility: hidden</tt> won't hide a GChart
19311           ** (<tt>update</tt>
19312           ** commandeers the visibility attribute).  Instead use
19313           ** <tt>display: none</tt> or, equivalently:
19314           **
19315           ** <pre>
19316           **    myGChart.setVisible(false);
19317           ** </pre>
19318           **
19319           ** If you need to avoid <tt>display: none</tt> (it can change
19320           ** page layout), you can also hide a GChart via lines such as: 
19321           ** 
19322           ** <pre>
19323           **    DOM.setStyleAttribute(myGChart.getElement(),"overflow","hidden");
19324           **    myGChart.setPixelSize(0, 0);
19325           ** </pre>
19326           **
19327           ** This later approach gives you the option of leaving the top
19328           ** corner of the GChart visible, etc. Note that, with the next
19329           ** <tt>update</tt>, GChart will overwrite your size (based on the
19330           ** GChart properties that define the size of the the chart, such
19331           ** as <tt>setChartSize</tt> and <tt>set*Thickness</tt>)
19332           ** and your <tt>overflow:hidden</tt> (based on
19333           ** <tt>setClipToDecoratedChart</tt>) specifications. To preserve
19334           ** them (or in other special cases) you may need to apply such
19335           ** settings to an enclosing parent element.
19336           ** 
19337           ** </small>
19338           ** </blockquote>
19339           **
19340           **
19341           ** @param option determines how the touched (or "hovered
19342           ** over") point changes as a result of this update. See
19343           ** <tt>TouchedPointUpdateOption</tt> for the available
19344           ** choices.
19345           **
19346           ** @see TouchedPointUpdateOption TouchedPointUpdateOption
19347           ** @see #setOptimizeForMemory setOptimizeForMemory
19348           ** @see #isUpdateNeeded isUpdateNeeded
19349           ** 
19350           **/ 
19351          public void update(TouchedPointUpdateOption option) {
19352    
19353             /*
19354              * This method defines each curve's default pie slice
19355              * orientations, and also separates each curve's points
19356              * into the vertically or horizontally banded bins,
19357              * that GChart needs to perform the hit testing 
19358              * that allows it to emulate "touching" points with
19359              * the mouse.
19360              * <p>
19361              * 
19362              * Therefore, this line must come first.
19363              * 
19364              */ 
19365             assembleChart();
19366                     
19367             if (TouchedPointUpdateOption.TOUCHED_POINT_LOCKED == option) {
19368                 // must re-touch (point position, hover-config can change)
19369                 plotPanel.touch(plotPanel.touchedPoint);
19370             }
19371             else if (TouchedPointUpdateOption.TOUCHED_POINT_CLEARED == option) {
19372                // if needed, will clear out touched point & related feedback
19373                plotPanel.touch(null);
19374             }
19375             else if (TouchedPointUpdateOption.TOUCHED_POINT_UPDATED == option) {
19376               // re-determine which point is underneath the mouse now... 
19377                plotPanel.retouchObjectAtMousePosition();
19378             }
19379    
19380             /*
19381              * Because hover feedback curves come at the end of the curve
19382              * list, given how GChart's rendering process works, this
19383              * second call only has to update these hover feedback curves
19384              * (so it's not like we are really building the chart twice)
19385              * 
19386              */ 
19387             assembleChart();
19388                 
19389          }
19390    
19391          /**
19392           * Updates the chart, using an appropriate default touched point
19393           * update option, depending on if hover touching is enabled or
19394           * not.<p>
19395           * 
19396           * A convenience method equivalent to:
19397           * <p>
19398           * 
19399           * <pre>
19400           *  if (getHoverTouchingEnabled())
19401           *     update(TouchedPointUpdateOption.TOUCHED_POINT_UPDATED);
19402           *  else
19403           *    update(TouchedPointUpdateOption.TOUCHED_POINT_LOCKED);
19404           * </pre>
19405           *
19406           *
19407           * @see #update(TouchedPointUpdateOption) update(TouchedPointUpdateOption) 
19408           * @see #setHoverTouchingEnabled setHoverTouchingEnabled
19409           *
19410           */
19411          public void update() {
19412             if (getHoverTouchingEnabled())
19413                update(TouchedPointUpdateOption.TOUCHED_POINT_UPDATED);
19414             else
19415               update(TouchedPointUpdateOption.TOUCHED_POINT_LOCKED);
19416          }
19417    
19418        // constructs the chart within the chart panel from current specs
19419        private void assembleChart() {
19420               
19421          if (chartDecorationsChanged ||
19422              xAxis.limitsChanged() ||
19423              yAxis.limitsChanged() ||
19424              y2Axis.limitsChanged() ) {
19425            plotPanel.reset(xChartSize, yChartSize,
19426                            hasYAxis(), hasY2Axis(),
19427                            xAxis, yAxis, y2Axis);
19428            GChart.setFontFamily(this,getFontFamily());
19429            GChart.setBackgroundColor(this, getBackgroundColor());
19430            GChart.setBorderColor(this, getBorderColor());
19431            GChart.setBorderStyle(this,getBorderStyle());
19432            GChart.setBorderWidth(this, getBorderWidth());
19433            GChart.setPadding(this,getPadding());
19434            GChart.setOverflow(this, getClipToDecoratedChart() ?
19435                               "hidden" : "visible");
19436    
19437            this.setPixelSize(plotPanel.getXChartSizeDecoratedQuickly(),
19438                              plotPanel.getYChartSizeDecoratedQuickly());
19439            updateDecorations(plotPanel.getXChartSizeDecoratedQuickly());
19440            xAxis.rememberLimits();
19441            yAxis.rememberLimits();
19442            y2Axis.rememberLimits();
19443            invalidateEveryCurve();
19444            chartDecorationsChanged = false;
19445          }
19446          // actually renders chart, including internal curves used
19447          // to represent the decorations (title, axis labels, etc.)
19448          realizePlotPanel();
19449    
19450    // To avoid order-of-magnitude FF2 performance hit on busy pages,
19451    // first time, must add plotPanel only AFTER building chart      
19452          if (plotPanel != chartPanel.getWidget()) {
19453             chartPanel.add(plotPanel);
19454    /*         
19455     * Due to how GChart plays around with visible elements contained inside
19456     * hidden elements to align it's labels properly, if we allowed top
19457     * level <tt>visibility:hidden</tt> the result would be that everything
19458     * <i>except</i> annotations would be invisible.
19459     * <p>
19460     *
19461     * We can prevent such
19462     * weird behavior by setting <tt>visibility:visible</tt> on the top
19463     * level element; this setting effectively short-circuits any
19464     * top level visibility setting the user may have made. <p>
19465     * 
19466     * Users must either use <tt>display:none</tt> (as the Widget method
19467     * <tt>setVisible</tt> does) or create an enclosing 0-sized div with
19468     * <tt>overflow:hidden</tt>) to hide a GChart.
19469     * <p>
19470     *
19471     */
19472            DOM.setStyleAttribute(getElement(), "visibility","visible");         
19473          }
19474          else {  
19475    /*
19476     * Without these 2 lines IE7 won't repaint GChart's annotations.
19477     * The lines are not needed in FF2; an IE7 bug is suspected.<p>
19478     *
19479     * I got this workaround from <a href=
19480     * "http://examples.roughian.com">Ian Bambury</a> as part of <a
19481     * href="http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/4c54d8b4aea7f98b/6efd1ab4e5fc0e7b?#6efd1ab4e5fc0e7b">
19482     * this discussion on the GWT forum</a>.
19483     * <p>
19484     *
19485     * (Note comment regarding need for explicit visibility above).
19486     * 
19487     */
19488            DOM.setStyleAttribute(getElement(), "visibility","hidden");
19489            DOM.setStyleAttribute(getElement(), "visibility","visible");
19490         
19491          }
19492        }     
19493        // create a Grid representing the chart legend.
19494        private Grid createLegend(PlotPanel pp) {
19495           Grid result = new Grid(getNVisibleCurvesOnLegend(), 2);
19496           int iVisible = 0;
19497    /*
19498     * Simply eliminating the border entirely is a valid transparency
19499     * emulation for the legend (no positional shifting is needed as is
19500     * needed for the images used to draw the main chart's curves) because
19501     * the legend is always positioned by its center point, and the border
19502     * extends around the entire legend key, so removing it does not result
19503     * in any change to the legend key's center position.  <p>
19504     *
19505     * If multiple legend locations (beyond the current "always centered in
19506     * a band along the right edge" option) were ever supported, appropriate
19507     * positional shifts would then have to be introduced to emulate
19508     * transparent borders.
19509     * 
19510     */ 
19511           GChart.setBorderWidth(result,
19512             TRANSPARENT_BORDER_COLOR == getLegendBorderColor() ? 0 :
19513                                 Math.abs(getLegendBorderWidth()));
19514           GChart.setBorderColor(result,
19515              TRANSPARENT_BORDER_COLOR == getLegendBorderColor() ?
19516                                 "transparent" :
19517                                 getLegendBorderColor());
19518           GChart.setBorderStyle(result, getLegendBorderStyle());
19519           GChart.setBackgroundColor(result, getLegendBackgroundColor());
19520           int nCurves = getNCurves();
19521           for (int i = 0; i < nCurves; i++) {
19522              Curve c = getSystemCurve(i);
19523              if (c.isVisible() && c.getLegendLabel()!=null) {
19524                 double symBorderFraction =
19525                   c.getSymbol().getBorderWidth()/
19526                    Math.max(
19527                      Math.max(1.0,c.getSymbol().getFillThickness()),
19528                      Math.max(c.getSymbol().getWidth(pp),
19529                               c.getSymbol().getHeight(pp, c.onY2())));
19530                 Image icon =
19531                   c.getSymbol().getSymbolType().createIconImage(
19532                    c.getSymbol(), getLegendFontSize(),
19533                    symBorderFraction);
19534                      
19535                 result.setWidget(iVisible, 0, icon);
19536                 result.getCellFormatter().setAlignment(iVisible, 0,
19537                    HasHorizontalAlignment.ALIGN_CENTER,
19538                    HasVerticalAlignment.ALIGN_MIDDLE);
19539                 
19540                 HTML label = new HTML(c.getLegendLabel());
19541                 GChart.setFontWeight(label, getLegendFontWeight());
19542                 GChart.setFontStyle(label, getLegendFontStyle());
19543                 GChart.setColor(label, getLegendFontColor());
19544                 GChart.setFontSize(label, getLegendFontSize());
19545                               
19546                 result.setWidget(iVisible, 1, label);
19547                 result.getCellFormatter().setAlignment(iVisible, 1,
19548                    HasHorizontalAlignment.ALIGN_LEFT,
19549                    HasVerticalAlignment.ALIGN_MIDDLE);
19550    
19551                 iVisible++;
19552              }
19553           }
19554           return result;
19555        }
19556    
19557        // returns char-width-based default legend thickness
19558        private int getDefaultLegendThickness() {
19559           final int EXTRA_WIDTH = 5;  // allow for padding & symbol
19560           int maxLen = 0;
19561           int nCurves = getNCurves();
19562           for (int i = 0; i < nCurves; i++) {
19563              Curve c = getSystemCurve(i);
19564              if (c.isVisible() && null != c.getLegendLabel()) {
19565                 maxLen = Math.max(maxLen,
19566                                   htmlWidth(c.getLegendLabel()));
19567              }
19568           }
19569           int result = (int) ((maxLen + EXTRA_WIDTH) *
19570                               getLegendFontSize() *
19571                    TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
19572           return result;
19573        }
19574    
19575        private int getNVisibleCurvesOnLegend() {
19576           int result = 0;
19577           int nCurves = getNCurves();
19578           for (int i = 0; i < nCurves; i++) {
19579             if (getSystemCurve(i).isVisible() &&
19580                 getSystemCurve(i).getLegendLabel() != null) result++;
19581           }
19582           return result;
19583        }
19584    
19585        // Defines a default curve border color when curves first created
19586        private void setDefaultBorderColor(Curve curve, int index) {
19587              curve.getSymbol().setBorderColor(
19588                  defaultSymbolBorderColors[
19589                         index % defaultSymbolBorderColors.length]);
19590        }
19591    
19592        // Is the symbol type one of the special ANCHOR_MOUSE types,
19593        // whose position varies with the mouse cursor location?
19594       private boolean isMouseAnchored(SymbolType symbolType) {
19595         boolean result = false;  
19596         if (SymbolType.ANCHOR_MOUSE == symbolType ||
19597             SymbolType.ANCHOR_MOUSE_SNAP_TO_X == symbolType ||     
19598             SymbolType.ANCHOR_MOUSE_SNAP_TO_Y == symbolType)
19599            result = true;
19600         return result;
19601       }
19602         
19603       // renders the curve in the plot panel 
19604       private void realizeCurve(Curve c) {
19605          if (!c.isValidated()) {
19606             int internalIndex = getInternalCurveIndex(c);
19607             int rpIndex = getRenderingPanelIndex(internalIndex);
19608             GraphicsRenderingPanel grp =
19609                plotPanel.getGraphicsRenderingPanel(rpIndex);
19610             AnnotationRenderingPanel arp =
19611                plotPanel.getAnnotationRenderingPanel(rpIndex);
19612             if (PlotPanel.DECORATIVE_RENDERING_PANEL_INDEX == rpIndex) {
19613             // background panel only gets initialized for first curve
19614                if (0 == internalIndex) {
19615                   // background panel never uses canvas
19616                   grp.beginRendering(null);
19617                   arp.beginRendering();
19618                }
19619                c.setWasCanvasRendered(false);
19620             }
19621             else if (0 == c.getSymbol().getFillSpacing() &&  // continuous fill
19622                 0 < c.getSymbol().getFillThickness() && // non-empty fill
19623                 null != getCanvasFactory() &&           // canvas available 
19624                 c.isVisible()) {
19625               grp.maybeAddCanvas();
19626               Rectangle canvasRegion = c.getContainingRectangle(plotPanel);
19627               grp.beginRendering(canvasRegion);
19628               arp.beginRendering();
19629               c.setWasCanvasRendered(true);
19630             }
19631             else { // does not use canvas, or it is invisible
19632               grp.beginRendering(null);
19633               arp.beginRendering();
19634               c.setWasCanvasRendered(false);
19635             }
19636             
19637             if (c.isVisible()) {
19638                // Separate points into vertical/horizontal band-bins provided
19639                // 1) it is not a system curve and 2) it is not of a type whose
19640                // position follows the mouse (and thus has no fixed location
19641                // suitable for banding) and 3) at least one kind of hover feedback
19642                // is being provided for the curve. 
19643                if (getCurveIndex(c) >= 0 &&
19644                    !isMouseAnchored(c.getSymbol().getSymbolType()) &&
19645                    (c.getSymbol().getHoverSelectionEnabled() ||
19646                     c.getSymbol().getHoverAnnotationEnabled()))
19647                   c.bandSeparatePoints();
19648                else // hit test banding calcs unneeded; skip them for speed.
19649                   c.clearBandList();
19650    
19651                // Note: these lines must come AFTER band separation lines above
19652                int nPoints = c.getNPoints(); 
19653                for (int j = 0; j < nPoints; j++) {
19654                   c.realizePoint(plotPanel, grp, arp, j);
19655                }
19656             }
19657             // only end background panel rendering w last background curve
19658             if (PlotPanel.DECORATIVE_RENDERING_PANEL_INDEX != rpIndex ||
19659                 internalIndex == N_PRE_SYSTEM_CURVES-1) {
19660                grp.endRendering();
19661                arp.endRendering();
19662             }
19663             // else it's a background panel curve, and not the last one
19664             
19665             c.isValidated = true;
19666           }
19667       }
19668    
19669       // marks every curve, including system curves, as needing an update
19670       private void invalidateEveryCurve() {
19671         for (int i = 0; i < curves.size(); i++) { 
19672            curves.get(i).invalidate();
19673         }
19674       }
19675       // marks every developer-accessible curve as needing an update
19676       private void invalidateAccessibleCurves() {
19677         int nCurves = getNCurves();
19678         for (int i = 0; i < nCurves; i++) { 
19679            getSystemCurve(i).invalidate();
19680         }
19681       }
19682    
19683       // invalidates every curve that has a pie slice type
19684       void invalidateAllSlices() {
19685          int nCurves = getNCurves();
19686          for (int i = 0; i < nCurves; i++) {
19687            Curve c = getSystemCurve(i);
19688            if (c.getSymbol().getSymbolType() instanceof
19689                SymbolType.PieSliceSymbolType)
19690               c.invalidate();
19691          }
19692       }
19693       // Invalidates every pie slice curve whose orientation could
19694       // depend on the orientation of the given curve
19695       void invalidateDependentSlices(int iFirstCurve) {
19696          // only user defined curve can have slice dependency relationships
19697          if (isSystemCurveIndex(iFirstCurve)) return;
19698          int nCurves = getNCurves();      
19699          for (int i = iFirstCurve; i < nCurves; i++) {
19700            Curve c = getSystemCurve(i);
19701            if (c.getSymbol().getSymbolType() instanceof
19702                SymbolType.PieSliceSymbolType)
19703               c.invalidate();
19704            else if (i == iFirstCurve) // if first curve isn't a slice,
19705               break;                  // there are no dependent slices 
19706          }
19707       }
19708       
19709       // Defines the default pie slice orientations for every pie-slice curve
19710       private void setDefaultPieSliceOrientations() {     
19711          setLastPieSliceOrientation(getInitialPieSliceOrientation());
19712          int nCurves = getNCurves();
19713          for (int i = 0; i < nCurves; i++) {
19714             Curve c = getSystemCurve(i);
19715             // keep track of default next orientation for pie slices  
19716             // (must do this even if we don't have to redraw slice)
19717             if (c.getSymbol().getSymbolType() instanceof SymbolType.PieSliceSymbolType) {
19718                c.getSymbol().setDefaultPieSliceOrientation(
19719                   getLastPieSliceOrientation());
19720                setLastPieSliceOrientation(
19721                                           c.getSymbol().getDecodedPieSliceOrientation()
19722                                           + c.getSymbol().getPieSliceSize());
19723             }
19724          }
19725       }
19726    
19727          
19728       private void realizePlotPanel() {
19729    
19730         setDefaultPieSliceOrientations();
19731         /*
19732          * Render both system curves (those with negative ids that
19733          * are used to render title, ticks, etc.) and ordinary curves.
19734          */
19735         int nCurves = getNCurves();
19736         for (int i = -N_SYSTEM_CURVES; i < nCurves; i++) {
19737           Curve c = getSystemCurve(i);
19738           realizeCurve(c);
19739         }                                 
19740         
19741      }
19742    
19743       /* Returns true if the rendering panel index is associated
19744        * with one of the internal, hover-feedback curves.
19745        * <p>
19746        * 
19747        * This method relies on the fact that rendering panels
19748        * appear in this order:
19749        * <p>
19750        *
19751        * <ol>
19752        * <li> a single rp that renders all chart decorations
19753        * <li> getNCurves() rps (1 for each developer-defined curve)
19754        * <li> the two rendering panels associated with the two
19755        *   system-defined hover feedback curves
19756        * </ol>
19757        *
19758        */ 
19759       boolean isHoverFeedbackRenderingPanel(int rpIndex) {
19760          boolean result = rpIndex > getNCurves();
19761          return result;
19762       }
19763    
19764    
19765       /*
19766        * This code works around a bug in GWTCanvas that can cause
19767        * (in IE) previously rendered VML elements to have their fill
19768        * and stroke color, and stroke thickness properties revert to
19769        * some sort of defaults (I saw white, black, and 1px in my
19770        * tests) when the canvas is re-inserted into the DOM.
19771        *
19772        * See TestGChart55.java and TestGChart55a.java for more
19773        * info on the GWTCanvas bug that makes this code neccessary.
19774        *
19775        */
19776       
19777       // avoids inefficiency of re-rendering in most common case
19778       private boolean wasUnloaded = false;
19779       @Override protected void onUnload() {
19780          super.onUnload();
19781          wasUnloaded = true;
19782       }
19783       
19784       @Override protected void onLoad() {
19785          super.onLoad();
19786          if (wasUnloaded && plotPanel.getRenderingPanelCount() > 0) {
19787             boolean isUpToDate = !isUpdateNeeded();
19788             int nCurves = getNCurves();
19789             for (int i = 0; i < nCurves; i++) {
19790                Curve c = getCurve(i);
19791                if (c.isCanvasRendered()) {
19792                   c.invalidate();
19793                   if (isUpToDate)
19794                      realizeCurve(c);
19795                   // else since chart needs update, presume they will
19796                   // update later, no need to auto-patch things up
19797                   // (and simple patch-rerender won't work anyway).
19798                }
19799             }
19800          }
19801          // else never inserted/rendered; skip patchup-rerendering
19802       }
19803        
19804    } // end of class GChart