1. /* ===========================================================
  2. * JFreeChart : a free chart library for the Java(tm) platform
  3. * ===========================================================
  4. *
  5. * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
  6. *
  7. * Project Info: http://www.jfree.org/jfreechart/index.html
  8. *
  9. * This library is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU Lesser General Public License as published by
  11. * the Free Software Foundation; either version 2.1 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17. * License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public License
  20. * along with this library; if not, write to the Free Software Foundation,
  21. * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  22. *
  23. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  24. * in the United States and other countries.]
  25. *
  26. * -----------------
  27. * CategoryPlot.java
  28. * -----------------
  29. * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
  30. *
  31. * Original Author: David Gilbert (for Object Refinery Limited);
  32. * Contributor(s): Jeremy Bowman;
  33. * Arnaud Lelievre;
  34. *
  35. * $Id: CategoryPlot.java,v 1.12 2005/02/23 13:03:32 mungady Exp $
  36. *
  37. * Changes (from 21-Jun-2001)
  38. * --------------------------
  39. * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
  40. * 21-Aug-2001 : Added standard header. Fixed DOS encoding problem (DG);
  41. * 18-Sep-2001 : Updated header (DG);
  42. * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
  43. * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
  44. * 23-Oct-2001 : Changed intro and trail gaps on bar plots to use percentage of
  45. * available space rather than a fixed number of units (DG);
  46. * 12-Dec-2001 : Changed constructors to protected (DG);
  47. * 13-Dec-2001 : Added tooltips (DG);
  48. * 16-Jan-2002 : Increased maximum intro and trail gap percents, plus added
  49. * some argument checking code. Thanks to Taoufik Romdhane for
  50. * suggesting this (DG);
  51. * 05-Feb-2002 : Added accessor methods for the tooltip generator, incorporated
  52. * alpha-transparency for Plot and subclasses (DG);
  53. * 06-Mar-2002 : Updated import statements (DG);
  54. * 14-Mar-2002 : Renamed BarPlot.java --> CategoryPlot.java, and changed code
  55. * to use the CategoryItemRenderer interface (DG);
  56. * 22-Mar-2002 : Dropped the getCategories() method (DG);
  57. * 23-Apr-2002 : Moved the dataset from the JFreeChart class to the Plot
  58. * class (DG);
  59. * 29-Apr-2002 : New methods to support printing values at the end of bars,
  60. * contributed by Jeremy Bowman (DG);
  61. * 11-May-2002 : New methods for label visibility and overlaid plot support,
  62. * contributed by Jeremy Bowman (DG);
  63. * 06-Jun-2002 : Removed the tooltip generator, this is now stored with the
  64. * renderer. Moved constants into the CategoryPlotConstants
  65. * interface. Updated Javadoc comments (DG);
  66. * 10-Jun-2002 : Overridden datasetChanged() method to update the upper and
  67. * lower bound on the range axis (if necessary), updated
  68. * Javadocs (DG);
  69. * 25-Jun-2002 : Removed redundant imports (DG);
  70. * 20-Aug-2002 : Changed the constructor for Marker (DG);
  71. * 28-Aug-2002 : Added listener notification to setDomainAxis() and
  72. * setRangeAxis() (DG);
  73. * 23-Sep-2002 : Added getLegendItems() method and fixed errors reported by
  74. * Checkstyle (DG);
  75. * 28-Oct-2002 : Changes to the CategoryDataset interface (DG);
  76. * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
  77. * 07-Nov-2002 : Renamed labelXXX as valueLabelXXX (DG);
  78. * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
  79. * these were set in the axes) (DG);
  80. * 19-Nov-2002 : Added axis location parameters to constructor (DG);
  81. * 17-Jan-2003 : Moved to com.jrefinery.chart.plot package (DG);
  82. * 14-Feb-2003 : Fixed bug in auto-range calculation for secondary axis (DG);
  83. * 26-Mar-2003 : Implemented Serializable (DG);
  84. * 02-May-2003 : Moved render() method up from subclasses. Added secondary
  85. * range markers. Added an attribute to control the dataset
  86. * rendering order. Added a drawAnnotations() method. Changed
  87. * the axis location from an int to an AxisLocation (DG);
  88. * 07-May-2003 : Merged HorizontalCategoryPlot and VerticalCategoryPlot into
  89. * this class (DG);
  90. * 02-Jun-2003 : Removed check for range axis compatibility (DG);
  91. * 04-Jul-2003 : Added a domain gridline position attribute (DG);
  92. * 21-Jul-2003 : Moved DrawingSupplier to Plot superclass (DG);
  93. * 19-Aug-2003 : Added equals(...) method and implemented Cloneable (DG);
  94. * 01-Sep-2003 : Fixed bug 797466 (no change event when secondary dataset
  95. * changes) (DG);
  96. * 02-Sep-2003 : Fixed bug 795209 (wrong dataset checked in render2 method) and
  97. * 790407 (initialise method) (DG);
  98. * 08-Sep-2003 : Added internationalization via use of properties
  99. * resourceBundle (RFE 690236) (AL);
  100. * 08-Sep-2003 : Fixed bug (wrong secondary range axis being used). Changed
  101. * ValueAxis API (DG);
  102. * 10-Sep-2003 : Fixed bug in setRangeAxis(...) method (DG);
  103. * 15-Sep-2003 : Fixed two bugs in serialization, implemented
  104. * PublicCloneable (DG);
  105. * 23-Oct-2003 : Added event notification for changes to renderer (DG);
  106. * 26-Nov-2003 : Fixed bug (849645) in clearRangeMarkers() method (DG);
  107. * 03-Dec-2003 : Modified draw method to accept anchor (DG);
  108. * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
  109. * 10-Mar-2004 : Fixed bug in axis range calculation when secondary renderer is
  110. * stacked (DG);
  111. * 12-May-2004 : Added fixed legend items (DG);
  112. * 19-May-2004 : Added check for null legend item from renderer (DG);
  113. * 02-Jun-2004 : Updated the DatasetRenderingOrder class (DG);
  114. * 05-Nov-2004 : Renamed getDatasetsMappedToRangeAxis()
  115. * --> datasetsMappedToRangeAxis(), and ensured that returned
  116. * list doesn't contain null datasets (DG);
  117. * 12-Nov-2004 : Implemented new Zoomable interface (DG);
  118. * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() in
  119. * CategoryItemRenderer (DG);
  120. *
  121. */
  122. package org.jfree.chart.plot;
  123. import java.awt.AlphaComposite;
  124. import java.awt.BasicStroke;
  125. import java.awt.Color;
  126. import java.awt.Composite;
  127. import java.awt.Font;
  128. import java.awt.Graphics2D;
  129. import java.awt.Insets;
  130. import java.awt.Paint;
  131. import java.awt.Shape;
  132. import java.awt.Stroke;
  133. import java.awt.geom.Line2D;
  134. import java.awt.geom.Point2D;
  135. import java.awt.geom.Rectangle2D;
  136. import java.io.IOException;
  137. import java.io.ObjectInputStream;
  138. import java.io.ObjectOutputStream;
  139. import java.io.Serializable;
  140. import java.util.ArrayList;
  141. import java.util.Collection;
  142. import java.util.Collections;
  143. import java.util.HashMap;
  144. import java.util.Iterator;
  145. import java.util.List;
  146. import java.util.Map;
  147. import java.util.ResourceBundle;
  148. import org.jfree.chart.LegendItem;
  149. import org.jfree.chart.LegendItemCollection;
  150. import org.jfree.chart.annotations.CategoryAnnotation;
  151. import org.jfree.chart.axis.Axis;
  152. import org.jfree.chart.axis.AxisCollection;
  153. import org.jfree.chart.axis.AxisLocation;
  154. import org.jfree.chart.axis.AxisSpace;
  155. import org.jfree.chart.axis.AxisState;
  156. import org.jfree.chart.axis.CategoryAnchor;
  157. import org.jfree.chart.axis.CategoryAxis;
  158. import org.jfree.chart.axis.ValueAxis;
  159. import org.jfree.chart.axis.ValueTick;
  160. import org.jfree.chart.event.ChartChangeEventType;
  161. import org.jfree.chart.event.PlotChangeEvent;
  162. import org.jfree.chart.event.RendererChangeEvent;
  163. import org.jfree.chart.event.RendererChangeListener;
  164. import org.jfree.chart.renderer.category.CategoryItemRenderer;
  165. import org.jfree.chart.renderer.category.CategoryItemRendererState;
  166. import org.jfree.data.Range;
  167. import org.jfree.data.category.CategoryDataset;
  168. import org.jfree.data.general.DatasetChangeEvent;
  169. import org.jfree.data.general.DatasetUtilities;
  170. import org.jfree.io.SerialUtilities;
  171. import org.jfree.ui.Layer;
  172. import org.jfree.ui.RectangleEdge;
  173. import org.jfree.ui.RectangleInsets;
  174. import org.jfree.util.ObjectList;
  175. import org.jfree.util.ObjectUtilities;
  176. import org.jfree.util.PublicCloneable;
  177. import org.jfree.util.SortOrder;
  178. /**
  179. * A general plotting class that uses data from a {@link CategoryDataset} and
  180. * renders each data item using a {@link CategoryItemRenderer}.
  181. */
  182. public class CategoryPlot extends Plot
  183. implements ValueAxisPlot,
  184. Zoomable,
  185. RendererChangeListener,
  186. Cloneable, PublicCloneable, Serializable {
  187. /**
  188. * The default visibility of the grid lines plotted against the domain
  189. * axis.
  190. */
  191. public static final boolean DEFAULT_DOMAIN_GRIDLINES_VISIBLE = false;
  192. /**
  193. * The default visibility of the grid lines plotted against the range
  194. * axis.
  195. */
  196. public static final boolean DEFAULT_RANGE_GRIDLINES_VISIBLE = true;
  197. /** The default grid line stroke. */
  198. public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
  199. BasicStroke.CAP_BUTT,
  200. BasicStroke.JOIN_BEVEL,
  201. 0.0f,
  202. new float[] {2.0f, 2.0f},
  203. 0.0f);
  204. /** The default grid line paint. */
  205. public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
  206. /** The default value label font. */
  207. public static final Font DEFAULT_VALUE_LABEL_FONT
  208. = new Font("SansSerif", Font.PLAIN, 10);
  209. /** The resourceBundle for the localization. */
  210. protected static ResourceBundle localizationResources
  211. = ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
  212. /** The plot orientation. */
  213. private PlotOrientation orientation;
  214. /** The offset between the data area and the axes. */
  215. private RectangleInsets axisOffset;
  216. /** Storage for the domain axes. */
  217. private ObjectList domainAxes;
  218. /** Storage for the domain axis locations. */
  219. private ObjectList domainAxisLocations;
  220. /**
  221. * A flag that controls whether or not the shared domain axis is drawn
  222. * (only relevant when the plot is being used as a subplot).
  223. */
  224. private boolean drawSharedDomainAxis;
  225. /** Storage for the range axes. */
  226. private ObjectList rangeAxes;
  227. /** Storage for the range axis locations. */
  228. private ObjectList rangeAxisLocations;
  229. /** Storage for the datasets. */
  230. private ObjectList datasets;
  231. /** Storage for keys that map datasets to domain axes. */
  232. private ObjectList datasetToDomainAxisMap;
  233. /** Storage for keys that map datasets to range axes. */
  234. private ObjectList datasetToRangeAxisMap;
  235. /** Storage for the renderers. */
  236. private ObjectList renderers;
  237. /** The dataset rendering order. */
  238. private DatasetRenderingOrder renderingOrder
  239. = DatasetRenderingOrder.REVERSE;
  240. /**
  241. * Controls the order in which the columns are traversed when rendering the
  242. * data items.
  243. */
  244. private SortOrder columnRenderingOrder = SortOrder.ASCENDING;
  245. /**
  246. * Controls the order in which the rows are traversed when rendering the
  247. * data items.
  248. */
  249. private SortOrder rowRenderingOrder = SortOrder.ASCENDING;
  250. /**
  251. * A flag that controls whether the grid-lines for the domain axis are
  252. * visible.
  253. */
  254. private boolean domainGridlinesVisible;
  255. /** The position of the domain gridlines relative to the category. */
  256. private CategoryAnchor domainGridlinePosition;
  257. /** The stroke used to draw the domain grid-lines. */
  258. private transient Stroke domainGridlineStroke;
  259. /** The paint used to draw the domain grid-lines. */
  260. private transient Paint domainGridlinePaint;
  261. /**
  262. * A flag that controls whether the grid-lines for the range axis are
  263. * visible.
  264. */
  265. private boolean rangeGridlinesVisible;
  266. /** The stroke used to draw the range axis grid-lines. */
  267. private transient Stroke rangeGridlineStroke;
  268. /** The paint used to draw the range axis grid-lines. */
  269. private transient Paint rangeGridlinePaint;
  270. /** The anchor value. */
  271. private double anchorValue;
  272. /** A flag that controls whether or not a range crosshair is drawn..*/
  273. private boolean rangeCrosshairVisible;
  274. /** The range crosshair value. */
  275. private double rangeCrosshairValue;
  276. /** The pen/brush used to draw the crosshair (if any). */
  277. private transient Stroke rangeCrosshairStroke;
  278. /** The color used to draw the crosshair (if any). */
  279. private transient Paint rangeCrosshairPaint;
  280. /**
  281. * A flag that controls whether or not the crosshair locks onto actual
  282. * data points.
  283. */
  284. private boolean rangeCrosshairLockedOnData = true;
  285. /** A map containing lists of markers for the range axes. */
  286. private transient Map foregroundRangeMarkers;
  287. /** A map containing lists of markers for the range axes. */
  288. private transient Map backgroundRangeMarkers;
  289. /** A list of annotations (optional) for the plot. */
  290. private transient List annotations;
  291. /**
  292. * The weight for the plot (only relevant when the plot is used as a subplot
  293. * within a combined plot).
  294. */
  295. private int weight;
  296. /** The fixed space for the domain axis. */
  297. private AxisSpace fixedDomainAxisSpace;
  298. /** The fixed space for the range axis. */
  299. private AxisSpace fixedRangeAxisSpace;
  300. /**
  301. * An optional collection of legend items that can be returned by the
  302. * getLegendItems() method.
  303. */
  304. private LegendItemCollection fixedLegendItems;
  305. /**
  306. * Default constructor.
  307. */
  308. public CategoryPlot() {
  309. this(null, null, null, null);
  310. }
  311. /**
  312. * Creates a new plot.
  313. *
  314. * @param dataset the dataset (<code>null</code> permitted).
  315. * @param domainAxis the domain axis (<code>null</code> permitted).
  316. * @param rangeAxis the range axis (<code>null</code> permitted).
  317. * @param renderer the item renderer (<code>null</code> permitted).
  318. *
  319. */
  320. public CategoryPlot(CategoryDataset dataset,
  321. CategoryAxis domainAxis,
  322. ValueAxis rangeAxis,
  323. CategoryItemRenderer renderer) {
  324. super();
  325. this.orientation = PlotOrientation.VERTICAL;
  326. // allocate storage for dataset, axes and renderers
  327. this.domainAxes = new ObjectList();
  328. this.domainAxisLocations = new ObjectList();
  329. this.rangeAxes = new ObjectList();
  330. this.rangeAxisLocations = new ObjectList();
  331. this.datasetToDomainAxisMap = new ObjectList();
  332. this.datasetToRangeAxisMap = new ObjectList();
  333. this.renderers = new ObjectList();
  334. this.datasets = new ObjectList();
  335. this.datasets.set(0, dataset);
  336. if (dataset != null) {
  337. dataset.addChangeListener(this);
  338. }
  339. this.axisOffset = RectangleInsets.ZERO_INSETS;
  340. setDomainAxisLocation(AxisLocation.BOTTOM_OR_LEFT, false);
  341. setRangeAxisLocation(AxisLocation.TOP_OR_LEFT, false);
  342. this.renderers.set(0, renderer);
  343. if (renderer != null) {
  344. renderer.setPlot(this);
  345. renderer.addChangeListener(this);
  346. }
  347. this.domainAxes.set(0, domainAxis);
  348. this.mapDatasetToDomainAxis(0, 0);
  349. if (domainAxis != null) {
  350. domainAxis.setPlot(this);
  351. domainAxis.addChangeListener(this);
  352. }
  353. this.drawSharedDomainAxis = false;
  354. this.rangeAxes.set(0, rangeAxis);
  355. this.mapDatasetToRangeAxis(0, 0);
  356. if (rangeAxis != null) {
  357. rangeAxis.setPlot(this);
  358. rangeAxis.addChangeListener(this);
  359. }
  360. configureDomainAxes();
  361. configureRangeAxes();
  362. this.domainGridlinesVisible = DEFAULT_DOMAIN_GRIDLINES_VISIBLE;
  363. this.domainGridlinePosition = CategoryAnchor.MIDDLE;
  364. this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
  365. this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
  366. this.rangeGridlinesVisible = DEFAULT_RANGE_GRIDLINES_VISIBLE;
  367. this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
  368. this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
  369. this.foregroundRangeMarkers = new HashMap();
  370. this.backgroundRangeMarkers = new HashMap();
  371. Marker baseline = new ValueMarker(
  372. 0.0, new Color(0.8f, 0.8f, 0.8f, 0.5f), new BasicStroke(1.0f),
  373. new Color(0.85f, 0.85f, 0.95f, 0.5f), new BasicStroke(1.0f), 0.6f
  374. );
  375. addRangeMarker(baseline, Layer.BACKGROUND);
  376. this.anchorValue = 0.0;
  377. }
  378. /**
  379. * Returns a string describing the type of plot.
  380. *
  381. * @return The type.
  382. */
  383. public String getPlotType() {
  384. return localizationResources.getString("Category_Plot");
  385. }
  386. /**
  387. * Returns the orientation of the plot.
  388. *
  389. * @return The orientation of the plot.
  390. */
  391. public PlotOrientation getOrientation() {
  392. return this.orientation;
  393. }
  394. /**
  395. * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
  396. * all registered listeners.
  397. *
  398. * @param orientation the orientation (<code>null</code> not permitted).
  399. */
  400. public void setOrientation(PlotOrientation orientation) {
  401. if (orientation == null) {
  402. throw new IllegalArgumentException("Null 'orientation' argument.");
  403. }
  404. this.orientation = orientation;
  405. notifyListeners(new PlotChangeEvent(this));
  406. }
  407. /**
  408. * Returns the axis offset.
  409. *
  410. * @return The axis offset (never <code>null</code>).
  411. */
  412. public RectangleInsets getAxisOffset() {
  413. return this.axisOffset;
  414. }
  415. /**
  416. * Sets the axis offsets (gap between the data area and the axes).
  417. *
  418. * @param offset the offset (<code>null</code> not permitted).
  419. */
  420. public void setAxisOffset(RectangleInsets offset) {
  421. if (offset == null) {
  422. throw new IllegalArgumentException("Null 'offset' argument.");
  423. }
  424. this.axisOffset = offset;
  425. notifyListeners(new PlotChangeEvent(this));
  426. }
  427. /**
  428. * Returns the domain axis for the plot. If the domain axis for this plot
  429. * is <code>null</code>, then the method will return the parent plot's
  430. * domain axis (if there is a parent plot).
  431. *
  432. * @return The domain axis (<code>null</code> permitted).
  433. */
  434. public CategoryAxis getDomainAxis() {
  435. return getDomainAxis(0);
  436. }
  437. /**
  438. * Returns a domain axis.
  439. *
  440. * @param index the axis index.
  441. *
  442. * @return The axis (<code>null</code> possible).
  443. */
  444. public CategoryAxis getDomainAxis(int index) {
  445. CategoryAxis result = null;
  446. if (index < this.domainAxes.size()) {
  447. result = (CategoryAxis) this.domainAxes.get(index);
  448. }
  449. if (result == null) {
  450. Plot parent = getParent();
  451. if (parent instanceof CategoryPlot) {
  452. CategoryPlot cp = (CategoryPlot) parent;
  453. result = cp.getDomainAxis(index);
  454. }
  455. }
  456. return result;
  457. }
  458. /**
  459. * Sets the domain axis for the plot and sends a {@link PlotChangeEvent} to
  460. * all registered listeners.
  461. *
  462. * @param axis the axis (<code>null</code> permitted).
  463. */
  464. public void setDomainAxis(CategoryAxis axis) {
  465. setDomainAxis(0, axis);
  466. }
  467. /**
  468. * Sets a domain axis.
  469. *
  470. * @param index the axis index.
  471. * @param axis the axis.
  472. */
  473. public void setDomainAxis(int index, CategoryAxis axis) {
  474. CategoryAxis existing = (CategoryAxis) this.domainAxes.get(index);
  475. if (existing != null) {
  476. existing.removeChangeListener(this);
  477. }
  478. if (axis != null) {
  479. axis.setPlot(this);
  480. }
  481. this.domainAxes.set(index, axis);
  482. if (axis != null) {
  483. axis.configure();
  484. axis.addChangeListener(this);
  485. }
  486. notifyListeners(new PlotChangeEvent(this));
  487. }
  488. /**
  489. * Returns the domain axis location.
  490. *
  491. * @return The location (never <code>null</code>).
  492. */
  493. public AxisLocation getDomainAxisLocation() {
  494. return getDomainAxisLocation(0);
  495. }
  496. /**
  497. * Returns the location for a domain axis.
  498. *
  499. * @param index the axis index.
  500. *
  501. * @return The location.
  502. */
  503. public AxisLocation getDomainAxisLocation(int index) {
  504. AxisLocation result = null;
  505. if (index < this.domainAxisLocations.size()) {
  506. result = (AxisLocation) this.domainAxisLocations.get(index);
  507. }
  508. if (result == null) {
  509. result = AxisLocation.getOpposite(getDomainAxisLocation(0));
  510. }
  511. return result;
  512. }
  513. /**
  514. * Sets the location of the domain axis and sends a {@link PlotChangeEvent}
  515. * to all registered listeners.
  516. *
  517. * @param location the axis location (<code>null</code> not permitted).
  518. */
  519. public void setDomainAxisLocation(AxisLocation location) {
  520. // defer argument checking...
  521. setDomainAxisLocation(location, true);
  522. }
  523. /**
  524. * Sets the location of the domain axis.
  525. *
  526. * @param location the axis location (<code>null</code> not permitted).
  527. * @param notify a flag that controls whether listeners are notified.
  528. */
  529. public void setDomainAxisLocation(AxisLocation location, boolean notify) {
  530. if (location == null) {
  531. throw new IllegalArgumentException("Null 'location' argument.");
  532. }
  533. setDomainAxisLocation(0, location);
  534. }
  535. /**
  536. * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
  537. * to all registered listeners.
  538. *
  539. * @param index the axis index.
  540. * @param location the location.
  541. */
  542. public void setDomainAxisLocation(int index, AxisLocation location) {
  543. // TODO: handle argument checking for primary axis location which
  544. // should not be null
  545. this.domainAxisLocations.set(index, location);
  546. notifyListeners(new PlotChangeEvent(this));
  547. }
  548. /**
  549. * Returns the domain axis edge. This is derived from the axis location
  550. * and the plot orientation.
  551. *
  552. * @return The edge (never <code>null</code>).
  553. */
  554. public RectangleEdge getDomainAxisEdge() {
  555. return getDomainAxisEdge(0);
  556. }
  557. /**
  558. * Returns the edge for a domain axis.
  559. *
  560. * @param index the axis index.
  561. *
  562. * @return The edge (never <code>null</code>).
  563. */
  564. public RectangleEdge getDomainAxisEdge(int index) {
  565. RectangleEdge result = null;
  566. AxisLocation location = getDomainAxisLocation(index);
  567. if (location != null) {
  568. result = Plot.resolveDomainAxisLocation(location, this.orientation);
  569. }
  570. else {
  571. result = RectangleEdge.opposite(getDomainAxisEdge(0));
  572. }
  573. return result;
  574. }
  575. /**
  576. * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
  577. * to all registered listeners.
  578. */
  579. public void clearDomainAxes() {
  580. for (int i = 0; i < this.domainAxes.size(); i++) {
  581. CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i);
  582. if (axis != null) {
  583. axis.removeChangeListener(this);
  584. }
  585. }
  586. this.domainAxes.clear();
  587. notifyListeners(new PlotChangeEvent(this));
  588. }
  589. /**
  590. * Configures the domain axes.
  591. */
  592. public void configureDomainAxes() {
  593. for (int i = 0; i < this.domainAxes.size(); i++) {
  594. CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i);
  595. if (axis != null) {
  596. axis.configure();
  597. }
  598. }
  599. }
  600. /**
  601. * Returns the range axis for the plot. If the range axis for this plot is
  602. * null, then the method will return the parent plot's range axis (if there
  603. * is a parent plot).
  604. *
  605. * @return The range axis (possibly <code>null</code>).
  606. */
  607. public ValueAxis getRangeAxis() {
  608. return getRangeAxis(0);
  609. }
  610. /**
  611. * Returns a range axis.
  612. *
  613. * @param index the axis index.
  614. *
  615. * @return The axis (<code>null</code> possible).
  616. */
  617. public ValueAxis getRangeAxis(int index) {
  618. ValueAxis result = null;
  619. if (index < this.rangeAxes.size()) {
  620. result = (ValueAxis) this.rangeAxes.get(index);
  621. }
  622. if (result == null) {
  623. Plot parent = getParent();
  624. if (parent instanceof CategoryPlot) {
  625. CategoryPlot cp = (CategoryPlot) parent;
  626. result = cp.getRangeAxis(index);
  627. }
  628. }
  629. return result;
  630. }
  631. /**
  632. * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
  633. * all registered listeners.
  634. *
  635. * @param axis the axis (<code>null</code> permitted).
  636. */
  637. public void setRangeAxis(ValueAxis axis) {
  638. setRangeAxis(0, axis);
  639. }
  640. /**
  641. * Sets a range axis.
  642. *
  643. * @param index the axis index.
  644. * @param axis the axis.
  645. */
  646. public void setRangeAxis(int index, ValueAxis axis) {
  647. ValueAxis existing = (ValueAxis) this.rangeAxes.get(index);
  648. if (existing != null) {
  649. existing.removeChangeListener(this);
  650. }
  651. if (axis != null) {
  652. axis.setPlot(this);
  653. }
  654. this.rangeAxes.set(index, axis);
  655. if (axis != null) {
  656. axis.configure();
  657. axis.addChangeListener(this);
  658. }
  659. notifyListeners(new PlotChangeEvent(this));
  660. }
  661. /**
  662. * Returns the range axis location.
  663. *
  664. * @return the location (never <code>null</code>).
  665. */
  666. public AxisLocation getRangeAxisLocation() {
  667. return getRangeAxisLocation(0);
  668. }
  669. /**
  670. * Returns the location for a range axis.
  671. *
  672. * @param index the axis index.
  673. *
  674. * @return The location.
  675. */
  676. public AxisLocation getRangeAxisLocation(int index) {
  677. AxisLocation result = null;
  678. if (index < this.rangeAxisLocations.size()) {
  679. result = (AxisLocation) this.rangeAxisLocations.get(index);
  680. }
  681. if (result == null) {
  682. result = AxisLocation.getOpposite(getRangeAxisLocation(0));
  683. }
  684. return result;
  685. }
  686. /**
  687. * Sets the location of the range axis and sends a {@link PlotChangeEvent}
  688. * to all registered listeners.
  689. *
  690. * @param location the location (<code>null</code> not permitted).
  691. */
  692. public void setRangeAxisLocation(AxisLocation location) {
  693. // defer argument checking...
  694. setRangeAxisLocation(location, true);
  695. }
  696. /**
  697. * Sets the location of the range axis and, if requested, sends a
  698. * {@link PlotChangeEvent} to all registered listeners.
  699. *
  700. * @param location the location (<code>null</code> not permitted).
  701. * @param notify notify listeners?
  702. */
  703. public void setRangeAxisLocation(AxisLocation location, boolean notify) {
  704. setRangeAxisLocation(0, location, notify);
  705. }
  706. /**
  707. * Sets the location for a range axis and sends a {@link PlotChangeEvent}
  708. * to all registered listeners.
  709. *
  710. * @param index the axis index.
  711. * @param location the location.
  712. */
  713. public void setRangeAxisLocation(int index, AxisLocation location) {
  714. setRangeAxisLocation(index, location, true);
  715. }
  716. /**
  717. * Sets the location for a range axis and sends a {@link PlotChangeEvent}
  718. * to all registered listeners.
  719. *
  720. * @param index the axis index.
  721. * @param location the location.
  722. * @param notify notify listeners?
  723. */
  724. public void setRangeAxisLocation(int index, AxisLocation location,
  725. boolean notify) {
  726. // TODO: don't allow null for index = 0
  727. this.rangeAxisLocations.set(index, location);
  728. if (notify) {
  729. notifyListeners(new PlotChangeEvent(this));
  730. }
  731. }
  732. /**
  733. * Returns the edge where the primary range axis is located.
  734. *
  735. * @return The edge (never <code>null</code>).
  736. */
  737. public RectangleEdge getRangeAxisEdge() {
  738. return getRangeAxisEdge(0);
  739. }
  740. /**
  741. * Returns the edge for a range axis.
  742. *
  743. * @param index the axis index.
  744. *
  745. * @return The edge.
  746. */
  747. public RectangleEdge getRangeAxisEdge(int index) {
  748. AxisLocation location = getRangeAxisLocation(index);
  749. RectangleEdge result = Plot.resolveRangeAxisLocation(
  750. location, this.orientation
  751. );
  752. if (result == null) {
  753. result = RectangleEdge.opposite(getRangeAxisEdge(0));
  754. }
  755. return result;
  756. }
  757. /**
  758. * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
  759. * to all registered listeners.
  760. */
  761. public void clearRangeAxes() {
  762. for (int i = 0; i < this.rangeAxes.size(); i++) {
  763. ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
  764. if (axis != null) {
  765. axis.removeChangeListener(this);
  766. }
  767. }
  768. this.rangeAxes.clear();
  769. notifyListeners(new PlotChangeEvent(this));
  770. }
  771. /**
  772. * Configures the range axes.
  773. */
  774. public void configureRangeAxes() {
  775. for (int i = 0; i < this.rangeAxes.size(); i++) {
  776. ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
  777. if (axis != null) {
  778. axis.configure();
  779. }
  780. }
  781. }
  782. /**
  783. * Returns the primary dataset for the plot.
  784. *
  785. * @return The primary dataset (possibly <code>null</code>).
  786. */
  787. public CategoryDataset getDataset() {
  788. return getDataset(0);
  789. }
  790. /**
  791. * Returns the dataset at the given index.
  792. *
  793. * @param index the dataset index.
  794. *
  795. * @return The dataset (possibly <code>null</code>).
  796. */
  797. public CategoryDataset getDataset(int index) {
  798. CategoryDataset result = null;
  799. if (this.datasets.size() > index) {
  800. result = (CategoryDataset) this.datasets.get(index);
  801. }
  802. return result;
  803. }
  804. /**
  805. * Sets the dataset for the plot, replacing the existing dataset, if there
  806. * is one. This method also calls the
  807. * {@link #datasetChanged(DatasetChangeEvent)} method, which adjusts the
  808. * axis ranges if necessary and sends a {@link PlotChangeEvent} to all
  809. * registered listeners.
  810. *
  811. * @param dataset the dataset (<code>null</code> permitted).
  812. */
  813. public void setDataset(CategoryDataset dataset) {
  814. setDataset(0, dataset);
  815. }
  816. /**
  817. * Sets a dataset for the plot.
  818. *
  819. * @param index the dataset index.
  820. * @param dataset the dataset (<code>null</code> permitted).
  821. */
  822. public void setDataset(int index, CategoryDataset dataset) {
  823. CategoryDataset existing = (CategoryDataset) this.datasets.get(index);
  824. if (existing != null) {
  825. existing.removeChangeListener(this);
  826. }
  827. this.datasets.set(index, dataset);
  828. if (dataset != null) {
  829. dataset.addChangeListener(this);
  830. }
  831. // send a dataset change event to self...
  832. DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
  833. datasetChanged(event);
  834. }
  835. /**
  836. * Maps a dataset to a particular domain axis.
  837. *
  838. * @param index the dataset index (zero-based).
  839. * @param axisIndex the axis index (zero-based).
  840. */
  841. public void mapDatasetToDomainAxis(int index, int axisIndex) {
  842. this.datasetToDomainAxisMap.set(index, new Integer(axisIndex));
  843. // fake a dataset change event to update axes...
  844. datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
  845. }
  846. /**
  847. * Returns the domain axis for a dataset. You can change the axis for a
  848. * dataset using the {@link #mapDatasetToDomainAxis(int, int)} method.
  849. *
  850. * @param index the dataset index.
  851. *
  852. * @return The domain axis.
  853. */
  854. public CategoryAxis getDomainAxisForDataset(int index) {
  855. CategoryAxis result = getDomainAxis();
  856. Integer axisIndex = (Integer) this.datasetToDomainAxisMap.get(index);
  857. if (axisIndex != null) {
  858. result = getDomainAxis(axisIndex.intValue());
  859. }
  860. return result;
  861. }
  862. /**
  863. * Maps a dataset to a particular range axis.
  864. *
  865. * @param index the dataset index (zero-based).
  866. * @param axisIndex the axis index (zero-based).
  867. */
  868. public void mapDatasetToRangeAxis(int index, int axisIndex) {
  869. this.datasetToRangeAxisMap.set(index, new Integer(axisIndex));
  870. // fake a dataset change event to update axes...
  871. datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
  872. }
  873. /**
  874. * Returns the range axis for a dataset. You can change the axis for a
  875. * dataset using the {@link #mapDatasetToRangeAxis(int, int)} method.
  876. *
  877. * @param index the dataset index.
  878. *
  879. * @return The range axis.
  880. */
  881. public ValueAxis getRangeAxisForDataset(int index) {
  882. ValueAxis result = getRangeAxis();
  883. Integer axisIndex = (Integer) this.datasetToRangeAxisMap.get(index);
  884. if (axisIndex != null) {
  885. result = getRangeAxis(axisIndex.intValue());
  886. }
  887. return result;
  888. }
  889. /**
  890. * Returns a reference to the renderer for the plot.
  891. *
  892. * @return The renderer.
  893. */
  894. public CategoryItemRenderer getRenderer() {
  895. return getRenderer(0);
  896. }
  897. /**
  898. * Returns the renderer at the given index.
  899. *
  900. * @param index the renderer index.
  901. *
  902. * @return The renderer (possibly <code>null</code>).
  903. */
  904. public CategoryItemRenderer getRenderer(int index) {
  905. CategoryItemRenderer result = null;
  906. if (this.renderers.size() > index) {
  907. result = (CategoryItemRenderer) this.renderers.get(index);
  908. }
  909. return result;
  910. }
  911. /**
  912. * Sets the renderer at index 0 (sometimes referred to as the "primary"
  913. * renderer) and sends a {@link PlotChangeEvent} to all registered
  914. * listeners.
  915. *
  916. * @param renderer the renderer (<code>null</code> permitted.
  917. */
  918. public void setRenderer(CategoryItemRenderer renderer) {
  919. setRenderer(0, renderer, true);
  920. }
  921. /**
  922. * Sets the renderer at index 0 (sometimes referred to as the "primary"
  923. * renderer) and, if requested, sends a {@link PlotChangeEvent} to all
  924. * registered listeners.
  925. * <p>
  926. * You can set the renderer to <code>null</code>, but this is not
  927. * recommended because:
  928. * <ul>
  929. * <li>no data will be displayed;</li>
  930. * <li>the plot background will not be painted;</li>
  931. * </ul>
  932. *
  933. * @param renderer the renderer (<code>null</code> permitted).
  934. * @param notify notify listeners?
  935. */
  936. public void setRenderer(CategoryItemRenderer renderer, boolean notify) {
  937. setRenderer(0, renderer, notify);
  938. }
  939. /**
  940. * Sets the renderer at the specified index and sends a
  941. * {@link PlotChangeEvent} to all registered listeners.
  942. *
  943. * @param index the index.
  944. * @param renderer the renderer (<code>null</code> permitted).
  945. */
  946. public void setRenderer(int index, CategoryItemRenderer renderer) {
  947. setRenderer(index, renderer, true);
  948. }
  949. /**
  950. * Sets a renderer. A {@link PlotChangeEvent} is sent to all registered
  951. * listeners.
  952. *
  953. * @param index the index.
  954. * @param renderer the renderer (<code>null</code> permitted).
  955. * @param notify notify listeners?
  956. */
  957. public void setRenderer(int index, CategoryItemRenderer renderer,
  958. boolean notify) {
  959. // stop listening to the existing renderer...
  960. CategoryItemRenderer existing
  961. = (CategoryItemRenderer) this.renderers.get(index);
  962. if (existing != null) {
  963. existing.removeChangeListener(this);
  964. }
  965. // register the new renderer...
  966. this.renderers.set(index, renderer);
  967. if (renderer != null) {
  968. renderer.setPlot(this);
  969. renderer.addChangeListener(this);
  970. }
  971. configureDomainAxes();
  972. configureRangeAxes();
  973. if (notify) {
  974. notifyListeners(new PlotChangeEvent(this));
  975. }
  976. }
  977. /**
  978. * Returns the renderer for the specified dataset. If the dataset doesn't
  979. * belong to the plot, this method will return <code>null</code>.
  980. *
  981. * @param dataset the dataset (<code>null</code> permitted).
  982. *
  983. * @return The renderer (possibly <code>null</code>).
  984. */
  985. public CategoryItemRenderer getRendererForDataset(CategoryDataset dataset) {
  986. CategoryItemRenderer result = null;
  987. for (int i = 0; i < this.datasets.size(); i++) {
  988. if (this.datasets.get(i) == dataset) {
  989. result = (CategoryItemRenderer) this.renderers.get(i);
  990. break;
  991. }
  992. }
  993. return result;
  994. }
  995. /**
  996. * Returns the index of the specified renderer, or <code>-1</code> if the
  997. * renderer is not assigned to this plot.
  998. *
  999. * @param renderer the renderer (<code>null</code> permitted).
  1000. *
  1001. * @return The renderer index.
  1002. */
  1003. public int getIndexOf(CategoryItemRenderer renderer) {
  1004. return this.renderers.indexOf(renderer);
  1005. }
  1006. /**
  1007. * Returns the dataset rendering order.
  1008. *
  1009. * @return The order (never <code>null</code>).
  1010. */
  1011. public DatasetRenderingOrder getDatasetRenderingOrder() {
  1012. return this.renderingOrder;
  1013. }
  1014. /**
  1015. * Sets the rendering order and sends a {@link PlotChangeEvent} to all
  1016. * registered listeners. By default, the plot renders the primary dataset
  1017. * last (so that the primary dataset overlays the secondary datasets). You
  1018. * can reverse this if you want to.
  1019. *
  1020. * @param order the rendering order (<code>null</code> not permitted).
  1021. */
  1022. public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
  1023. if (order == null) {
  1024. throw new IllegalArgumentException("Null 'order' argument.");
  1025. }
  1026. this.renderingOrder = order;
  1027. notifyListeners(new PlotChangeEvent(this));
  1028. }
  1029. /**
  1030. * Returns the order in which the columns are rendered.
  1031. *
  1032. * @return The order.
  1033. */
  1034. public SortOrder getColumnRenderingOrder() {
  1035. return this.columnRenderingOrder;
  1036. }
  1037. /**
  1038. * Sets the order in which the columns should be rendered.
  1039. *
  1040. * @param order the order.
  1041. */
  1042. public void setColumnRenderingOrder(SortOrder order) {
  1043. this.columnRenderingOrder = order;
  1044. }
  1045. /**
  1046. * Returns the order in which the rows should be rendered.
  1047. *
  1048. * @return the order (never <code>null</code>).
  1049. */
  1050. public SortOrder getRowRenderingOrder() {
  1051. return this.rowRenderingOrder;
  1052. }
  1053. /**
  1054. * Sets the order in which the rows should be rendered.
  1055. *
  1056. * @param order the order (<code>null</code> not allowed).
  1057. */
  1058. public void setRowRenderingOrder(SortOrder order) {
  1059. if (order == null) {
  1060. throw new IllegalArgumentException("Null 'order' argument.");
  1061. }
  1062. this.rowRenderingOrder = order;
  1063. }
  1064. /**
  1065. * Returns the flag that controls whether the domain grid-lines are visible.
  1066. *
  1067. * @return the <code>true</code> or <code>false</code>.
  1068. */
  1069. public boolean isDomainGridlinesVisible() {
  1070. return this.domainGridlinesVisible;
  1071. }
  1072. /**
  1073. * Sets the flag that controls whether or not grid-lines are drawn against
  1074. * the domain axis.
  1075. * <p>
  1076. * If the flag value changes, a {@link PlotChangeEvent} is sent to all
  1077. * registered listeners.
  1078. *
  1079. * @param visible the new value of the flag.
  1080. */
  1081. public void setDomainGridlinesVisible(boolean visible) {
  1082. if (this.domainGridlinesVisible != visible) {
  1083. this.domainGridlinesVisible = visible;
  1084. notifyListeners(new PlotChangeEvent(this));
  1085. }
  1086. }
  1087. /**
  1088. * Returns the position used for the domain gridlines.
  1089. *
  1090. * @return The gridline position.
  1091. */
  1092. public CategoryAnchor getDomainGridlinePosition() {
  1093. return this.domainGridlinePosition;
  1094. }
  1095. /**
  1096. * Sets the position used for the domain gridlines.
  1097. *
  1098. * @param position the position.
  1099. */
  1100. public void setDomainGridlinePosition(CategoryAnchor position) {
  1101. this.domainGridlinePosition = position;
  1102. notifyListeners(new PlotChangeEvent(this));
  1103. }
  1104. /**
  1105. * Returns the stroke used to draw grid-lines against the domain axis.
  1106. *
  1107. * @return the stroke.
  1108. */
  1109. public Stroke getDomainGridlineStroke() {
  1110. return this.domainGridlineStroke;
  1111. }
  1112. /**
  1113. * Sets the stroke used to draw grid-lines against the domain axis. A
  1114. * {@link PlotChangeEvent} is sent to all registered listeners.
  1115. *
  1116. * @param stroke the stroke.
  1117. */
  1118. public void setDomainGridlineStroke(Stroke stroke) {
  1119. this.domainGridlineStroke = stroke;
  1120. notifyListeners(new PlotChangeEvent(this));
  1121. }
  1122. /**
  1123. * Returns the paint used to draw grid-lines against the domain axis.
  1124. *
  1125. * @return the paint.
  1126. */
  1127. public Paint getDomainGridlinePaint() {
  1128. return this.domainGridlinePaint;
  1129. }
  1130. /**
  1131. * Sets the paint used to draw the grid-lines (if any) against the domain
  1132. * axis. A {@link PlotChangeEvent} is sent to all registered listeners.
  1133. *
  1134. * @param paint the paint.
  1135. */
  1136. public void setDomainGridlinePaint(Paint paint) {
  1137. this.domainGridlinePaint = paint;
  1138. notifyListeners(new PlotChangeEvent(this));
  1139. }
  1140. /**
  1141. * Returns the flag that controls whether the range grid-lines are visible.
  1142. *
  1143. * @return the flag.
  1144. */
  1145. public boolean isRangeGridlinesVisible() {
  1146. return this.rangeGridlinesVisible;
  1147. }
  1148. /**
  1149. * Sets the flag that controls whether or not grid-lines are drawn against
  1150. * the range axis. If the flag changes value, a {@link PlotChangeEvent} is
  1151. * sent to all registered listeners.
  1152. *
  1153. * @param visible the new value of the flag.
  1154. */
  1155. public void setRangeGridlinesVisible(boolean visible) {
  1156. if (this.rangeGridlinesVisible != visible) {
  1157. this.rangeGridlinesVisible = visible;
  1158. notifyListeners(new PlotChangeEvent(this));
  1159. }
  1160. }
  1161. /**
  1162. * Returns the stroke used to draw the grid-lines against the range axis.
  1163. *
  1164. * @return the stroke.
  1165. */
  1166. public Stroke getRangeGridlineStroke() {
  1167. return this.rangeGridlineStroke;
  1168. }
  1169. /**
  1170. * Sets the stroke used to draw the grid-lines against the range axis.
  1171. * A {@link PlotChangeEvent} is sent to all registered listeners.
  1172. *
  1173. * @param stroke the stroke.
  1174. */
  1175. public void setRangeGridlineStroke(Stroke stroke) {
  1176. this.rangeGridlineStroke = stroke;
  1177. notifyListeners(new PlotChangeEvent(this));
  1178. }
  1179. /**
  1180. * Returns the paint used to draw the grid-lines against the range axis.
  1181. *
  1182. * @return the paint.
  1183. */
  1184. public Paint getRangeGridlinePaint() {
  1185. return this.rangeGridlinePaint;
  1186. }
  1187. /**
  1188. * Sets the paint used to draw the grid lines against the range axis.
  1189. * A {@link PlotChangeEvent} is sent to all registered listeners.
  1190. *
  1191. * @param paint the paint.
  1192. */
  1193. public void setRangeGridlinePaint(Paint paint) {
  1194. this.rangeGridlinePaint = paint;
  1195. notifyListeners(new PlotChangeEvent(this));
  1196. }
  1197. /**
  1198. * Returns the fixed legend items, if any.
  1199. *
  1200. * @return The legend items (possibly <code>null</code>).
  1201. */
  1202. public LegendItemCollection getFixedLegendItems() {
  1203. return this.fixedLegendItems;
  1204. }
  1205. /**
  1206. * Sets the fixed legend items for the plot. Leave this set to
  1207. * <code>null</code> if you prefer the legend items to be created
  1208. * automatically.
  1209. *
  1210. * @param items the legend items (<code>null</code> permitted).
  1211. */
  1212. public void setFixedLegendItems(LegendItemCollection items) {
  1213. this.fixedLegendItems = items;
  1214. notifyListeners(new PlotChangeEvent(this));
  1215. }
  1216. /**
  1217. * Returns the legend items for the plot. By default, this method creates
  1218. * a legend item for each series in each of the datasets. You can change
  1219. * this behaviour by overriding this method.
  1220. *
  1221. * @return The legend items.
  1222. */
  1223. public LegendItemCollection getLegendItems() {
  1224. LegendItemCollection result = this.fixedLegendItems;
  1225. if (result == null) {
  1226. result = new LegendItemCollection();
  1227. // get the legend items for the datasets...
  1228. int count = this.datasets.size();
  1229. for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) {
  1230. CategoryDataset dataset = getDataset(datasetIndex);
  1231. if (dataset != null) {
  1232. CategoryItemRenderer renderer = getRenderer(datasetIndex);
  1233. if (renderer != null) {
  1234. int seriesCount = dataset.getRowCount();
  1235. for (int i = 0; i < seriesCount; i++) {
  1236. LegendItem item = renderer.getLegendItem(
  1237. datasetIndex, i
  1238. );
  1239. if (item != null) {
  1240. result.add(item);
  1241. }
  1242. }
  1243. }
  1244. }
  1245. }
  1246. }
  1247. return result;
  1248. }
  1249. /**
  1250. * Handles a 'click' on the plot by updating the anchor value.
  1251. *
  1252. * @param x x-coordinate of the click (in Java2D space).
  1253. * @param y y-coordinate of the click (in Java2D space).
  1254. * @param info information about the plot's dimensions.
  1255. *
  1256. */
  1257. public void handleClick(int x, int y, PlotRenderingInfo info) {
  1258. Rectangle2D dataArea = info.getDataArea();
  1259. if (dataArea.contains(x, y)) {
  1260. // set the anchor value for the range axis...
  1261. double java2D = 0.0;
  1262. if (this.orientation == PlotOrientation.HORIZONTAL) {
  1263. java2D = x;
  1264. }
  1265. else if (this.orientation == PlotOrientation.VERTICAL) {
  1266. java2D = y;
  1267. }
  1268. RectangleEdge edge = Plot.resolveRangeAxisLocation(
  1269. getRangeAxisLocation(), this.orientation
  1270. );
  1271. double value = getRangeAxis().java2DToValue(
  1272. java2D, info.getDataArea(), edge
  1273. );
  1274. setAnchorValue(value);
  1275. setRangeCrosshairValue(value);
  1276. }
  1277. }
  1278. /**
  1279. * Zooms (in or out) on the plot's value axis.
  1280. * <p>
  1281. * If the value 0.0 is passed in as the zoom percent, the auto-range
  1282. * calculation for the axis is restored (which sets the range to include
  1283. * the minimum and maximum data values, thus displaying all the data).
  1284. *
  1285. * @param percent the zoom amount.
  1286. */
  1287. public void zoom(double percent) {
  1288. if (percent > 0.0) {
  1289. double range = getRangeAxis().getRange().getLength();
  1290. double scaledRange = range * percent;
  1291. getRangeAxis().setRange(
  1292. this.anchorValue - scaledRange / 2.0,
  1293. this.anchorValue + scaledRange / 2.0
  1294. );
  1295. }
  1296. else {
  1297. getRangeAxis().setAutoRange(true);
  1298. }
  1299. }
  1300. /**
  1301. * Receives notification of a change to the plot's dataset.
  1302. * <P>
  1303. * The range axis bo