/* Copyright 2003-2012 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 biz.ganttproject.core.chart.scene.gantt; import biz.ganttproject.core.chart.canvas.Canvas; import biz.ganttproject.core.chart.canvas.Canvas.Line; import biz.ganttproject.core.chart.scene.BarChartActivity; import biz.ganttproject.core.chart.scene.BarChartConnector; import java.awt.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Renders dependency lines between tasks. * * @author Dmitry Barashev */ public class DependencySceneBuilder<T, D extends BarChartConnector<T, D>> { private final Canvas myTaskCanvas; private final Canvas myOutputCanvas; private final ChartApi myChartApi; private final TaskApi<T, D> myTaskApi; private int myBarHeight; private Canvas.Arrow myFinishArrow; public interface TaskApi<T, D> { boolean isMilestone(T task); Dimension getUnitVector(BarChartActivity<T> activity, D dependency); String getStyle(D dependency); Iterable<D> getConnectors(T task); List<T> getTasks(); } public interface ChartApi { int getBarHeight(); } public DependencySceneBuilder(Canvas taskCanvas, Canvas outputCanvas, TaskApi<T, D> taskApi, ChartApi chartApi) { myTaskApi = taskApi; myChartApi = chartApi; //myVisibleTasks = visibleTasks; myTaskCanvas = taskCanvas; myOutputCanvas = outputCanvas; myFinishArrow = Canvas.Arrow.FINISH; myBarHeight = -1; } public void build() { List<Connector> dependencyDrawData = prepareDependencyDrawData(); drawDependencies(dependencyDrawData); } public void drawDependencies(Collection<Connector> connectors) { if (myChartApi.getBarHeight() != myBarHeight) { myFinishArrow = new Canvas.Arrow((int)(0.7f * myChartApi.getBarHeight()), (int)(0.3f*myChartApi.getBarHeight())); myBarHeight = myChartApi.getBarHeight(); } Canvas primitiveContainer = myOutputCanvas; for (Connector connector : connectors) { Connector.Vector dependantVector = connector.getEnd(); Connector.Vector dependeeVector = connector.getStart(); // Determine the line style (depending on type of dependency) String lineStyle = connector.getStyleName(); if (dependeeVector.getHProjection().reaches(dependantVector.getHProjection().getPoint())) { // when dependee.end <= dependant.start && dependency.type is // any // or dependee.end <= dependant.end && dependency.type==FF // or dependee.start >= dependant.end && dependency.type==SF Point first = new Point(dependeeVector.getPoint().x, dependeeVector.getPoint().y); int xEntry = dependantVector.getPoint().x; int yEntry = dependantVector.getPoint().y; Point second = new Point(xEntry, dependeeVector.getPoint().y); Point third = new Point(xEntry, yEntry); primitiveContainer.createLine(first.x, first.y, second.x, second.y).setStyle(lineStyle); Line secondLine = primitiveContainer.createLine(second.x, second.y, third.x, third.y); secondLine.setStyle(lineStyle); secondLine.setArrow(myFinishArrow); } else if (dependantVector.getHProjection().reaches(dependeeVector.getHProjection().getPoint(3))) { Point first = dependeeVector.getPoint(3); Point second = new Point(first.x, dependantVector.getPoint().y); primitiveContainer.createLine(dependeeVector.getPoint().x, dependeeVector.getPoint().y, first.x, first.y).setStyle(lineStyle); primitiveContainer.createLine(first.x, first.y, second.x, second.y).setStyle(lineStyle); Line line = primitiveContainer.createLine(second.x, second.y, dependantVector.getPoint().x, dependantVector.getPoint().y); line.setStyle(lineStyle); line.setArrow(myFinishArrow); } else { Point first = dependeeVector.getPoint(3); Point forth = dependantVector.getPoint(3); Point second = new Point(first.x, (first.y + forth.y) / 2); Point third = new Point(forth.x, (first.y + forth.y) / 2); primitiveContainer.createLine(dependeeVector.getPoint().x, dependeeVector.getPoint().y, first.x, first.y).setStyle(lineStyle); primitiveContainer.createLine(first.x, first.y, second.x, second.y).setStyle(lineStyle); primitiveContainer.createLine(second.x, second.y, third.x, third.y).setStyle(lineStyle); primitiveContainer.createLine(third.x, third.y, forth.x, forth.y).setStyle(lineStyle); primitiveContainer.createLine(forth.x, forth.y, dependantVector.getPoint().x, dependantVector.getPoint().y).setStyle(lineStyle); } } } private List<Connector> prepareDependencyDrawData() { List<Connector> result = new ArrayList<Connector>(); for (T t : myTaskApi.getTasks()) { if (t != null) { for (D td : myTaskApi.getConnectors(t)) { prepareDependencyDrawData(td, result); } } } return result; } private void prepareDependencyDrawData(D connector, List<Connector> result) { BarChartActivity<T> dependant = connector.getEnd(); BarChartActivity<T> dependee = connector.getStart(); Canvas graphicPrimitiveContainer = myTaskCanvas; Canvas.Polygon dependantRectangle = (Canvas.Polygon) graphicPrimitiveContainer.getPrimitive(dependant); if (dependantRectangle == null) { return; } Canvas.Polygon dependeeRectangle = (Canvas.Polygon) graphicPrimitiveContainer.getPrimitive(dependee); if (dependeeRectangle == null) { return; } if (!dependantRectangle.isVisible() && !dependeeRectangle.isVisible()) { return; } if (!dependeeRectangle.isVisible() && dependantRectangle.getWidth() == 0) { return; } final int ysign = Integer.signum(dependeeRectangle.getMiddleY() - dependantRectangle.getMiddleY()); final Dimension dependantDirection = myTaskApi.getUnitVector(dependant, connector); final int yDantEntry = ysign > 0 ? dependantRectangle.getBottomY() : dependantRectangle.getTopY(); int xDantEntry; if (myTaskApi.isMilestone(dependant.getOwner())) { xDantEntry = dependantRectangle.getMiddleX(); } else if (dependantDirection == Connector.Vector.WEST) { xDantEntry = dependantRectangle.getLeftX() + 3; } else if (dependantDirection == Connector.Vector.EAST) { xDantEntry = dependantRectangle.getRightX() - 3; } else { xDantEntry = dependantRectangle.getMiddleX(); } Connector.Vector dependantVector = new Connector.Vector(new Point(xDantEntry, yDantEntry), dependantDirection); Dimension dependeeDirection = myTaskApi.getUnitVector(dependee, connector); int xDeeExit; int yDeeExit; if (myTaskApi.isMilestone(dependee.getOwner()) && xDantEntry == dependeeRectangle.getMiddleX()) { xDeeExit = xDantEntry; yDeeExit = ysign > 0 ? dependeeRectangle.getTopY() : dependeeRectangle.getBottomY(); } else { yDeeExit = dependeeRectangle.getMiddleY(); if (dependeeDirection == Connector.Vector.WEST) { xDeeExit = dependeeRectangle.getLeftX(); } else if (dependeeDirection == Connector.Vector.EAST) { xDeeExit = dependeeRectangle.getRightX(); } else { xDeeExit = dependeeRectangle.getMiddleX(); } } Connector.Vector dependeeVector = new Connector.Vector(new Point(xDeeExit, yDeeExit), dependeeDirection); result.add(new Connector(dependeeVector, dependantVector, myTaskApi.getStyle(connector.getImpl()))); } }