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><html></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><html></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><br></tt> or
1878 ** <tt><li></tt>
1879 ** delimited line for y-axis labels, and based on the
1880 ** estimated number of (<tt><br></tt> or
1881 ** <tt><li></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 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 * <inherits name='com.google.gwt.widgetideas.GWTCanvas' />
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>>
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><html></tt></b> (otherwise, GChart will treat
4130 ** it as plain text). Note that the leading
4131 ** <tt><html></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></html></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><html></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><html></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 > 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 > 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 > 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 > 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 > 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><html></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"); */
15785 ** border-width: 1px; /* setBorderWidth("1px"); */
15786 ** border-color: black; /* setBorderColor("black"); */
15787 ** border-style: solid; /* setBorderStyle("solid"); */
15788 ** font-family: Arial, sans-serif; /* setFontFamily("Arial, sans-serif"); */
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><br></tt> or <tt><li></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><br></tt> or <tt><li></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 "<html><div style='background-color:#FFFFF0; border-color:black; border-style:solid; border-width:1px; padding:2px'>"
16753 + plainTextLabel + "</div>";
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><br></tt> or
18300 ** <tt><li></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><br></tt> or
18381 ** <tt><li></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