001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ------------------ 028 * GanttRenderer.java 029 * ------------------ 030 * (C) Copyright 2003-2006, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * $Id: GanttRenderer.java,v 1.6.2.4 2006/01/18 14:22:51 mungady Exp $ 036 * 037 * Changes 038 * ------- 039 * 16-Sep-2003 : Version 1 (DG); 040 * 23-Sep-2003 : Fixed Checkstyle issues (DG); 041 * 21-Oct-2003 : Bar width moved into CategoryItemRendererState (DG); 042 * 03-Feb-2004 : Added get/set methods for attributes (DG); 043 * 12-Aug-2004 : Fixed rendering problem with maxBarWidth attribute (DG); 044 * 05-Nov-2004 : Modified drawItem() signature (DG); 045 * 20-Apr-2005 : Renamed CategoryLabelGenerator 046 * --> CategoryItemLabelGenerator (DG); 047 * 01-Dec-2005 : Fix for bug 1369954, drawBarOutline flag ignored (DG); 048 * 17-Jan-2006 : Set includeBaseInRange flag to false (DG); 049 * 050 */ 051 052 package org.jfree.chart.renderer.category; 053 054 import java.awt.Color; 055 import java.awt.Graphics2D; 056 import java.awt.Paint; 057 import java.awt.Stroke; 058 import java.awt.geom.Rectangle2D; 059 import java.io.Serializable; 060 061 import org.jfree.chart.axis.CategoryAxis; 062 import org.jfree.chart.axis.ValueAxis; 063 import org.jfree.chart.entity.CategoryItemEntity; 064 import org.jfree.chart.entity.EntityCollection; 065 import org.jfree.chart.event.RendererChangeEvent; 066 import org.jfree.chart.labels.CategoryItemLabelGenerator; 067 import org.jfree.chart.labels.CategoryToolTipGenerator; 068 import org.jfree.chart.plot.CategoryPlot; 069 import org.jfree.chart.plot.PlotOrientation; 070 import org.jfree.data.category.CategoryDataset; 071 import org.jfree.data.gantt.GanttCategoryDataset; 072 import org.jfree.ui.RectangleEdge; 073 074 /** 075 * A renderer for simple Gantt charts. 076 */ 077 public class GanttRenderer extends IntervalBarRenderer 078 implements Serializable { 079 080 /** For serialization. */ 081 private static final long serialVersionUID = -4010349116350119512L; 082 083 /** The paint for displaying the percentage complete. */ 084 private Paint completePaint; 085 086 /** The paint for displaying the incomplete part of a task. */ 087 private Paint incompletePaint; 088 089 /** 090 * Controls the starting edge of the progress indicator (expressed as a 091 * percentage of the overall bar width). 092 */ 093 private double startPercent; 094 095 /** 096 * Controls the ending edge of the progress indicator (expressed as a 097 * percentage of the overall bar width). 098 */ 099 private double endPercent; 100 101 /** 102 * Creates a new renderer. 103 */ 104 public GanttRenderer() { 105 super(); 106 setIncludeBaseInRange(false); 107 this.completePaint = Color.green; 108 this.incompletePaint = Color.red; 109 this.startPercent = 0.35; 110 this.endPercent = 0.65; 111 } 112 113 /** 114 * Returns the paint used to show the percentage complete. 115 * 116 * @return The paint (never <code>null</code>. 117 */ 118 public Paint getCompletePaint() { 119 return this.completePaint; 120 } 121 122 /** 123 * Sets the paint used to show the percentage complete and sends a 124 * {@link RendererChangeEvent} to all registered listeners. 125 * 126 * @param paint the paint (<code>null</code> not permitted). 127 */ 128 public void setCompletePaint(Paint paint) { 129 if (paint == null) { 130 throw new IllegalArgumentException("Null 'paint' argument."); 131 } 132 this.completePaint = paint; 133 notifyListeners(new RendererChangeEvent(this)); 134 } 135 136 /** 137 * Returns the paint used to show the percentage incomplete. 138 * 139 * @return The paint (never <code>null</code>). 140 */ 141 public Paint getIncompletePaint() { 142 return this.incompletePaint; 143 } 144 145 /** 146 * Sets the paint used to show the percentage incomplete and sends a 147 * {@link RendererChangeEvent} to all registered listeners. 148 * 149 * @param paint the paint (<code>null</code> not permitted). 150 */ 151 public void setIncompletePaint(Paint paint) { 152 if (paint == null) { 153 throw new IllegalArgumentException("Null 'paint' argument."); 154 } 155 this.incompletePaint = paint; 156 notifyListeners(new RendererChangeEvent(this)); 157 } 158 159 /** 160 * Returns the position of the start of the progress indicator, as a 161 * percentage of the bar width. 162 * 163 * @return The start percent. 164 */ 165 public double getStartPercent() { 166 return this.startPercent; 167 } 168 169 /** 170 * Sets the position of the start of the progress indicator, as a 171 * percentage of the bar width. 172 * 173 * @param percent the percent. 174 */ 175 public void setStartPercent(double percent) { 176 this.startPercent = percent; 177 notifyListeners(new RendererChangeEvent(this)); 178 } 179 180 /** 181 * Returns the position of the end of the progress indicator, as a 182 * percentage of the bar width. 183 * 184 * @return The end percent. 185 */ 186 public double getEndPercent() { 187 return this.endPercent; 188 } 189 190 /** 191 * Sets the position of the end of the progress indicator, as a percentage 192 * of the bar width. 193 * 194 * @param percent the percent. 195 */ 196 public void setEndPercent(double percent) { 197 this.endPercent = percent; 198 notifyListeners(new RendererChangeEvent(this)); 199 } 200 201 /** 202 * Draws the bar for a single (series, category) data item. 203 * 204 * @param g2 the graphics device. 205 * @param state the renderer state. 206 * @param dataArea the data area. 207 * @param plot the plot. 208 * @param domainAxis the domain axis. 209 * @param rangeAxis the range axis. 210 * @param dataset the dataset. 211 * @param row the row index (zero-based). 212 * @param column the column index (zero-based). 213 * @param pass the pass index. 214 */ 215 public void drawItem(Graphics2D g2, 216 CategoryItemRendererState state, 217 Rectangle2D dataArea, 218 CategoryPlot plot, 219 CategoryAxis domainAxis, 220 ValueAxis rangeAxis, 221 CategoryDataset dataset, 222 int row, 223 int column, 224 int pass) { 225 226 if (dataset instanceof GanttCategoryDataset) { 227 GanttCategoryDataset gcd = (GanttCategoryDataset) dataset; 228 drawTasks( 229 g2, state, dataArea, plot, domainAxis, rangeAxis, gcd, 230 row, column 231 ); 232 } 233 else { // let the superclass handle it... 234 super.drawItem( 235 g2, state, dataArea, plot, domainAxis, rangeAxis, 236 dataset, row, column, pass 237 ); 238 } 239 240 } 241 242 /** 243 * Draws the tasks/subtasks for one item. 244 * 245 * @param g2 the graphics device. 246 * @param state the renderer state. 247 * @param dataArea the data plot area. 248 * @param plot the plot. 249 * @param domainAxis the domain axis. 250 * @param rangeAxis the range axis. 251 * @param dataset the data. 252 * @param row the row index (zero-based). 253 * @param column the column index (zero-based). 254 */ 255 protected void drawTasks(Graphics2D g2, 256 CategoryItemRendererState state, 257 Rectangle2D dataArea, 258 CategoryPlot plot, 259 CategoryAxis domainAxis, 260 ValueAxis rangeAxis, 261 GanttCategoryDataset dataset, 262 int row, 263 int column) { 264 265 int count = dataset.getSubIntervalCount(row, column); 266 if (count == 0) { 267 drawTask( 268 g2, state, dataArea, plot, domainAxis, rangeAxis, 269 dataset, row, column 270 ); 271 } 272 273 for (int subinterval = 0; subinterval < count; subinterval++) { 274 275 RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); 276 277 // value 0 278 Number value0 = dataset.getStartValue(row, column, subinterval); 279 if (value0 == null) { 280 return; 281 } 282 double translatedValue0 = rangeAxis.valueToJava2D( 283 value0.doubleValue(), dataArea, rangeAxisLocation 284 ); 285 286 // value 1 287 Number value1 = dataset.getEndValue(row, column, subinterval); 288 if (value1 == null) { 289 return; 290 } 291 double translatedValue1 = rangeAxis.valueToJava2D( 292 value1.doubleValue(), dataArea, rangeAxisLocation 293 ); 294 295 if (translatedValue1 < translatedValue0) { 296 double temp = translatedValue1; 297 translatedValue1 = translatedValue0; 298 translatedValue0 = temp; 299 } 300 301 double rectStart = calculateBarW0( 302 plot, plot.getOrientation(), dataArea, domainAxis, state, 303 row, column 304 ); 305 double rectLength = Math.abs(translatedValue1 - translatedValue0); 306 double rectBreadth = state.getBarWidth(); 307 308 // DRAW THE BARS... 309 Rectangle2D bar = null; 310 311 if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 312 bar = new Rectangle2D.Double( 313 translatedValue0, rectStart, rectLength, rectBreadth 314 ); 315 } 316 else if (plot.getOrientation() == PlotOrientation.VERTICAL) { 317 bar = new Rectangle2D.Double( 318 rectStart, translatedValue0, rectBreadth, rectLength 319 ); 320 } 321 322 Rectangle2D completeBar = null; 323 Rectangle2D incompleteBar = null; 324 Number percent = dataset.getPercentComplete( 325 row, column, subinterval 326 ); 327 double start = getStartPercent(); 328 double end = getEndPercent(); 329 if (percent != null) { 330 double p = percent.doubleValue(); 331 if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 332 completeBar = new Rectangle2D.Double( 333 translatedValue0, 334 rectStart + start * rectBreadth, 335 rectLength * p, 336 rectBreadth * (end - start) 337 ); 338 incompleteBar = new Rectangle2D.Double( 339 translatedValue0 + rectLength * p, 340 rectStart + start * rectBreadth, 341 rectLength * (1 - p), 342 rectBreadth * (end - start) 343 ); 344 } 345 else if (plot.getOrientation() == PlotOrientation.VERTICAL) { 346 completeBar = new Rectangle2D.Double( 347 rectStart + start * rectBreadth, 348 translatedValue0 + rectLength * (1 - p), 349 rectBreadth * (end - start), 350 rectLength * p 351 ); 352 incompleteBar = new Rectangle2D.Double( 353 rectStart + start * rectBreadth, 354 translatedValue0, 355 rectBreadth * (end - start), 356 rectLength * (1 - p) 357 ); 358 } 359 360 } 361 362 Paint seriesPaint = getItemPaint(row, column); 363 g2.setPaint(seriesPaint); 364 g2.fill(bar); 365 if (completeBar != null) { 366 g2.setPaint(getCompletePaint()); 367 g2.fill(completeBar); 368 } 369 if (incompleteBar != null) { 370 g2.setPaint(getIncompletePaint()); 371 g2.fill(incompleteBar); 372 } 373 if (isDrawBarOutline() 374 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 375 g2.setStroke(getItemStroke(row, column)); 376 g2.setPaint(getItemOutlinePaint(row, column)); 377 g2.draw(bar); 378 } 379 380 // collect entity and tool tip information... 381 if (state.getInfo() != null) { 382 EntityCollection entities = state.getEntityCollection(); 383 if (entities != null) { 384 String tip = null; 385 if (getToolTipGenerator(row, column) != null) { 386 tip = getToolTipGenerator(row, column).generateToolTip( 387 dataset, row, column 388 ); 389 } 390 String url = null; 391 if (getItemURLGenerator(row, column) != null) { 392 url = getItemURLGenerator(row, column).generateURL( 393 dataset, row, column 394 ); 395 } 396 CategoryItemEntity entity = new CategoryItemEntity( 397 bar, tip, url, dataset, row, 398 dataset.getColumnKey(column), column 399 ); 400 entities.add(entity); 401 } 402 } 403 } 404 } 405 406 /** 407 * Draws a single task. 408 * 409 * @param g2 the graphics device. 410 * @param state the renderer state. 411 * @param dataArea the data plot area. 412 * @param plot the plot. 413 * @param domainAxis the domain axis. 414 * @param rangeAxis the range axis. 415 * @param dataset the data. 416 * @param row the row index (zero-based). 417 * @param column the column index (zero-based). 418 */ 419 protected void drawTask(Graphics2D g2, 420 CategoryItemRendererState state, 421 Rectangle2D dataArea, 422 CategoryPlot plot, 423 CategoryAxis domainAxis, 424 ValueAxis rangeAxis, 425 GanttCategoryDataset dataset, 426 int row, 427 int column) { 428 429 PlotOrientation orientation = plot.getOrientation(); 430 431 RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); 432 433 // Y0 434 Number value0 = dataset.getEndValue(row, column); 435 if (value0 == null) { 436 return; 437 } 438 double java2dValue0 = rangeAxis.valueToJava2D( 439 value0.doubleValue(), dataArea, rangeAxisLocation 440 ); 441 442 // Y1 443 Number value1 = dataset.getStartValue(row, column); 444 if (value1 == null) { 445 return; 446 } 447 double java2dValue1 = rangeAxis.valueToJava2D( 448 value1.doubleValue(), dataArea, rangeAxisLocation 449 ); 450 451 if (java2dValue1 < java2dValue0) { 452 double temp = java2dValue1; 453 java2dValue1 = java2dValue0; 454 java2dValue0 = temp; 455 Number tempNum = value1; 456 value1 = value0; 457 value0 = tempNum; 458 } 459 460 double rectStart = calculateBarW0( 461 plot, orientation, dataArea, domainAxis, state, row, column 462 ); 463 double rectBreadth = state.getBarWidth(); 464 double rectLength = Math.abs(java2dValue1 - java2dValue0); 465 466 Rectangle2D bar = null; 467 if (orientation == PlotOrientation.HORIZONTAL) { 468 bar = new Rectangle2D.Double( 469 java2dValue0, rectStart, rectLength, rectBreadth 470 ); 471 } 472 else if (orientation == PlotOrientation.VERTICAL) { 473 bar = new Rectangle2D.Double( 474 rectStart, java2dValue1, rectBreadth, rectLength 475 ); 476 } 477 478 Rectangle2D completeBar = null; 479 Rectangle2D incompleteBar = null; 480 Number percent = dataset.getPercentComplete(row, column); 481 double start = getStartPercent(); 482 double end = getEndPercent(); 483 if (percent != null) { 484 double p = percent.doubleValue(); 485 if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 486 completeBar = new Rectangle2D.Double( 487 java2dValue0, 488 rectStart + start * rectBreadth, 489 rectLength * p, 490 rectBreadth * (end - start) 491 ); 492 incompleteBar = new Rectangle2D.Double( 493 java2dValue0 + rectLength * p, 494 rectStart + start * rectBreadth, 495 rectLength * (1 - p), 496 rectBreadth * (end - start) 497 ); 498 } 499 else if (plot.getOrientation() == PlotOrientation.VERTICAL) { 500 completeBar = new Rectangle2D.Double( 501 rectStart + start * rectBreadth, 502 java2dValue1 + rectLength * (1 - p), 503 rectBreadth * (end - start), 504 rectLength * p 505 ); 506 incompleteBar = new Rectangle2D.Double( 507 rectStart + start * rectBreadth, 508 java2dValue1, 509 rectBreadth * (end - start), 510 rectLength * (1 - p) 511 ); 512 } 513 514 } 515 516 Paint seriesPaint = getItemPaint(row, column); 517 g2.setPaint(seriesPaint); 518 g2.fill(bar); 519 520 if (completeBar != null) { 521 g2.setPaint(getCompletePaint()); 522 g2.fill(completeBar); 523 } 524 if (incompleteBar != null) { 525 g2.setPaint(getIncompletePaint()); 526 g2.fill(incompleteBar); 527 } 528 529 // draw the outline... 530 if (isDrawBarOutline() 531 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 532 Stroke stroke = getItemOutlineStroke(row, column); 533 Paint paint = getItemOutlinePaint(row, column); 534 if (stroke != null && paint != null) { 535 g2.setStroke(stroke); 536 g2.setPaint(paint); 537 g2.draw(bar); 538 } 539 } 540 541 CategoryItemLabelGenerator generator 542 = getItemLabelGenerator(row, column); 543 if (generator != null && isItemLabelVisible(row, column)) { 544 drawItemLabel( 545 g2, dataset, row, column, plot, generator, bar, false 546 ); 547 } 548 549 // collect entity and tool tip information... 550 if (state.getInfo() != null) { 551 EntityCollection entities = state.getEntityCollection(); 552 if (entities != null) { 553 String tip = null; 554 CategoryToolTipGenerator tipster = getToolTipGenerator( 555 row, column 556 ); 557 if (tipster != null) { 558 tip = tipster.generateToolTip(dataset, row, column); 559 } 560 String url = null; 561 if (getItemURLGenerator(row, column) != null) { 562 url = getItemURLGenerator(row, column).generateURL( 563 dataset, row, column 564 ); 565 } 566 CategoryItemEntity entity = new CategoryItemEntity( 567 bar, tip, url, dataset, row, 568 dataset.getColumnKey(column), column 569 ); 570 entities.add(entity); 571 } 572 } 573 574 } 575 576 }