001 /** 002 * planningtool - A Planning Tool with Critiquing Support. 003 * 004 * Copyright (C) 2006 olale 005 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU General Public License 008 * as published by the Free Software Foundation; either version 2 009 * of the License, or (at your option) any later version. 010 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 016 * You should have received a copy of the GNU General Public License 017 * along with this program; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 020 * Contact information: 021 * E-mail: olale@ida.liu.se 022 * olale@lysator.liu.se 023 */ 024 package se.liu.ida.critiquer.gui; 025 026 import java.awt.Graphics2D; 027 import java.awt.Paint; 028 import java.awt.Stroke; 029 import java.awt.geom.Rectangle2D; 030 import java.util.ArrayList; 031 import java.util.HashMap; 032 033 import org.jfree.chart.axis.CategoryAxis; 034 import org.jfree.chart.axis.ValueAxis; 035 import org.jfree.chart.entity.CategoryItemEntity; 036 import org.jfree.chart.entity.EntityCollection; 037 import org.jfree.chart.labels.CategoryItemLabelGenerator; 038 import org.jfree.chart.labels.CategoryToolTipGenerator; 039 import org.jfree.chart.plot.CategoryPlot; 040 import org.jfree.chart.plot.PlotOrientation; 041 import org.jfree.chart.renderer.category.CategoryItemRendererState; 042 import org.jfree.chart.renderer.category.GanttRenderer; 043 import org.jfree.data.gantt.GanttCategoryDataset; 044 import org.jfree.data.gantt.TaskSeriesCollection; 045 import org.jfree.ui.RectangleEdge; 046 047 public class TimeLineRenderer extends GanttRenderer { 048 049 050 /** 051 * 052 */ 053 private static final long serialVersionUID = 1L; 054 /* 055 * Each tasks series in the collection has to do with one agent. Tasks can occur in more than one series 056 * */ 057 private TaskSeriesCollection taskSeries; 058 private HashMap<Comparable,Paint> colors; 059 060 private ArrayList<TimeLineRenderingListener> listeners = new ArrayList<TimeLineRenderingListener>(); 061 public TimeLineRenderer(TaskSeriesCollection c,HashMap<Comparable,Paint> colorMap){ 062 colors=colorMap; 063 taskSeries = c; 064 065 } 066 067 /** 068 * Use the same colors for the resources. These colors are defined by Agent.setTimeLineColor 069 * 070 * */ 071 @Override 072 public Paint getSeriesPaint(int series) { 073 Comparable resourceName = taskSeries.getRowKey(series); 074 return colors.get(resourceName); 075 076 } 077 /** 078 * Draws a single task. 079 * 080 * @param g2 the graphics device. 081 * @param state the renderer state. 082 * @param dataArea the data plot area. 083 * @param plot the plot. 084 * @param domainAxis the domain axis. 085 * @param rangeAxis the range axis. 086 * @param dataset the data. 087 * @param row the row index (zero-based). 088 * @param column the column index (zero-based). 089 */ 090 protected void drawTask(Graphics2D g2, 091 CategoryItemRendererState state, 092 Rectangle2D dataArea, 093 CategoryPlot plot, 094 CategoryAxis domainAxis, 095 ValueAxis rangeAxis, 096 GanttCategoryDataset dataset, 097 int row, 098 int column) { 099 100 PlotOrientation orientation = plot.getOrientation(); 101 102 RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); 103 104 // Y0 105 Number value0 = dataset.getEndValue(row, column); 106 if (value0 == null) { 107 return; 108 } 109 double java2dValue0 = rangeAxis.valueToJava2D( 110 value0.doubleValue(), dataArea, rangeAxisLocation 111 ); 112 113 // Y1 114 Number value1 = dataset.getStartValue(row, column); 115 if (value1 == null) { 116 return; 117 } 118 double java2dValue1 = rangeAxis.valueToJava2D( 119 value1.doubleValue(), dataArea, rangeAxisLocation 120 ); 121 122 if (java2dValue1 < java2dValue0) { 123 double temp = java2dValue1; 124 java2dValue1 = java2dValue0; 125 java2dValue0 = temp; 126 Number tempNum = value1; 127 value1 = value0; 128 value0 = tempNum; 129 } 130 131 double rectStart = calculateBarW0( 132 plot, orientation, dataArea, domainAxis, state, row, column 133 ); 134 double rectBreadth = state.getBarWidth(); 135 double rectLength = Math.abs(java2dValue1 - java2dValue0); 136 137 Rectangle2D bar = null; 138 if (orientation == PlotOrientation.HORIZONTAL) { 139 bar = new Rectangle2D.Double( 140 java2dValue0, rectStart, rectLength, rectBreadth 141 ); 142 } 143 else if (orientation == PlotOrientation.VERTICAL) { 144 bar = new Rectangle2D.Double( 145 rectStart, java2dValue1, rectBreadth, rectLength 146 ); 147 } 148 149 Rectangle2D completeBar = null; 150 Rectangle2D incompleteBar = null; 151 Number percent = dataset.getPercentComplete(row, column); 152 double start = getStartPercent(); 153 double end = getEndPercent(); 154 if (percent != null) { 155 double p = percent.doubleValue(); 156 if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 157 completeBar = new Rectangle2D.Double( 158 java2dValue0, 159 rectStart + start * rectBreadth, 160 rectLength * p, 161 rectBreadth * (end - start) 162 ); 163 incompleteBar = new Rectangle2D.Double( 164 java2dValue0 + rectLength * p, 165 rectStart + start * rectBreadth, 166 rectLength * (1 - p), 167 rectBreadth * (end - start) 168 ); 169 } 170 else if (plot.getOrientation() == PlotOrientation.VERTICAL) { 171 completeBar = new Rectangle2D.Double( 172 rectStart + start * rectBreadth, 173 java2dValue1 + rectLength * (1 - p), 174 rectBreadth * (end - start), 175 rectLength * p 176 ); 177 incompleteBar = new Rectangle2D.Double( 178 rectStart + start * rectBreadth, 179 java2dValue1, 180 rectBreadth * (end - start), 181 rectLength * (1 - p) 182 ); 183 } 184 185 } 186 187 /* 188 * BEGIN Added code 189 * 190 * Add the bar to the list of bars for this task series 191 * */ 192 193 if (dataset instanceof TaskSeriesCollection) { 194 TaskSeriesCollection collection = (TaskSeriesCollection) dataset; 195 Comparable key = collection.getColumnKey(column); 196 notifyListeners(key,bar); 197 } 198 199 /* 200 * END Added code 201 * 202 * */ 203 204 Paint seriesPaint = getItemPaint(row, column); 205 g2.setPaint(seriesPaint); 206 g2.fill(bar); 207 208 if (completeBar != null) { 209 g2.setPaint(getCompletePaint()); 210 g2.fill(completeBar); 211 } 212 if (incompleteBar != null) { 213 g2.setPaint(getIncompletePaint()); 214 g2.fill(incompleteBar); 215 } 216 217 // draw the outline... 218 if (isDrawBarOutline() 219 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 220 Stroke stroke = getItemOutlineStroke(row, column); 221 Paint paint = getItemOutlinePaint(row, column); 222 if (stroke != null && paint != null) { 223 g2.setStroke(stroke); 224 g2.setPaint(paint); 225 g2.draw(bar); 226 } 227 } 228 229 CategoryItemLabelGenerator generator 230 = getItemLabelGenerator(row, column); 231 if (generator != null && isItemLabelVisible(row, column)) { 232 drawItemLabel( 233 g2, dataset, row, column, plot, generator, bar, false 234 ); 235 } 236 237 // collect entity and tool tip information... 238 if (state.getInfo() != null) { 239 EntityCollection entities = state.getEntityCollection(); 240 if (entities != null) { 241 String tip = null; 242 CategoryToolTipGenerator tipster = getToolTipGenerator( 243 row, column 244 ); 245 if (tipster != null) { 246 tip = tipster.generateToolTip(dataset, row, column); 247 } 248 String url = null; 249 if (getItemURLGenerator(row, column) != null) { 250 url = getItemURLGenerator(row, column).generateURL( 251 dataset, row, column 252 ); 253 } 254 CategoryItemEntity entity = new CategoryItemEntity( 255 bar, tip, url, dataset, row, 256 dataset.getColumnKey(column), column 257 ); 258 entities.add(entity); 259 } 260 } 261 262 } 263 264 /** 265 * @param key 266 * @param bar 267 */ 268 private void notifyListeners(Comparable key, Rectangle2D bar) { 269 ArrayList<TimeLineRenderingListener> l = listeners; 270 for (TimeLineRenderingListener listener : l) { 271 listener.updateActivityArea(key, bar); 272 } 273 } 274 275 public void addListener(TimeLineRenderingListener l) { 276 listeners.add(l); 277 } 278 279 280 281 }