1:
85:
86: package ;
87:
88: import ;
89: import ;
90: import ;
91: import ;
92: import ;
93: import ;
94: import ;
95:
96: import ;
97: import ;
98: import ;
99: import ;
100: import ;
101: import ;
102: import ;
103: import ;
104: import ;
105: import ;
106: import ;
107: import ;
108: import ;
109:
110:
114: public class CombinedRangeXYPlot extends XYPlot
115: implements Zoomable,
116: Cloneable, PublicCloneable,
117: Serializable,
118: PlotChangeListener {
119:
120:
121: private static final long serialVersionUID = -5177814085082031168L;
122:
123:
124: private List subplots;
125:
126:
127: private int totalWeight = 0;
128:
129:
130: private double gap = 5.0;
131:
132:
133: private transient Rectangle2D[] subplotAreas;
134:
135:
138: public CombinedRangeXYPlot() {
139: this(new NumberAxis());
140: }
141:
142:
147: public CombinedRangeXYPlot(ValueAxis rangeAxis) {
148:
149: super(null,
150: null,
151: rangeAxis,
152: null);
153:
154: this.subplots = new java.util.ArrayList();
155:
156: }
157:
158:
163: public String getPlotType() {
164: return localizationResources.getString("Combined_Range_XYPlot");
165: }
166:
167:
172: public double getGap() {
173: return this.gap;
174: }
175:
176:
181: public void setGap(double gap) {
182: this.gap = gap;
183: }
184:
185:
190: public void add(XYPlot subplot) {
191: add(subplot, 1);
192: }
193:
194:
202: public void add(XYPlot subplot, int weight) {
203:
204:
205: if (weight <= 0) {
206: String msg = "The 'weight' must be positive.";
207: throw new IllegalArgumentException(msg);
208: }
209:
210:
211: subplot.setParent(this);
212: subplot.setWeight(weight);
213: subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0));
214: subplot.setRangeAxis(null);
215: subplot.addChangeListener(this);
216: this.subplots.add(subplot);
217:
218:
219: this.totalWeight += weight;
220: configureRangeAxes();
221: notifyListeners(new PlotChangeEvent(this));
222:
223: }
224:
225:
230: public void remove(XYPlot subplot) {
231: if (subplot == null) {
232: throw new IllegalArgumentException(" Null 'subplot' argument.");
233: }
234: int position = -1;
235: int size = this.subplots.size();
236: int i = 0;
237: while (position == -1 && i < size) {
238: if (this.subplots.get(i) == subplot) {
239: position = i;
240: }
241: i++;
242: }
243: if (position != -1) {
244: subplot.setParent(null);
245: subplot.removeChangeListener(this);
246: this.totalWeight -= subplot.getWeight();
247: configureRangeAxes();
248: notifyListeners(new PlotChangeEvent(this));
249: }
250: }
251:
252:
257: public List getSubplots() {
258: return Collections.unmodifiableList(this.subplots);
259: }
260:
261:
269: protected AxisSpace calculateAxisSpace(Graphics2D g2,
270: Rectangle2D plotArea) {
271:
272: AxisSpace space = new AxisSpace();
273: PlotOrientation orientation = getOrientation();
274:
275:
276: AxisSpace fixed = getFixedRangeAxisSpace();
277: if (fixed != null) {
278: if (orientation == PlotOrientation.VERTICAL) {
279: space.setLeft(fixed.getLeft());
280: space.setRight(fixed.getRight());
281: }
282: else if (orientation == PlotOrientation.HORIZONTAL) {
283: space.setTop(fixed.getTop());
284: space.setBottom(fixed.getBottom());
285: }
286: }
287: else {
288: ValueAxis valueAxis = getRangeAxis();
289: RectangleEdge valueEdge = Plot.resolveRangeAxisLocation(
290: getRangeAxisLocation(), orientation
291: );
292: if (valueAxis != null) {
293: space = valueAxis.reserveSpace(
294: g2, this, plotArea, valueEdge, space
295: );
296: }
297: }
298:
299: Rectangle2D adjustedPlotArea = space.shrink(plotArea, null);
300:
301: int n = this.subplots.size();
302:
303:
304:
305: this.subplotAreas = new Rectangle2D[n];
306: double x = adjustedPlotArea.getX();
307: double y = adjustedPlotArea.getY();
308: double usableSize = 0.0;
309: if (orientation == PlotOrientation.VERTICAL) {
310: usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1);
311: }
312: else if (orientation == PlotOrientation.HORIZONTAL) {
313: usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1);
314: }
315:
316: for (int i = 0; i < n; i++) {
317: XYPlot plot = (XYPlot) this.subplots.get(i);
318:
319:
320: if (orientation == PlotOrientation.VERTICAL) {
321: double w = usableSize * plot.getWeight() / this.totalWeight;
322: this.subplotAreas[i] = new Rectangle2D.Double(
323: x, y, w, adjustedPlotArea.getHeight()
324: );
325: x = x + w + this.gap;
326: }
327: else if (orientation == PlotOrientation.HORIZONTAL) {
328: double h = usableSize * plot.getWeight() / this.totalWeight;
329: this.subplotAreas[i] = new Rectangle2D.Double(
330: x, y, adjustedPlotArea.getWidth(), h
331: );
332: y = y + h + this.gap;
333: }
334:
335: AxisSpace subSpace = plot.calculateDomainAxisSpace(
336: g2, this.subplotAreas[i], null
337: );
338: space.ensureAtLeast(subSpace);
339:
340: }
341:
342: return space;
343: }
344:
345:
357: public void draw(Graphics2D g2,
358: Rectangle2D area,
359: Point2D anchor,
360: PlotState parentState,
361: PlotRenderingInfo info) {
362:
363:
364: if (info != null) {
365: info.setPlotArea(area);
366: }
367:
368:
369: RectangleInsets insets = getInsets();
370: insets.trim(area);
371:
372: AxisSpace space = calculateAxisSpace(g2, area);
373: Rectangle2D dataArea = space.shrink(area, null);
374:
375:
376:
377: setFixedDomainAxisSpaceForSubplots(space);
378:
379:
380: ValueAxis axis = getRangeAxis();
381: RectangleEdge edge = getRangeAxisEdge();
382: double cursor = RectangleEdge.coordinate(dataArea, edge);
383: AxisState axisState = axis.draw(g2, cursor, area, dataArea, edge, info);
384:
385: if (parentState == null) {
386: parentState = new PlotState();
387: }
388: parentState.getSharedAxisStates().put(axis, axisState);
389:
390:
391: for (int i = 0; i < this.subplots.size(); i++) {
392: XYPlot plot = (XYPlot) this.subplots.get(i);
393: PlotRenderingInfo subplotInfo = null;
394: if (info != null) {
395: subplotInfo = new PlotRenderingInfo(info.getOwner());
396: info.addSubplotInfo(subplotInfo);
397: }
398: plot.draw(
399: g2, this.subplotAreas[i], anchor, parentState, subplotInfo
400: );
401: }
402:
403: if (info != null) {
404: info.setDataArea(dataArea);
405: }
406:
407: }
408:
409:
414: public LegendItemCollection getLegendItems() {
415: LegendItemCollection result = getFixedLegendItems();
416: if (result == null) {
417: result = new LegendItemCollection();
418:
419: if (this.subplots != null) {
420: Iterator iterator = this.subplots.iterator();
421: while (iterator.hasNext()) {
422: XYPlot plot = (XYPlot) iterator.next();
423: LegendItemCollection more = plot.getLegendItems();
424: result.addAll(more);
425: }
426: }
427: }
428: return result;
429: }
430:
431:
438: public void zoomDomainAxes(double factor, PlotRenderingInfo info,
439: Point2D source) {
440: XYPlot subplot = findSubplot(info, source);
441: if (subplot != null) {
442: subplot.zoomDomainAxes(factor, info, source);
443: }
444: }
445:
446:
454: public void zoomDomainAxes(double lowerPercent, double upperPercent,
455: PlotRenderingInfo info, Point2D source) {
456: XYPlot subplot = findSubplot(info, source);
457: if (subplot != null) {
458: subplot.zoomDomainAxes(lowerPercent, upperPercent, info, source);
459: }
460: }
461:
462:
471: public XYPlot findSubplot(PlotRenderingInfo info, Point2D source) {
472: XYPlot result = null;
473: int subplotIndex = info.getSubplotIndex(source);
474: if (subplotIndex >= 0) {
475: result = (XYPlot) this.subplots.get(subplotIndex);
476: }
477: return result;
478: }
479:
480:
489: public void setRenderer(XYItemRenderer renderer) {
490:
491: super.setRenderer(renderer);
492:
493:
494:
495: Iterator iterator = this.subplots.iterator();
496: while (iterator.hasNext()) {
497: XYPlot plot = (XYPlot) iterator.next();
498: plot.setRenderer(renderer);
499: }
500:
501: }
502:
503:
508: public void setOrientation(PlotOrientation orientation) {
509:
510: super.setOrientation(orientation);
511:
512: Iterator iterator = this.subplots.iterator();
513: while (iterator.hasNext()) {
514: XYPlot plot = (XYPlot) iterator.next();
515: plot.setOrientation(orientation);
516: }
517:
518: }
519:
520:
528: public Range getDataRange(ValueAxis axis) {
529:
530: Range result = null;
531: if (this.subplots != null) {
532: Iterator iterator = this.subplots.iterator();
533: while (iterator.hasNext()) {
534: XYPlot subplot = (XYPlot) iterator.next();
535: result = Range.combine(result, subplot.getDataRange(axis));
536: }
537: }
538: return result;
539:
540: }
541:
542:
548: protected void setFixedDomainAxisSpaceForSubplots(AxisSpace space) {
549:
550: Iterator iterator = this.subplots.iterator();
551: while (iterator.hasNext()) {
552: XYPlot plot = (XYPlot) iterator.next();
553: plot.setFixedDomainAxisSpace(space);
554: }
555:
556: }
557:
558:
565: public void handleClick(int x, int y, PlotRenderingInfo info) {
566:
567: Rectangle2D dataArea = info.getDataArea();
568: if (dataArea.contains(x, y)) {
569: for (int i = 0; i < this.subplots.size(); i++) {
570: XYPlot subplot = (XYPlot) this.subplots.get(i);
571: PlotRenderingInfo subplotInfo = info.getSubplotInfo(i);
572: subplot.handleClick(x, y, subplotInfo);
573: }
574: }
575:
576: }
577:
578:
584: public void plotChanged(PlotChangeEvent event) {
585: notifyListeners(event);
586: }
587:
588:
595: public boolean equals(Object obj) {
596:
597: if (obj == this) {
598: return true;
599: }
600:
601: if (!(obj instanceof CombinedRangeXYPlot)) {
602: return false;
603: }
604: if (!super.equals(obj)) {
605: return false;
606: }
607: CombinedRangeXYPlot that = (CombinedRangeXYPlot) obj;
608: if (!ObjectUtilities.equal(this.subplots, that.subplots)) {
609: return false;
610: }
611: if (this.totalWeight != that.totalWeight) {
612: return false;
613: }
614: if (this.gap != that.gap) {
615: return false;
616: }
617: return true;
618: }
619:
620:
628: public Object clone() throws CloneNotSupportedException {
629:
630: CombinedRangeXYPlot result = (CombinedRangeXYPlot) super.clone();
631: result.subplots = (List) ObjectUtilities.deepClone(this.subplots);
632: for (Iterator it = result.subplots.iterator(); it.hasNext();) {
633: Plot child = (Plot) it.next();
634: child.setParent(result);
635: }
636:
637:
638:
639: ValueAxis rangeAxis = result.getRangeAxis();
640: if (rangeAxis != null) {
641: rangeAxis.configure();
642: }
643:
644: return result;
645: }
646:
647: }