001/* ---------------------------------------------------------------------------- 002 The Kiwi Toolkit - A Java Class Library 003 Copyright (C) 1998-2004 Mark A. Lindner 004 005 This library is free software; you can redistribute it and/or 006 modify it under the terms of the GNU General Public License as 007 published by the Free Software Foundation; either version 2 of the 008 License, or (at your option) any later version. 009 010 This library is distributed in the hope that it will be useful, 011 but WITHOUT ANY WARRANTY; without even the implied warranty of 012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 General Public License for more details. 014 015 You should have received a copy of the GNU General Public License 016 along with this library; if not, write to the Free Software 017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 018 02111-1307, USA. 019 020 The author may be contacted at: mark_a_lindner@yahoo.com 021 ---------------------------------------------------------------------------- 022 $Log: ChartView.java,v $ 023 Revision 1.10 2004/05/05 22:24:14 markl 024 comment block updates 025 026 Revision 1.9 2004/03/16 06:43:39 markl 027 LocaleManager method change 028 029 Revision 1.8 2004/01/23 00:06:51 markl 030 javadoc corrections 031 032 Revision 1.7 2003/01/19 09:33:21 markl 033 Javadoc & comment header updates. 034 035 Revision 1.6 2001/03/20 00:54:56 markl 036 Fixed deprecated calls. 037 038 Revision 1.5 2001/03/12 07:23:29 markl 039 Javadoc cleanup. 040 041 Revision 1.4 2000/10/15 09:40:29 markl 042 Added javadoc and final API polishing. 043 044 Revision 1.3 2000/10/13 08:06:05 markl 045 Integration fixes, cleanup. 046 047 Revision 1.2 2000/10/13 02:04:19 markl 048 Added remaining classes, and integrated components with models. 049 ---------------------------------------------------------------------------- 050*/ 051 052package kiwi.ui.graph; 053 054import java.awt.*; 055import java.awt.geom.*; 056import java.util.*; 057import javax.swing.*; 058 059import kiwi.event.*; 060import kiwi.ui.model.*; 061import kiwi.util.*; 062 063/** A base class for chart components that provides basic rendering logic. 064 * <code>ChartView</code> provides logic to recalculate the scale and repaint 065 * the chart when the component is resized or when the data model changes. 066 * <p> 067 * The <i>scale</i> value is the ratio of pixels to units, and is computed by 068 * dividing the width (or height, depending on the orientation) of the 069 * component in pixels by the maximum value to be plotted in this chart. The 070 * calculation of the <i>maximum value</i> is a chart-specific; the method 071 * <code>getMaxValue()</code> must be overridden to perform this calculation. 072 * <p> 073 * The method <code>paintChart()</code> should be overridden to paint the 074 * chart and (if necessary) the chart scale. Convenience methods are provided 075 * for rendering chart scales. By convention, chart scales appear on the left 076 * for vertical charts and at the top for horizontal charts. 077 * 078 * @author Mark Lindner 079 */ 080 081public abstract class ChartView extends JComponent 082 implements ChartModelListener 083 { 084 private double maxValue = 0.0; 085 private double tickInterval = 10.0; 086 087 /** A vertical orientation. */ 088 public static final int VERTICAL = 0; 089 /** A horizontal orientation. */ 090 public static final int HORIZONTAL = 1; 091 /** The data model for this view. */ 092 protected ChartModel model = null; 093 /** The scale factor for this view. */ 094 protected double scale = 1.0; 095 /** The horizontal padding for this view. */ 096 protected int horizontalPad = 10; 097 /** The vertical padding for this view. */ 098 protected int verticalPad = 10; 099 /** A cached reference to the locale manager. */ 100 protected LocaleManager lm = LocaleManager.getDefault(); 101 /** The orientation for this view. */ 102 protected int orientation = VERTICAL; 103 /** The precision for this view. */ 104 protected int precision = 2; 105 /** The chart definition for this view. */ 106 protected Chart chart; 107 /** The width of the scale, in pixels. */ 108 protected int scaleWidth = 50; 109 110 /** Construct a new <code>ChartView</code> for the specified chart 111 * definition. 112 * 113 * @param chart The chart definition. 114 */ 115 116 protected ChartView(Chart chart) 117 { 118 setChart(chart); 119 120 setFont(new Font("Dialog", Font.PLAIN, 10)); 121 setBackground(Color.white); 122 } 123 124 /** Set the chart definition. The precision and tick interval from the chart 125 * definition are automatically copied into the view. 126 * 127 * @param chart The chart definition. 128 */ 129 130 public void setChart(Chart chart) 131 { 132 this.chart = chart; 133 134 setPrecision(chart.getPrecision()); 135 setTickInterval(chart.getTickInterval()); 136 } 137 138 /** Get the chart definition. 139 * 140 * @return The chart definition. 141 */ 142 143 public Chart getChart() 144 { 145 return(chart); 146 } 147 148 /** Set the data model for this view. 149 * 150 * @param model The data model. 151 */ 152 153 public void setModel(ChartModel model) 154 { 155 if (this.model != null) 156 this.model.removeChartModelListener(this); 157 this.model = model; 158 model.addChartModelListener(this); 159 } 160 161 /** Get the data model for this view. 162 * 163 * @return The data model. 164 */ 165 166 public ChartModel getModel() 167 { 168 return(model); 169 } 170 171 /** Handle <i>chart data changed</i> events. In response to data model 172 * events, the scale is recalculated and the chart is redrawn. 173 * 174 * @param event The event. 175 */ 176 177 public void chartDataChanged(ChartModelEvent event) 178 { 179 recalculateScale(); 180 repaint(); 181 } 182 183 /** Paint the component. This method should not be overridden by 184 * subclassers. 185 */ 186 187 public void paintComponent(Graphics gc) 188 { 189 Dimension d = getSize(); 190 gc.setColor(getBackground()); 191 gc.fillRect(0, 0, d.width, d.height); 192 193 if(model != null) 194 paintChart(gc); 195 } 196 197 /** Set the chart orientation. 198 * 199 * @param orientation The orientation; one of the constants 200 * <code>HORIZONTAL</code> or <code>VERTICAL</code>. 201 */ 202 203 public void setOrientation(int orientation) 204 { 205 if(orientation < VERTICAL || orientation > HORIZONTAL) 206 throw(new IllegalArgumentException("bad orientation value")); 207 208 this.orientation = orientation; 209 } 210 211 /** Set the horizontal padding. The padding value is used for the margins of 212 * the chart as well as the space between the chart and the scale. 213 * 214 * @param hpad The horizontal padding, in pixels. 215 * 216 */ 217 218 public void setHorizontalPad(int hpad) 219 { 220 horizontalPad = hpad; 221 } 222 223 /** Set the vertical padding. The padding value is used for the margins of 224 * the chart as well as the space between the chart and the scale. 225 * 226 * @param vpad The vertical padding, in pixels. 227 * 228 */ 229 230 public void setVerticalPad(int vpad) 231 { 232 verticalPad = vpad; 233 } 234 235 /** Get the horizontal padding. 236 * 237 * @return The horizontal padding, in pixels. 238 */ 239 240 public int getHorizontalPad() 241 { 242 return(horizontalPad); 243 } 244 245 /** Get the vertical padding. 246 * 247 * @return The vertical padding, in pixels. 248 */ 249 250 public int getVerticalPad() 251 { 252 return(verticalPad); 253 } 254 255 /** Set the tick interval for this view. The tick interval is specified 256 * (conceptually) in the same units as the actual data values in the chart. 257 * 258 * @param tickInterval The tick interval. 259 */ 260 261 public void setTickInterval(double tickInterval) 262 { 263 this.tickInterval = tickInterval; 264 } 265 266 /** Get the tick interval for this view. 267 * 268 * @return The tick interval. 269 */ 270 271 public double getTickInterval() 272 { 273 return(tickInterval); 274 } 275 276 /** Set the decimal precision for this view. This value 277 * determines how many decimal places are significant in the data values 278 * that are displayed by this view; the values in the chart scale will also 279 * be rendered to this many decimal places. 280 * 281 * @param precision The precision. 282 */ 283 284 public void setPrecision(int precision) 285 { 286 if(precision < 0 || precision > 10) 287 throw(new IllegalArgumentException("Invalid precision")); 288 289 this.precision = precision; 290 } 291 292 /** Get the decimal precision for this view. 293 * 294 * @return The precision. 295 */ 296 297 public int getPrecision() 298 { 299 return(precision); 300 } 301 302 /** Paint the chart. 303 */ 304 305 protected abstract void paintChart(Graphics gc); 306 307 /** Determine if this chart has a scale. Some charts (notably pie charts) 308 * do not have a scale. The default implementation returns <code>true</code>. 309 * 310 * @return <code>true</code> if the chart has a scale, <code>false</code> 311 * otherwise. 312 */ 313 314 protected boolean hasScale() 315 { 316 return(true); 317 } 318 319 /* 320 */ 321 322 private void recalculateScale() 323 { 324 if(hasScale()) 325 { 326 Dimension d = getSize(); 327 328 maxValue = getMaxValue(); 329 330 if(orientation == VERTICAL) 331 scale = (d.height - (2 * verticalPad)) / maxValue; 332 else 333 scale = (d.width - (2 * horizontalPad)) / maxValue; 334 } 335 } 336 337 /** Overridden to recalculate the scale when the component geometry 338 * changes. 339 */ 340 341 public void setBounds(int x, int y, int width, int height) 342 { 343 super.setBounds(x, y, width, height); 344 345 recalculateScale(); 346 } 347 348 /** Compute the maximum value. The default implementation returns 349 * <code>0.0</code>. 350 * 351 * @return The maximum value to be plotted. 352 */ 353 354 protected double getMaxValue() 355 { 356 return(0.0); 357 } 358 359 /** A convenience method for drawing a vertical scale with tickmarks and 360 * tick labels. 361 * 362 * @param gc The graphics context. 363 * @param x The x-coordinate of the scale. 364 */ 365 366 protected void drawVerticalScale(Graphics gc, int x) 367 { 368 Dimension d = getSize(); 369 FontMetrics fm = gc.getFontMetrics(getFont()); 370 int fh = fm.getAscent() / 2; 371 int y = d.height - verticalPad; 372 373 gc.setColor(Color.black); 374 gc.drawLine(x, y, x, verticalPad); 375 376 for(double tick = 0.0; tick <= maxValue; tick += tickInterval) 377 { 378 int yp = (int)(y - (tick * scale)); 379 gc.drawLine(x - 1, yp, x - 4, yp); 380 381 String label = lm.formatDecimal(tick, precision); 382 gc.drawString(label, x - 4 - fm.stringWidth(label), yp + fh); 383 } 384 } 385 386 /** A convenience method for drawing a horizontal scale with tickmarks and 387 * tick labels. 388 * 389 * @param gc The graphics context. 390 * @param y The y-coordinate of the scale. 391 */ 392 393 protected void drawHorizontalScale(Graphics gc, int y) 394 { 395 Dimension d = getSize(); 396 FontMetrics fm = gc.getFontMetrics(getFont()); 397 int fh = fm.getAscent() / 2; 398 int x = horizontalPad; 399 400 gc.setColor(Color.black); 401 gc.drawLine(x, y, d.width - horizontalPad, y); 402 403 for(double tick = 0.0; tick <= maxValue; tick += tickInterval) 404 { 405 int xp = (int)(x + (tick * scale)); 406 gc.drawLine(xp, y - 1, xp, y - 4); 407 } 408 409 Graphics2D gc2d = (Graphics2D)gc; 410 Paint paint = Color.black; 411 gc2d.setPaint(paint); 412 AffineTransform rotate = new AffineTransform(0, 1, -1, 0, 0, 0); 413 gc2d.transform(rotate); 414 415 for(double tick = 0.0; tick <= maxValue; tick += tickInterval) 416 { 417 int xp = (int)(x + (tick * scale)); 418 419 String label = lm.formatDecimal(tick, precision); 420 // in the rotated coordinate system, (x', y') = (y, -x) 421 gc2d.drawString(label, y - 5 - fm.stringWidth(label), -(xp - fh)); 422 } 423 } 424 425 } 426 427/* end of source file */