/* Copyright 2003-2012 Dmitry Barashev, GanttProject Team This file is part of GanttProject, an opensource project management tool. GanttProject is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GanttProject is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GanttProject. If not, see <http://www.gnu.org/licenses/>. */ package net.sourceforge.ganttproject.chart; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import com.google.common.base.Supplier; import net.sourceforge.ganttproject.util.PropertiesUtil; import biz.ganttproject.core.chart.canvas.Canvas; import biz.ganttproject.core.chart.canvas.Canvas.Line; import biz.ganttproject.core.chart.canvas.Canvas.Polygon; import biz.ganttproject.core.chart.canvas.Canvas.Rectangle; import biz.ganttproject.core.chart.canvas.Canvas.Text; import biz.ganttproject.core.chart.canvas.Canvas.TextGroup; import biz.ganttproject.core.chart.canvas.Painter; import biz.ganttproject.core.chart.render.LineRenderer; import biz.ganttproject.core.chart.render.PolygonRenderer; import biz.ganttproject.core.chart.render.RectangleRenderer; import biz.ganttproject.core.chart.render.TextPainter; import biz.ganttproject.core.option.ChangeValueEvent; import biz.ganttproject.core.option.ChangeValueListener; /** * Implements styled painters for the available primitives (see * {@link Canvas}) * * @author bard */ public class StyledPainterImpl implements Painter { private Graphics2D myGraphics; private final Map<String, RectanglePainter> myStyle2painter = new HashMap<String, RectanglePainter>(); private ChartUIConfiguration myConfig; private final int margin; /** List X coordinates used to draw polygons */ private int[] myXPoints = new int[4]; /** List Y coordinates used to draw polygons */ private int[] myYPoints = new int[4]; private final Properties myProperties; private final TextPainter myTextPainter; private final LineRenderer myLineRenderer; private final RectangleRenderer myRectangleRenderer; private final PolygonRenderer myPolygonRenderer; /** Default stroke used for the primitives */ private final static BasicStroke defaultStroke = new BasicStroke(); public StyledPainterImpl(final ChartUIConfiguration config) { myConfig = config; margin = myConfig.getMargin(); myStyle2painter.put("task.progress", new ColouredRectanglePainter(Color.BLACK)); myStyle2painter.put("task.progress.end", new ColouredRectanglePainter(Color.BLACK)); myStyle2painter.put("load.normal", myResourceLoadPainter); myStyle2painter.put("load.normal.first", myResourceLoadPainter); myStyle2painter.put("load.normal.last", myResourceLoadPainter); myStyle2painter.put("load.normal.first.last", myResourceLoadPainter); myStyle2painter.put("load.overload", myResourceLoadPainter); myStyle2painter.put("dependency.arrow.down", myArrowDownPainter); myStyle2painter.put("load.overload.first", myResourceLoadPainter); myStyle2painter.put("load.overload.last", myResourceLoadPainter); myStyle2painter.put("load.overload.first.last", myResourceLoadPainter); myStyle2painter.put("dependency.arrow.up", myArrowUpPainter); myStyle2painter.put("dependency.arrow.left", myArrowLeftPainter); myStyle2painter.put("dependency.arrow.right", myArrowRightPainter); myStyle2painter.put("dayoff", myDayOffPainter); myStyle2painter.put("load.underload", myResourceLoadPainter); myStyle2painter.put("load.underload.first", myResourceLoadPainter); myStyle2painter.put("load.underload.last", myResourceLoadPainter); myStyle2painter.put("load.underload.first.last", myResourceLoadPainter); myStyle2painter.put("previousStateTask", myPreviousStateTaskRectanglePainter); myProperties = new Properties(); PropertiesUtil.loadProperties(myProperties, "/chart.properties"); config.getChartStylesOption().addChangeValueListener(new ChangeValueListener() { @Override public void changeValue(ChangeValueEvent event) { for (Entry<String, String> entry : config.getChartStylesOption().getValues()) { myProperties.put(entry.getKey(), entry.getValue()); } } }); myTextPainter = new TextPainter(myProperties, new Supplier<Font>() { public Font get() { return config.getChartFont(); } }); myLineRenderer = new LineRenderer(myProperties); myRectangleRenderer = new RectangleRenderer(myProperties); myPolygonRenderer = new PolygonRenderer(myProperties); } public void setGraphics(Graphics g) { myGraphics = (Graphics2D) g; myTextPainter.setGraphics(myGraphics); myLineRenderer.setGraphics(myGraphics); myRectangleRenderer.setGraphics(myGraphics); myPolygonRenderer.setGraphics(myGraphics); } @Override public void prePaint() { myGraphics.setStroke(defaultStroke); myGraphics.setFont(myConfig.getChartFont()); } @Override public void paint(Canvas.Rectangle next) { assert myGraphics != null; if (myRectangleRenderer.render(next)) { return; } RectanglePainter painter = myStyle2painter.get(next.getStyle()); if (painter != null) { // Use found painter painter.paint(next); } else { // Use default painter, since no painter was provided if (next.getBackgroundColor() == null) { Color foreColor = next.getForegroundColor(); if (foreColor == null) { foreColor = Color.BLACK; } myGraphics.setColor(foreColor); myGraphics.drawRect(next.getLeftX(), next.getTopY(), next.getWidth(), next.getHeight()); } else { myGraphics.setColor(next.getBackgroundColor()); myGraphics.fillRect(next.getLeftX(), next.getTopY(), next.getWidth(), next.getHeight()); } } } /** * Interface providing a method to paint a rectangle (currently, used to draw * many more other things...) */ private interface RectanglePainter { public void paint(Canvas.Rectangle next); } private final RectanglePainter myArrowDownPainter = new RectanglePainter() { @Override public void paint(Rectangle next) { myXPoints[0] = next.getLeftX(); myXPoints[1] = next.getRightX(); myXPoints[2] = next.getMiddleX(); myYPoints[0] = next.getTopY(); myYPoints[1] = next.getTopY(); myYPoints[2] = next.getBottomY(); myGraphics.setColor(Color.BLACK); myGraphics.fillPolygon(myXPoints, myYPoints, 3); } }; private final RectanglePainter myArrowUpPainter = new RectanglePainter() { @Override public void paint(Rectangle next) { myXPoints[0] = next.getLeftX(); myXPoints[1] = next.getRightX(); myXPoints[2] = next.getMiddleX(); myYPoints[0] = next.getBottomY(); myYPoints[1] = next.getBottomY(); myYPoints[2] = next.getTopY(); myGraphics.setColor(Color.BLACK); myGraphics.fillPolygon(myXPoints, myYPoints, 3); } }; private final RectanglePainter myArrowLeftPainter = new RectanglePainter() { @Override public void paint(Rectangle next) { Graphics g = myGraphics; g.setColor(Color.BLACK); myXPoints[0] = next.getLeftX(); myXPoints[1] = next.getRightX(); myXPoints[2] = next.getRightX(); myYPoints[0] = next.getMiddleY(); myYPoints[1] = next.getTopY(); myYPoints[2] = next.getBottomY(); g.fillPolygon(myXPoints, myYPoints, 3); } }; private final RectanglePainter myArrowRightPainter = new RectanglePainter() { @Override public void paint(Rectangle next) { myXPoints[0] = next.getLeftX(); myXPoints[1] = next.getRightX(); myXPoints[2] = next.getLeftX(); myYPoints[0] = next.getTopY(); myYPoints[1] = next.getMiddleY(); myYPoints[2] = next.getBottomY(); myGraphics.setColor(Color.BLACK); myGraphics.fillPolygon(myXPoints, myYPoints, 3); } }; private final RectanglePainter myDayOffPainter = new RectanglePainter() { @Override public void paint(Rectangle next) { int margin = StyledPainterImpl.this.margin - 3; Color c = myConfig.getDayOffColor(); myGraphics.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), 100)); myGraphics.fillRect(next.getLeftX(), next.getTopY() + margin, next.getWidth(), next.getHeight() - 2 * margin); myGraphics.setColor(Color.BLACK); myGraphics.drawLine(next.getLeftX(), next.getTopY() + margin, next.getLeftX(), next.getBottomY() - margin); myGraphics.drawLine(next.getLeftX(), next.getTopY() + margin, next.getRightX(), next.getTopY() + margin); myGraphics.drawLine(next.getLeftX(), next.getBottomY() - margin, next.getRightX(), next.getBottomY() - margin); myGraphics.drawLine(next.getRightX(), next.getTopY() + margin, next.getRightX(), next.getBottomY() - margin); } }; private final RectanglePainter myResourceLoadPainter = new RectanglePainter() { @Override public void paint(Rectangle next) { String style = next.getStyle(); Color c; if (style.indexOf("overload") > 0) { c = myConfig.getResourceOverloadColor(); } else if (style.indexOf("underload") > 0) { c = myConfig.getResourceUnderLoadColor(); } else { c = myConfig.getResourceNormalLoadColor(); } myGraphics.setColor(c); myGraphics.fillRect(next.getLeftX(), next.getTopY() + margin, next.getWidth(), next.getHeight() - 2 * margin); if (style.indexOf(".first") > 0) { myGraphics.setColor(Color.BLACK); myGraphics.drawLine(next.getLeftX(), next.getTopY() + margin, next.getLeftX(), next.getBottomY() - margin); } if (style.indexOf(".last") > 0) { myGraphics.setColor(Color.BLACK); myGraphics.drawLine(next.getRightX(), next.getTopY() + margin, next.getRightX(), next.getBottomY() - margin); } myGraphics.setColor(Color.BLACK); // ResourceLoadRenderer.ResourceLoad load = (ResourceLoadRenderer.ResourceLoad) next.getModelObject(); // int loadInt = Math.round(load.getLoad()); // String loadStr = loadInt + "%"; // int emsLength = myTextLengthCalculator.getTextLength(loadStr); // boolean displayLoad = (loadInt != 100 && emsLength <= next.getWidth()); // if (displayLoad) { // myGraphics.drawString(loadStr, next.getMiddleX() - myTextLengthCalculator.getTextLength(loadStr) / 2, // next.getTopY() + margin + next.getHeight() / 2); // myGraphics.drawLine(next.getLeftX(), next.getTopY() + margin, next.getLeftX(), next.getBottomY() - margin); // } myGraphics.setColor(Color.BLACK); myGraphics.drawLine(next.getLeftX(), next.getTopY() + margin, next.getRightX(), next.getTopY() + margin); myGraphics.drawLine(next.getLeftX(), next.getBottomY() - margin, next.getRightX(), next.getBottomY() - margin); } }; private final RectanglePainter myPreviousStateTaskRectanglePainter = new RectanglePainter() { private int[] myXPoints = new int[4]; private int[] myYPoints = new int[4]; @Override public void paint(Canvas.Rectangle next) { Graphics g = myGraphics; final Color c; if (next.hasStyle("earlier")) { c = myConfig.getEarlierPreviousTaskColor(); } else if (next.hasStyle("later")) { c = myConfig.getLaterPreviousTaskColor(); } else { c = myConfig.getPreviousTaskColor(); } g.setColor(c); if (next.hasStyle("milestone")) { int middleX = (next.getWidth() <= next.getHeight()) ? next.getRightX() - next.getWidth() / 2 : next.getLeftX() + next.getHeight() / 2; int middleY = next.getMiddleY(); myXPoints[0] = next.getLeftX() + 2; myYPoints[0] = middleY; myXPoints[1] = middleX + 3; myYPoints[1] = next.getTopY() - 1; myXPoints[2] = (next.getWidth() <= next.getHeight()) ? next.getRightX() + 4 : next.getLeftX() + next.getHeight() + 4; myYPoints[2] = middleY; myXPoints[3] = middleX + 3; myYPoints[3] = next.getBottomY() + 1; g.fillPolygon(myXPoints, myYPoints, 4); } else if (next.hasStyle("super")) { g.fillRect(next.getLeftX(), next.getTopY() + next.getHeight() - 6, next.getWidth(), 3); int topy = next.getTopY() + next.getHeight() - 3; int rightx = next.getLeftX() + next.getWidth(); g.fillPolygon(new int[] { rightx - 3, rightx, rightx }, new int[] { topy, topy, topy + 3 }, 3); } else { g.fillRect(next.getLeftX(), next.getTopY(), next.getWidth(), next.getHeight()); g.setColor(Color.black); g.drawLine(next.getLeftX(), next.getTopY(), next.getRightX(), next.getTopY()); g.drawLine(next.getLeftX(), next.getBottomY(), next.getRightX(), next.getBottomY()); if (next.hasStyle("start")) { g.drawLine(next.getLeftX(), next.getTopY(), next.getLeftX(), next.getBottomY()); } if (next.hasStyle("end")) { g.drawLine(next.getRightX(), next.getTopY(), next.getRightX(), next.getBottomY()); } } } }; private class ColouredRectanglePainter implements RectanglePainter { private Color myColor; private ColouredRectanglePainter(Color color) { myColor = color; } @Override public void paint(Rectangle next) { myGraphics.setColor(myColor); myGraphics.fillRect(next.getLeftX(), next.getTopY(), next.getWidth(), next.getHeight()); } } @Override public void paint(Line line) { myLineRenderer.renderLine(line); } @Override public void paint(Text text) { myTextPainter.paint(text); } @Override public void paint(TextGroup textGroup) { myTextPainter.paint(textGroup); } @Override public void paint(Polygon p) { myPolygonRenderer.render(p); } }