001: /***********************************************************************************************
002: * File Info: $Id: ValueLabelRenderer.java,v 1.3 2004/05/31 16:23:03 nathaniel_auvil Exp $
003: * Copyright (C) 2002
004: * Author: Nathaniel G. Auvil, John Thomsen
005: * Contributor(s):
006: *
007: * Copyright 2002 (C) Nathaniel G. Auvil. All Rights Reserved.
008: *
009: * Redistribution and use of this software and associated documentation ("Software"), with or
010: * without modification, are permitted provided that the following conditions are met:
011: *
012: * 1. Redistributions of source code must retain copyright statements and notices.
013: * Redistributions must also contain a copy of this document.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
016: * conditions and the following disclaimer in the documentation and/or other materials
017: * provided with the distribution.
018: *
019: * 3. The name "jCharts" or "Nathaniel G. Auvil" must not be used to endorse or promote
020: * products derived from this Software without prior written permission of Nathaniel G.
021: * Auvil. For written permission, please contact nathaniel_auvil@users.sourceforge.net
022: *
023: * 4. Products derived from this Software may not be called "jCharts" nor may "jCharts" appear
024: * in their names without prior written permission of Nathaniel G. Auvil. jCharts is a
025: * registered trademark of Nathaniel G. Auvil.
026: *
027: * 5. Due credit should be given to the jCharts Project (http://jcharts.sourceforge.net/).
028: *
029: * THIS SOFTWARE IS PROVIDED BY Nathaniel G. Auvil AND CONTRIBUTORS ``AS IS'' AND ANY
030: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
031: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
032: * jCharts OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
033: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
034: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
035: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,STRICT LIABILITY, OR TORT
036: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
037: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
038: ************************************************************************************************/package org.krysalis.jcharts.axisChart.customRenderers.axisValue.renderers;
039:
040: import org.krysalis.jcharts.axisChart.AxisChart;
041: import org.krysalis.jcharts.axisChart.customRenderers.axisValue.AxisValueRenderEvent;
042: import org.krysalis.jcharts.axisChart.customRenderers.axisValue.PostAxisValueRenderListener;
043: import org.krysalis.jcharts.chartData.interfaces.IAxisChartDataSet;
044: import org.krysalis.jcharts.chartText.NumericTagGroup;
045: import org.krysalis.jcharts.chartText.TextTag;
046: import org.krysalis.jcharts.properties.util.ChartFont;
047:
048: import java.awt.*;
049: import java.text.NumberFormat;
050:
051: public class ValueLabelRenderer implements PostAxisValueRenderListener {
052:
053: private NumberFormat numberFormat;
054:
055: private ChartFont valueChartFont = ChartFont.DEFAULT_AXIS_VALUE;
056:
057: //---holds the derived Font if needed so don't have to recalculate it each time.
058: private Font derivedFont;
059:
060: //---vertical labels are only used when plotting on vertical charts; not used for horizontal bar plots.
061: private boolean isLabelVertical = false;
062:
063: private ValueLabelPosition valueLabelPosition = ValueLabelPosition.ON_TOP;
064:
065: private int pixelValuePadding = 4;
066:
067: /**********************************************************************************
068: *
069: * @param isCurrency
070: * @param showGrouping
071: * @param roundingPowerOfTen
072: * @deprecated use the other Constructor
073: **********************************************************************************/
074: public ValueLabelRenderer(boolean isCurrency, boolean showGrouping,
075: int roundingPowerOfTen) {
076: this .numberFormat = NumericTagGroup.getNumberFormatInstance(
077: isCurrency, false, showGrouping, roundingPowerOfTen);
078: }
079:
080: /**********************************************************************************
081: *
082: * @param isCurrency
083: * @param isPercent
084: * @param showGrouping
085: * @param roundingPowerOfTen
086: **********************************************************************************/
087: public ValueLabelRenderer(boolean isCurrency, boolean isPercent,
088: boolean showGrouping, int roundingPowerOfTen) {
089: this .numberFormat = NumericTagGroup
090: .getNumberFormatInstance(isCurrency, isPercent,
091: showGrouping, roundingPowerOfTen);
092: }
093:
094: /************************************************************************************
095: * Sets where you would like to position the label
096: *
097: * @param valueLabelPosition
098: ***********************************************************************************/
099: public void setValueLabelPosition(
100: ValueLabelPosition valueLabelPosition) {
101: this .valueLabelPosition = valueLabelPosition;
102: }
103:
104: /************************************************************************************
105: *
106: * @param valueChartFont
107: ***********************************************************************************/
108: public void setValueChartFont(ChartFont valueChartFont) {
109: this .valueChartFont = valueChartFont;
110: }
111:
112: /***********************************************************************************
113: *
114: * @param useVerticalLabels
115: **********************************************************************************/
116: public void useVerticalLabels(boolean useVerticalLabels) {
117: this .isLabelVertical = useVerticalLabels;
118:
119: //---set this here so can reuse same font
120: if (this .isLabelVertical) {
121: this .derivedFont = this .valueChartFont.deriveFont();
122: }
123: }
124:
125: /***********************************************************************************
126: * The pixel padding between the label and the data point.
127: *
128: * @param pixelValuePadding
129: **********************************************************************************/
130: public void setPixelValuePadding(int pixelValuePadding) {
131: this .pixelValuePadding = pixelValuePadding;
132: }
133:
134: /***********************************************************************************
135: *
136: * @param axisValueRenderEvent
137: ***********************************************************************************/
138: public void postRender(AxisValueRenderEvent axisValueRenderEvent) {
139: AxisChart axisChart = (AxisChart) axisValueRenderEvent
140: .getSource();
141: TextTag valueTag;
142: float x;
143: float y;
144:
145: if (axisValueRenderEvent.getiAxisPlotDataSet() instanceof IAxisChartDataSet) {
146: IAxisChartDataSet iAxisChartDataSet = (IAxisChartDataSet) axisValueRenderEvent
147: .getiAxisPlotDataSet();
148: double value = iAxisChartDataSet.getValue(
149: axisValueRenderEvent.getDataSetIndex(),
150: axisValueRenderEvent.getValueIndex());
151:
152: valueTag = new TextTag(this .numberFormat.format(value),
153: this .valueChartFont.getFont(), this .derivedFont,
154: axisValueRenderEvent.getFontRenderContext());
155:
156: if (axisChart.getAxisProperties().isPlotHorizontal()) {
157: x = this .calculateXHorizontalPlot(axisValueRenderEvent,
158: valueTag, (value < 0));
159: y = this .calculateYHorizontalPlot(axisValueRenderEvent,
160: valueTag);
161: } else {
162: x = this .calculateXVerticalPlot(axisValueRenderEvent,
163: valueTag);
164: y = this .calculateYVerticalPlot(axisValueRenderEvent,
165: valueTag, (value < 0));
166: }
167: } else {
168: //todo scatter and hi/low
169: valueTag = null;
170: x = 100;
171: y = 100;
172:
173: throw new RuntimeException(
174: "Axis Values not yet implemented for this type of chart.");
175: }
176:
177: /*
178: Line2D.Float line= new Line2D.Float( x, y, x, y -20 );
179: axisValueRenderEvent.getGraphics2D().draw( line );
180: */
181:
182: valueTag.setXPosition(x);
183: valueTag.setYPosition(y);
184: valueTag.render(axisValueRenderEvent.getGraphics2D(),
185: this .valueChartFont.getPaint());
186: }
187:
188: /*************************************************************************************************
189: * Calculates the label x so that the label is centered on the scale item.
190: *
191: * @param axisValueRenderEvent
192: * @param formattedTextTag
193: * @return float
194: ************************************************************************************************/
195: private float calculateXVerticalPlot(
196: AxisValueRenderEvent axisValueRenderEvent,
197: TextTag formattedTextTag) {
198: float x = axisValueRenderEvent.getValueX();
199:
200: if (this .isLabelVertical) {
201: x += formattedTextTag.getFontDescent();
202: } else {
203: x -= (formattedTextTag.getWidth() / 2);
204: }
205:
206: return x;
207: }
208:
209: /*************************************************************************************************
210: *
211: * @param axisValueRenderEvent
212: * @param formattedTextTag
213: * @return float
214: ************************************************************************************************/
215: private float calculateYHorizontalPlot(
216: AxisValueRenderEvent axisValueRenderEvent,
217: TextTag formattedTextTag) {
218: float y = axisValueRenderEvent.getValueY();
219:
220: if (this .isLabelVertical) {
221: y += (formattedTextTag.getWidth() / 2);
222: } else {
223: y += formattedTextTag.getFontDescent();
224: }
225:
226: return y;
227: }
228:
229: /*************************************************************************************************
230: *
231: * @param axisValueRenderEvent
232: * @param formattedTextTag
233: * @param isNegative
234: ************************************************************************************************/
235: private float calculateXHorizontalPlot(
236: AxisValueRenderEvent axisValueRenderEvent,
237: TextTag formattedTextTag, boolean isNegative) {
238: float x = axisValueRenderEvent.getValueX();
239:
240: if (this .valueLabelPosition.equals(ValueLabelPosition.ON_TOP)) {
241: //---if the value is negative, 'top' is to the left
242: if (isNegative) {
243: x -= (this .isLabelVertical) ? 0 : formattedTextTag
244: .getWidth();
245: x -= this .pixelValuePadding;
246: } else {
247: x += (this .isLabelVertical) ? formattedTextTag
248: .getFontAscent() : 0;
249: x += this .pixelValuePadding;
250: }
251: } else if (this .valueLabelPosition
252: .equals(ValueLabelPosition.AT_TOP)) {
253: if (isNegative) {
254: x += (this .isLabelVertical) ? formattedTextTag
255: .getFontAscent() : 0;
256: x += this .pixelValuePadding;
257: } else {
258: x -= (this .isLabelVertical) ? formattedTextTag
259: .getFontDescent() : formattedTextTag.getWidth();
260: x -= this .pixelValuePadding;
261: }
262: } else if (this .valueLabelPosition
263: .equals(ValueLabelPosition.ABOVE_ZERO_LINE)) {
264: x = axisValueRenderEvent.getZeroLineCoordinate();
265:
266: if (isNegative) {
267: x += (this .isLabelVertical) ? formattedTextTag
268: .getFontAscent() : 0;
269: x += this .pixelValuePadding;
270: } else {
271: x -= (this .isLabelVertical) ? formattedTextTag
272: .getFontDescent() : formattedTextTag.getWidth();
273: x -= this .pixelValuePadding;
274: }
275: }
276:
277: else if (this .valueLabelPosition
278: .equals(ValueLabelPosition.AXIS_TOP)) {
279: x = axisValueRenderEvent.getTotalItemAxisArea().x
280: + axisValueRenderEvent.getTotalItemAxisArea().width;
281:
282: x -= (this .isLabelVertical) ? 0 : formattedTextTag
283: .getWidth();
284: x -= this .pixelValuePadding;
285: } else if (this .valueLabelPosition
286: .equals(ValueLabelPosition.AXIS_BOTTOM)) {
287: x = axisValueRenderEvent.getTotalItemAxisArea().x;
288: x += (this .isLabelVertical) ? formattedTextTag
289: .getFontAscent() : 0;
290: x += this .pixelValuePadding;
291: }
292:
293: //VALIDATION - force labels into plot area, in case there is a user defined scale.
294: //todo could we skip this validation for non-user defined scales?
295: //---if label goes off the right edge, force it to stay in the plot area.
296: if ((x + formattedTextTag.getWidth()) > (axisValueRenderEvent
297: .getTotalItemAxisArea().x + axisValueRenderEvent
298: .getTotalItemAxisArea().width)) {
299: x = axisValueRenderEvent.getTotalItemAxisArea().x
300: + axisValueRenderEvent.getTotalItemAxisArea().width;
301: x -= formattedTextTag.getWidth();
302: x -= this .pixelValuePadding;
303: }
304: //---if label goes off left edge, force to the right
305: else if (x < axisValueRenderEvent.getTotalItemAxisArea().x) {
306: x = axisValueRenderEvent.getTotalItemAxisArea().x;
307: x += this .pixelValuePadding;
308: }
309:
310: return x;
311: }
312:
313: /*************************************************************************************************
314: *
315: * @param axisValueRenderEvent
316: * @param formattedTextTag
317: * @param isNegative
318: ************************************************************************************************/
319: private float calculateYVerticalPlot(
320: AxisValueRenderEvent axisValueRenderEvent,
321: TextTag formattedTextTag, boolean isNegative) {
322: float y = axisValueRenderEvent.getValueY();
323:
324: if (this .valueLabelPosition.equals(ValueLabelPosition.ON_TOP)) {
325: //---if the value is negative, 'top' is to the bottom
326: if (isNegative) {
327: y += (this .isLabelVertical) ? formattedTextTag
328: .getWidth() : formattedTextTag.getHeight();
329: y += this .pixelValuePadding;
330: } else {
331: y -= this .pixelValuePadding;
332: }
333: } else if (this .valueLabelPosition
334: .equals(ValueLabelPosition.AT_TOP)) {
335: //---if the value is negative, 'top' is to the bottom
336: if (isNegative) {
337: y -= this .pixelValuePadding;
338: } else {
339: y += (this .isLabelVertical) ? formattedTextTag
340: .getWidth() : formattedTextTag.getHeight();
341: y += this .pixelValuePadding;
342: }
343: } else if (this .valueLabelPosition
344: .equals(ValueLabelPosition.ABOVE_ZERO_LINE)) {
345: y = axisValueRenderEvent.getZeroLineCoordinate();
346:
347: //---if the value is negative, 'top' is to the bottom
348: if (isNegative) {
349: y -= this .pixelValuePadding;
350: } else {
351: y += (this .isLabelVertical) ? formattedTextTag
352: .getWidth() : formattedTextTag.getHeight();
353: y += this .pixelValuePadding;
354: }
355: }
356:
357: else if (this .valueLabelPosition
358: .equals(ValueLabelPosition.AXIS_TOP)) {
359: y = axisValueRenderEvent.getTotalItemAxisArea().y;
360: y += (this .isLabelVertical) ? formattedTextTag.getWidth()
361: : formattedTextTag.getHeight();
362: y += this .pixelValuePadding;
363: } else if (this .valueLabelPosition
364: .equals(ValueLabelPosition.AXIS_BOTTOM)) {
365: y = axisValueRenderEvent.getTotalItemAxisArea().y
366: + axisValueRenderEvent.getTotalItemAxisArea().height;
367: y -= this .pixelValuePadding;
368: }
369:
370: //VALIDATION - force labels into plot area, in case there is a user defined scale.
371: if (isLabelVertical) {
372: //---if label goes off the bottom edge, force it to stay in the plot area.
373: if ((y - formattedTextTag.getWidth()) < axisValueRenderEvent
374: .getTotalItemAxisArea().y) {
375: y = axisValueRenderEvent.getTotalItemAxisArea().y;
376: y += formattedTextTag.getWidth();
377: y += this .pixelValuePadding;
378: }
379: //---if label goes off bottom edge, force to the up
380: else if (y > axisValueRenderEvent.getTotalItemAxisArea().y
381: + axisValueRenderEvent.getTotalItemAxisArea().height) {
382: y = axisValueRenderEvent.getTotalItemAxisArea().y
383: + axisValueRenderEvent.getTotalItemAxisArea().height;
384: y -= this .pixelValuePadding;
385: }
386: } else {
387: //---if label goes off the top edge, force it to stay in the plot area.
388: if ((y - formattedTextTag.getHeight()) < axisValueRenderEvent
389: .getTotalItemAxisArea().y) {
390: y = axisValueRenderEvent.getTotalItemAxisArea().y;
391: y += formattedTextTag.getHeight();
392: y += this .pixelValuePadding;
393: }
394: //---if label goes off bottom edge, force to the up
395: else if (y > axisValueRenderEvent.getTotalItemAxisArea().y
396: + axisValueRenderEvent.getTotalItemAxisArea().height) {
397: y = axisValueRenderEvent.getTotalItemAxisArea().y
398: + axisValueRenderEvent.getTotalItemAxisArea().height;
399: y -= this.pixelValuePadding;
400: }
401: }
402:
403: return y;
404: }
405: }
|