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    }