/*
GanttProject is an opensource project management tool.
Copyright (C) 2005-2011 Bernoit Baranne, Julien Seiler, GanttProject Team
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.ganttproject.chart.pert;
import net.sourceforge.ganttproject.GanttExportSettings;
import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.chart.Chart;
import net.sourceforge.ganttproject.chart.export.ChartImageVisitor;
import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.util.StringUtils;
import org.ganttproject.chart.pert.PertChartAbstraction.TaskGraphNode;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* PERT chart implementation where nodes are tasks and links succession
* relations.
*
* @author bbaranne
* @author Julien Seiler
*
*/
public class ActivityOnNodePertChart extends PertChart {
/** List of abstract nodes. */
private List<TaskGraphNode> myTaskGraphNodes;
/** List of graphical arrows. */
private List<GraphicalArrow> myGraphicalArrows;
/** List of graphical nodes (in relation with abstract nodes) */
private List<GraphicalNode> myGraphicalNodes;
// private Map myMapPositionListOfNodes;
/** Number of columns */
private int nbCols;
/** PERT chart abstraction used to build graph. */
private PertChartAbstraction myPertAbstraction;
private int myMaxX = 1;
private int myMaxY = 1;
/** The currently mouse pressed graphical node. */
private GraphicalNode myPressedGraphicalNode;
/**
* Offset between the mouse pointer when clicked on a graphical node and the
* top left corner of this same node.
*/
private int myXClickedOffset, myYClickedOffset;
private final static GanttLanguage language = GanttLanguage.getInstance();
private final static int NODE_WIDTH = 110;// 205;
private final static int NODE_HEIGHT = 70;
private final static int X_GAP = 30;// 60;
private final static int Y_GAP = 15;// 30;
private final static int ARROW_HEIGHT = 10;
private final static int ARROW_WIDTH = 15;
private final static int ARROW_CORNER_WIDTH = 6;
private final static int X_OFFSET = 5;
private final static int Y_OFFSET = 5;
/** Color of the border of normal tasks. */
private final static Color NORMAL_COLOR = Color.BLUE.brighter();
/** Color of the border of supertasks. */
private final static Color SUPER_COLOR = Color.RED;
/** Color of the border of milestones. */
private final static Color MILESTONE_COLOR = Color.BLACK;
/** Color of the arrows. */
private final static Color ARROW_COLOR = Color.GRAY;
private final JScrollPane myScrollPane;
public ActivityOnNodePertChart() {
setBackground(Color.WHITE.brighter());
this.addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseDragged(final MouseEvent e) {
if (myPressedGraphicalNode != null) {
myPressedGraphicalNode.x = e.getX() - myXClickedOffset;
myPressedGraphicalNode.y = e.getY() - myYClickedOffset;
if (e.getX() > getPreferredSize().getWidth()) {
ActivityOnNodePertChart.this.setPreferredSize(new Dimension(myPressedGraphicalNode.x + getNodeWidth() + getxGap(),
(int) getPreferredSize().getHeight()));
revalidate();
}
if (e.getY() > getPreferredSize().getHeight()) {
ActivityOnNodePertChart.this.setPreferredSize(new Dimension((int) getPreferredSize().getWidth(),
myPressedGraphicalNode.y + getNodeHeight() + getyGap()));
revalidate();
}
repaint();
}
}
@Override
public void mouseMoved(MouseEvent e) {
// nothing to do...
}
});
this.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent arg0) {
// nothing to do...
}
@Override
public void mouseEntered(MouseEvent arg0) {
// nothing to do...
}
@Override
public void mouseExited(MouseEvent arg0) {
// nothing to do...
}
@Override
public void mousePressed(MouseEvent e) {
myPressedGraphicalNode = getGraphicalNode(e.getX(), e.getY());
if (myPressedGraphicalNode != null) {
myXClickedOffset = e.getX() - myPressedGraphicalNode.x;
myYClickedOffset = e.getY() - myPressedGraphicalNode.y;
myPressedGraphicalNode.backgroundColor = myPressedGraphicalNode.backgroundColor.darker();
}
repaint();
}
@Override
public void mouseReleased(MouseEvent e) {
if (myPressedGraphicalNode != null) {
if (myPressedGraphicalNode.node.isCritical()) {
myPressedGraphicalNode.backgroundColor = defaultCriticalColor;
} else {
myPressedGraphicalNode.backgroundColor = defaultBackgroundColor;
}
myPressedGraphicalNode.x = getGridX(e.getX() - myXClickedOffset + getNodeWidth() / 2);
myPressedGraphicalNode.y = getGridY(e.getY());
myPressedGraphicalNode = null;
repaint();
}
recalculatPreferredSize();
revalidate();
repaint();
}
});
myScrollPane = new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
public int getTextPaddingX() {
return (int) (textPaddingX * getDpi());
}
public int getTextPaddingY() {
return (int) (textPaddingY * getDpi());
}
/** Graphical nodes width. */
private int getNodeWidth() {
return (int) (NODE_WIDTH * getDpi());
}
/** Graphical nodes height. */
private int getNodeHeight() {
return (int) (NODE_HEIGHT * getDpi());
}
/** Gap between two TaskGraphNodes with the same X coordinate. */
private int getxGap() {
return (int) (X_GAP * getDpi());
}
/** Gap between two TaskGraphNodes with the same Y coordinate. */
private int getyGap() {
return (int) (Y_GAP * getDpi());
}
private int getArrowHeight() {
return (int) (ARROW_HEIGHT * getDpi());
}
private int getArrowWidth() {
return (int) (ARROW_WIDTH * getDpi());
}
private int getArrowCornerWidth() {
return (int) (ARROW_CORNER_WIDTH * getDpi());
}
/** X offset for the top left task graph node. */
private int getxOffset() {
return (int) (X_OFFSET * getDpi());
}
/** Y offset for the top left task graph node. */
private int getYOffset() {
return (int) (Y_OFFSET * getDpi());
}
/** Recalculate preferred size so that graphics fit with nodes positions. */
private void recalculatPreferredSize() {
int maxX = 0;
int maxY = 0;
for (GraphicalNode gn : myGraphicalNodes) {
int x = gn.x + getNodeWidth();
int y = gn.y + getNodeHeight();
maxX = Math.max(maxX, x);
maxY = Math.max(maxY, y);
}
setPreferredSize(new Dimension(maxX, maxY));
setMaxX(maxX);
setMaxY(maxY);
}
/**
* @return <code>true</code> if the point of coordinates <code>x</code>,
* <code>y</code> is in the rectangle described by is top left corner
* (<code>rectX</code>, <code>rectY</code>) and dimension (
* <code>rectWidth</code>, <code>rectHeight</code>),
* <code>false</code> otherwise.
*/
private static boolean isInRectancle(int x, int y, int rectX, int rectY, int rectWidth, int rectHeight) {
return (x > rectX && x < rectX + rectWidth && y > rectY && y < rectY + rectHeight);
}
/**
* @return The GraphicalNode at the <code>x</code>, <code>y</code> position,
* or <code>null</code> if there is no node.
*/
private GraphicalNode getGraphicalNode(int x, int y) {
for (GraphicalNode gn : myGraphicalNodes) {
if (isInRectancle(x, y, gn.x, gn.y, getNodeWidth(), getNodeHeight())) {
return gn;
}
}
return null;
}
@Override
protected void buildPertChart() {
if (myPertAbstraction == null) {
myPertAbstraction = new PertChartAbstraction(myTaskManager);
myTaskGraphNodes = myPertAbstraction.getTaskGraphNodes();
myGraphicalNodes = new ArrayList<>();
myGraphicalArrows = new ArrayList<>();
// myMapPositionListOfNodes = new HashMap();
// rowsList = new HashMap();
nbCols = 0;
setBackground(Color.WHITE);
this.process();
// System.out.println("Position correction");
// correctPositionBecauseOfSuperTasks();
avoidCrossingNode();
avoidCrossingLine();
removeEmptyColumn();
calculateGraphicalNodesCoordinates();
calculateArrowsCoordinates();
setPreferredSize(new Dimension(getMaxX(), getMaxY()));
} else {
myPertAbstraction = new PertChartAbstraction(myTaskManager);
myTaskGraphNodes = myPertAbstraction.getTaskGraphNodes();
updateGraphNodesInfo();
}
}
/** Updates the data for each nodes. */
private void updateGraphNodesInfo() {
if (myTaskGraphNodes != null) {
for (TaskGraphNode tgn : myTaskGraphNodes) {
GraphicalNode graphicalNode = getGraphicalNodeByID(tgn.getID());
if (graphicalNode != null) {
graphicalNode.updateData(tgn);
}
}
}
}
private boolean isZeroPosition(TaskGraphNode taskGraphNode) {
for (TaskGraphNode t : myTaskGraphNodes) {
if (t.getSuccessors().contains(taskGraphNode)) {
return false;
}
}
return true;
}
private int getGridX(int x) {
int res = getxOffset();
int tmp = 0;
while (res < x) {
tmp = res;
res += getNodeWidth() + getxGap();
}
return tmp;
}
private int getGridY(int y) {
int res = getYOffset();
int tmp = 0;
while (res < y) {
tmp = res;
res += getNodeHeight() + getyGap();
}
return tmp;
}
private void process() {
for (TaskGraphNode tgn : myTaskGraphNodes) {
if (isZeroPosition(tgn)) {
add(0, new GraphicalNode(tgn));
}
}
// TODO Translate:
// ici tous les 0 position sont faits.
int col = 0;
List<TaskGraphNode> l = getNodesThatAreInASpecificSuccessorPosition(col);
while (l != null) {
for (TaskGraphNode tnode : l) {
GraphicalNode gnode = getGraphicalNodeByID(tnode.getID());
if (gnode == null) {
gnode = createGraphicalNode(tnode);
} else {
remove(gnode);
}
add(col + 1, gnode);
}
col++;
l = getNodesThatAreInASpecificSuccessorPosition(col);
}
}
/**
* Creates or gets the graphical node corresponding to the taskGrahNode
*
* @param taskGraphNode
*/
private GraphicalNode createGraphicalNode(TaskGraphNode taskGraphNode) {
GraphicalNode res = getGraphicalNodeByID(taskGraphNode.getID());
if (res != null) {
return res;
}
return new GraphicalNode(taskGraphNode);
}
private void moveDown(GraphicalNode graphicalNode) {
int row = graphicalNode.row;
while (this.isOccupied(++row, graphicalNode.col)) {
// yup, the body is empty. But there is ++row above.
}
graphicalNode.row = row;
}
private GraphicalNode getNode(int row, int col) {
for (GraphicalNode node : getNodeInColumn(col)) {
if (node.row == row) {
return node;
}
}
return null;
}
private void moveRight(GraphicalNode graphicalNode) {
for (TaskGraphNode successor : graphicalNode.node.getSuccessors()) {
moveRight(getGraphicalNodeByID(successor.getID()));
}
int newCol = graphicalNode.col + 1;
if (isOccupied(graphicalNode.row, newCol)) {
moveRight(getNode(graphicalNode.row, newCol));
}
graphicalNode.col = newCol;
if (newCol == nbCols) {
this.nbCols++;
}
}
private void remove(GraphicalNode graphicalNode) {
myGraphicalNodes.remove(graphicalNode);
if (graphicalNode.col == -1) {
return;
}
for (GraphicalNode gnode : getNodeInColumn(graphicalNode.col)) {
if (gnode.row > graphicalNode.row) {
gnode.row--;
}
}
// int iNbRow = ((Integer)this.rowsList.get(new
// Integer(graphicalNode.col))).intValue();
// rowsList.put(new Integer(graphicalNode.col), new Integer(iNbRow-1));
if (graphicalNode.col == nbCols - 1) {
List<GraphicalNode> list = getNodeInColumn(this.nbCols - 1);
while (list.size() == 0) {
nbCols--;
list = getNodeInColumn(nbCols - 1);
}
}
graphicalNode.row = -1;
graphicalNode.col = -1;
}
private GraphicalNode getGraphicalNodeByID(int id) {
for (GraphicalNode gn : myGraphicalNodes) {
if (gn.node.getID() == id) {
return gn;
}
}
return null;
}
// TODO Translate:
/** ajoute la graphical node dans la map position/liste des successeurs */
private void add(int col, GraphicalNode graphicalNode) {
myGraphicalNodes.remove(graphicalNode);
if (nbCols - 1 < col) {
nbCols = col + 1;
}
int row = 0;
while (isOccupied(row, col)) {
row++;
}
graphicalNode.row = row;
graphicalNode.col = col;
myGraphicalNodes.add(graphicalNode);
// rowsList.put(new Integer(col), new Integer(iNbRow+1));
}
private List<TaskGraphNode> getNodesThatAreInASpecificSuccessorPosition(int col) {
List<GraphicalNode> graphicaleNodes = getNodeInColumn(col);
if (graphicaleNodes.size() == 0) {
return null;
}
List<TaskGraphNode> res = new ArrayList<>();
for (GraphicalNode gn : graphicaleNodes) {
res.addAll(gn.node.getSuccessors());
}
return res;
}
/**
* Get the list of GraphicalNode that are in a column.
*
* @param col
* the column number to look in
* @return the list of GraphicalNode in the col
*/
private List<GraphicalNode> getNodeInColumn(int col) {
List<GraphicalNode> list = new ArrayList<>();
for (GraphicalNode gnode : myGraphicalNodes) {
if (gnode.col == col) {
list.add(gnode);
}
}
return list;
}
private boolean isOccupied(int row, int col) {
for (GraphicalNode gnode : getNodeInColumn(col)) {
if (gnode.row == row) {
return true;
}
}
return false;
}
private List<TaskGraphNode> getAncestor(TaskGraphNode tgn) {
List<TaskGraphNode> ancestors = new ArrayList<>();
for (TaskGraphNode tnode : myTaskGraphNodes) {
List<TaskGraphNode> successor = tnode.getSuccessors();
if (successor.contains(tgn)) {
ancestors.add(tnode);
}
}
return ancestors;
}
private boolean isCrossingNode(GraphicalNode gnode) {
for (TaskGraphNode ancestor : getAncestor(gnode.node)) {
GraphicalNode gancestor = getGraphicalNodeByID(ancestor.getID());
if (gancestor.col < gnode.col - 1) {
for (int col = gnode.col - 1; col > gancestor.col; col--) {
if (this.isOccupied(gnode.row, col)) {
return true;
}
}
}
}
return false;
}
private void avoidCrossingNode() {
if (nbCols == 0) {
return;
}
int col = nbCols - 1;
while (col > 0) {
boolean hasmoved = false;
for (GraphicalNode gnode : getNodeInColumn(col)) {
while (isCrossingNode(gnode)) {
moveDown(gnode);
hasmoved = true;
}
}
if (hasmoved && col < nbCols - 1) {
col++;
} else {
col--;
}
}
}
private boolean isCrossingArrow(GraphicalNode gnode) {
// search for the successors with the highest and lowest position
int maxUp = Integer.MAX_VALUE, maxDown = -1;
for (TaskGraphNode successorNode : gnode.node.getSuccessors()) {
GraphicalNode successor = getGraphicalNodeByID(successorNode.getID());
if (successor.row < maxUp) {
maxUp = successor.row;
}
if (successor.row > maxDown) {
maxDown = successor.row;
}
}
// Find all other nodes on the same column
List<GraphicalNode> otherNodes = this.getNodeInColumn(gnode.col);
otherNodes.remove(gnode);
// TODO Translate
// parcours des nodes sur la même colonne
List<TaskGraphNode> gnodeSuccessors = gnode.node.getSuccessors();
for (GraphicalNode otherNode : otherNodes) {
for (TaskGraphNode otherSuccessor : otherNode.node.getSuccessors()) {
GraphicalNode otherSuccessorNode = getGraphicalNodeByID(otherSuccessor.getID());
if (maxUp < gnode.row) {
// some arrows are going up
if (otherSuccessorNode.row <= gnode.row && !gnodeSuccessors.contains(otherSuccessor)) {
return true;
}
}
if (maxDown > gnode.row) {
// some arrow are going down
if (otherSuccessorNode.row >= gnode.row && !gnodeSuccessors.contains(otherSuccessor)) {
return true;
}
}
}
}
return false;
}
private void avoidCrossingLine() {
boolean restart;
do {
restart = false;
for (int col = 0; col < nbCols && restart == false; col++) {
List<GraphicalNode> list = getNodeInColumn(col);
if (list.size() > 1) {
for (GraphicalNode gnode : list) {
if (isCrossingArrow(gnode)) {
moveRight(gnode);
avoidCrossingNode();
// Stop processing and start over
restart = true;
break;
}
}
}
}
} while (restart);
}
private void removeEmptyColumn() {
for (int col = nbCols - 1; col >= 0; col--) {
if (this.getNodeInColumn(col).size() == 0) {
if (col != nbCols - 1) {
for (int c = col + 1; c < nbCols; c++) {
for (GraphicalNode gnode : getNodeInColumn(c)) {
gnode.col--;
}
}
}
nbCols--;
}
}
}
@Override
public void buildImage(GanttExportSettings settings, ChartImageVisitor imageVisitor) {
// TODO Auto-generated method stub
}
@Override
public RenderedImage getRenderedImage(GanttExportSettings settings) {
BufferedImage image = new BufferedImage(getMaxX(), getMaxY(), BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.fillRect(0, 0, getMaxX(), getMaxY());
paint(g);
return image;
}
@Override
public String getName() {
return language.getText("pertChartLongName");
}
@Override
public void reset() {
myPertAbstraction = null;
}
@Override
public void paint(Graphics g) {
this.buildPertChart();
super.paint(g);
for (GraphicalNode myGraphicalNode : myGraphicalNodes) {
myGraphicalNode.paint(g);
}
for (GraphicalArrow myGraphicalArrow : myGraphicalArrows) {
myGraphicalArrow.paintMe(g);
}
}
private void calculateGraphicalNodesCoordinates() {
setMaxX(0);
setMaxY(0);
for (GraphicalNode gnode : myGraphicalNodes) {
gnode.x += (getNodeWidth() + getxGap()) * gnode.col;
gnode.y += (getNodeHeight() + getyGap()) * gnode.row;
setMaxX(gnode.x > getMaxX() ? gnode.x : getMaxX());
setMaxY(gnode.y > getMaxY() ? gnode.y : getMaxY());
}
setMaxX(getMaxX() + getNodeWidth() + getxGap());
setMaxY(getMaxY() + getNodeHeight() + getyGap());
}
private void calculateArrowsCoordinates() {
for (GraphicalNode gn : myGraphicalNodes) {
for (TaskGraphNode tgn : gn.node.getSuccessors()) {
int id = tgn.getID();
GraphicalArrow arrow = new GraphicalArrow(gn, getGraphicalNodeByID(id));
myGraphicalArrows.add(arrow);
}
}
}
@Override
public Object getAdapter(Class adapter) {
if (adapter.equals(Chart.class)) {
return this;
}
if (adapter.equals(Container.class)) {
return myScrollPane;
}
return null;
}
/**
* Max and min coordinates in the graphics that paints the graphical nodes and
* arrows.
*/
private int getMaxX() {
return myMaxX;
}
private void setMaxX(int myMaxX) {
this.myMaxX = myMaxX;
}
private int getMaxY() {
return myMaxY;
}
private void setMaxY(int myMaxY) {
this.myMaxY = myMaxY;
}
private final static Color defaultBackgroundColor = new Color(0.9f, 0.9f, 0.9f);
private final static Color defaultCriticalColor = new Color(250, 250, 115).brighter();
private static int textPaddingX = 10;
private static int textPaddingY = 0;
/**
* Graphical node that is rendered on graphics.
*
* @author bbaranne
*/
private class GraphicalNode extends JComponent {
private TaskGraphNode node;
private int col = -1; // determines X
private int row = -1;
private Color backgroundColor = null;
int x = getxOffset(), y = getxOffset();
GraphicalNode(TaskGraphNode node) {
this.row = -1;
this.col = -1;
this.node = node;
this.backgroundColor = defaultBackgroundColor;
if (node.isCritical()) {
this.backgroundColor = defaultCriticalColor;
}
}
/**
* Updates the linked abstract node.
*
* @param node
* new linked abstract node.
*/
void updateData(TaskGraphNode node) {
this.node = node;
}
/**
* Paints the graphical node.
*
* @param g
* Graphics where the graphical node is to be painted.
*/
@Override
public void paint(Graphics g) {
if (node.isCritical()) {
this.backgroundColor = defaultCriticalColor;
} else {
this.backgroundColor = defaultBackgroundColor;
}
paintMe(g);
}
/**
* Paints the graphical node.
*
* @param g
* Graphics where the graphical node is to be painted.
*/
private void paintMe(Graphics g) {
Font f = g.getFont();
g.setFont(getBoldFont());
FontMetrics fontMetrics = g.getFontMetrics(g.getFont());
int type = this.node.getType();
Color color;
switch (type) {
case PertChartAbstraction.Type.NORMAL:
color = NORMAL_COLOR;
break;
case PertChartAbstraction.Type.SUPER:
color = SUPER_COLOR;
break;
case PertChartAbstraction.Type.MILESTONE:
color = MILESTONE_COLOR;
break;
default:
color = NORMAL_COLOR;
}
g.setColor(this.backgroundColor);
g.fillRoundRect(x, y, getNodeWidth(), getNodeHeight(), 16, 16);
g.setColor(color);
g.drawRoundRect(x, y, getNodeWidth(), getNodeHeight(), 16, 16);
g.drawRoundRect(x + 1, y + 1, getNodeWidth() - 2, getNodeHeight() - 2, 14, 14);
g.drawLine(x, y + getTextPaddingY() + fontMetrics.getHeight() + getYOffset(), x + getNodeWidth(), y + getTextPaddingY() + fontMetrics.getHeight()
+ getYOffset());
g.setColor(Color.BLACK);
String name = node.getName();
g.drawString(StringUtils.getTruncatedString(name, getNodeWidth() - getTextPaddingX(), fontMetrics), x + getTextPaddingX(), y + getTextPaddingY()
+ fontMetrics.getHeight());
g.setFont(getBaseFont());
fontMetrics = g.getFontMetrics(g.getFont());
g.setColor(Color.BLACK);
g.drawString(language.getText("start") + ": " + node.getStartDate().toString(), x + getTextPaddingX(),
(int) (y + getTextPaddingY() + 2.3 * fontMetrics.getHeight()));
g.drawString(language.getText("end") + ": " + node.getEndDate().toString(), x + getTextPaddingX(),
(int) (y + getTextPaddingY() + 3.3 * fontMetrics.getHeight()));
if (node.getDuration() != null)
g.drawString(language.getText("duration") + ": " + node.getDuration().getLength(), x + getTextPaddingX(),
(int) (y + getTextPaddingY() + 4.3 * fontMetrics.getHeight()));
g.setFont(f);
}
@Override
public boolean equals(Object o) {
if (o instanceof GraphicalNode) {
return node.equals(((GraphicalNode) o).node);
}
return false;
}
@Override
public String toString() {
return "[" + node.getName() + " (" + col + ") " + node.getSuccessors() + "]";
}
}
/**
* Graphical arrow that is rendered on graphics.
*
* @author bbaranne
*/
private class GraphicalArrow {
GraphicalNode from;
GraphicalNode to;
GraphicalArrow(GraphicalNode from, GraphicalNode to) {
this.from = from;
this.to = to;
}
private void paintMe(Graphics g) {
g.setColor(ARROW_COLOR);
int arrowFromX, arrowFromY;
int arrowToX, arrowToY;
arrowFromX = from.x + getNodeWidth();
arrowFromY = from.y + getNodeHeight() / 2;
arrowToX = to.x;
arrowToY = to.y + getNodeHeight() / 2;
int[] xS = { arrowToX, arrowToX - getArrowWidth(), arrowToX - getArrowWidth()};
int[] yS = { arrowToY, arrowToY - getArrowHeight() / 2, arrowToY + getArrowHeight() / 2 };
int nb = xS.length;
g.fillPolygon(xS, yS, nb); // flèche
if (arrowFromY != arrowToY) {
int[] middleLineX = { arrowFromX + getxGap() / 2 - getArrowCornerWidth(), arrowFromX + getxGap() / 2,
arrowFromX + getxGap() / 2, arrowFromX + getxGap() / 2 + getArrowCornerWidth()};
int[] middleLineY = { arrowFromY,
(arrowFromY < arrowToY ? arrowFromY + getArrowCornerWidth() : arrowFromY - getArrowCornerWidth()),
(arrowFromY < arrowToY ? arrowToY - getArrowCornerWidth() : arrowToY + getArrowCornerWidth()), arrowToY };
int middleLineNb = middleLineX.length;
g.drawPolyline(middleLineX, middleLineY, middleLineNb);
g.drawLine(arrowFromX, arrowFromY, middleLineX[0], middleLineY[0]);
g.drawLine(arrowFromX + getxGap() / 2 + getArrowCornerWidth(), arrowToY, arrowToX - getArrowWidth(), arrowToY);
} else {
g.drawLine(arrowFromX, arrowFromY, arrowToX, arrowToY);
}
// g.drawString(from.node.getName(),arrowFromX+5,arrowFromY+15);
// g.drawString(to.node.getName(),arrowFromX+50,arrowFromY+15);
}
}
@Override
public IGanttProject getProject() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setDimensions(int height, int width) {
// TODO Auto-generated method stub
}
@Override
public void setStartDate(Date startDate) {
// TODO Auto-generated method stub
}
}