/***************************************************************************
ActivityOnNodePertChart.java - description
Copyright [2005 - ADAE]
This file is part of GanttProject].
***************************************************************************/
/***************************************************************************
* 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 2 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. *
***************************************************************************/
package org.ganttproject.chart.pert;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
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.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import net.sourceforge.ganttproject.GanttExportSettings;
import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.chart.Chart;
import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.task.Task;
import net.sourceforge.ganttproject.task.TaskContainmentHierarchyFacade;
import net.sourceforge.ganttproject.task.TaskLength;
import net.sourceforge.ganttproject.time.TimeUnit;
import org.ganttproject.chart.pert.PertChartAbstraction.TaskGraphNode;
/**
* PERT char 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 myTaskGraphNodes;
/**
* List of graphical arrows.
*/
private List myGraphicalArrows;
/**
* List of graphical nodes (in relation with abstract nodes)
*/
private List myGraphicalNodes;
//private Map myMapPositionListOfNodes;
private int nbCols;
/**
* PERT chart abstraction used to build graph.
*/
private PertChartAbstraction myPertAbstraction;
/**
* Max and min coordinates in the graphics that paints the graphical nodes
* and arrows.
*/
private int myMaxX = 1, myMaxY = 1;
/**
* The currently mouse pressed graphical node.
*/
private GraphicalNode myPressedGraphicalNode;
// private List pressedGraphicalNodes;
//
// private List xClickedOffsets;
//
// private List yClickedOffsets;
/**
* Offset between the mouse pointer when clicked on a graphical node and the
* top left corner of this same node.
*/
int myXClickedOffset, myYClickedOffset;
/**
* Graphics where PERT chart is painted.
*/
private Graphics myGraphics;
private static GanttLanguage ourLanguage = GanttLanguage.getInstance();
/**
* Constructs a ActivityOnNodePertChart.
*
*/
public ActivityOnNodePertChart() {
super(null);
// pressedGraphicalNodes = new ArrayList();
// xClickedOffsets = new ArrayList();
// yClickedOffsets = new ArrayList();
this.setBackground(Color.WHITE.brighter());
this.addMouseMotionListener(new MouseMotionListener() {
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 + NODE_WIDTH
+ X_GAP,
(int) getPreferredSize().getHeight()));
revalidate();
}
if (e.getY() > getPreferredSize().getHeight()) {
ActivityOnNodePertChart.this
.setPreferredSize(new Dimension(
(int) getPreferredSize().getWidth(),
myPressedGraphicalNode.y + NODE_HEIGHT
+ Y_GAP));
revalidate();
}
repaint();
}
}
public void mouseMoved(MouseEvent e) {
// nothing to do...
}
});
this.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent arg0) {
// nothing to do...
}
public void mouseEntered(MouseEvent arg0) {
// nothing to do...
}
public void mouseExited(MouseEvent arg0) {
// nothing to do...
}
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();
}
public void mouseReleased(MouseEvent e) {
if (myPressedGraphicalNode != null) {
if (myPressedGraphicalNode.node.isCritical())
myPressedGraphicalNode.backgroundColor = GraphicalNode.defaultCriticalColor;
else
myPressedGraphicalNode.backgroundColor = GraphicalNode.defaultBackgroundColor;
myPressedGraphicalNode.x = getGridX(e.getX()
- myXClickedOffset + NODE_WIDTH / 2);
myPressedGraphicalNode.y = getGridY(e.getY());
myPressedGraphicalNode = null;
repaint();
}
recalculatPreferredSize();
revalidate();
repaint();
}
});
}
/**
* Recalculated preferred size so that graphics fit with nodes positions.
*
*/
private void recalculatPreferredSize()
{
int maxX = 0;
int maxY = 0;
Iterator it = myGraphicalNodes.iterator();
while (it.hasNext()) {
GraphicalNode gn = (GraphicalNode) it.next();
int x = gn.x + NODE_WIDTH;
int y = gn.y + NODE_HEIGHT;
maxX = Math.max(maxX, x);
maxY = Math.max(maxY, y);
}
setPreferredSize(new Dimension(maxX, maxY));
myMaxX = maxX;
myMaxY = maxY;
}
/**
* Returns <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> ortherwise.
*
* @param x
* @param y
* @param rectX
* @param rectY
* @param rectWidth
* @param rectHeight
* @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> ortherwise.
*/
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);
}
/**
* Returns the GraphicalNode at the <code>x</code>, <code>y</code>
* position, or <code>null</code> if there is no node.
*
* @param x
* @param y
* @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) {
Iterator it = myGraphicalNodes.iterator();
while (it.hasNext()) {
GraphicalNode gn = (GraphicalNode) it.next();
if (isInRectancle(x, y, gn.x, gn.y, NODE_WIDTH, NODE_HEIGHT))
return gn;
}
return null;
}
/**
* @inheritDoc
*/
protected void buildPertChart() {
if (this.myPertAbstraction == null)
{
this.myPertAbstraction = new PertChartAbstraction(myTaskManager);
this.myTaskGraphNodes = myPertAbstraction.getTaskGraphNodes();
this.myGraphicalNodes = new ArrayList();
this.myGraphicalArrows = new ArrayList();
//myMapPositionListOfNodes = new HashMap();
//this.rowsList = new HashMap();
this.nbCols = 0;
this.setBackground(Color.WHITE);
this.process();
//System.out.println("Position correction");
//this.correctPositionBecauseOfSuperTasks();
this.avoidCrossingNode();
this.avoidCrossingLine();
this.removeEmptyColumn();
this.calculateGraphicalNodesCoordinates();
this.calculateArrowsCoordinates();
this.setPreferredSize(new Dimension(myMaxX, myMaxY));
}
else
{
this.myPertAbstraction = new PertChartAbstraction(myTaskManager);
myTaskGraphNodes = myPertAbstraction.getTaskGraphNodes();
updateGraphNodesInfo();
}
}
/**
* Updates the data for each nodes.
*
*/
private void updateGraphNodesInfo() {
if (myTaskGraphNodes != null) {
Iterator it = myTaskGraphNodes.iterator();
while (it.hasNext()) {
TaskGraphNode tgn = (TaskGraphNode) it.next();
int id = tgn.getID();
if(getGraphicalNodeByID(id) != null)
getGraphicalNodeByID(id).updateData(tgn);
}
}
}
/**
*
* @param taskGraphNode
* @return
*/
private boolean isZeroPosition(TaskGraphNode taskGraphNode) {
Iterator it = myTaskGraphNodes.iterator();
while (it.hasNext())
{
TaskGraphNode t = (TaskGraphNode) it.next();
if (t.getSuccessors().contains(taskGraphNode))
return false;
}
return true;
}
private static int getGridX(int x) {
int res = X_OFFSET;
int tmp = 0;
while (res < x)
{
tmp = res;
res += NODE_WIDTH + X_GAP;
}
return tmp;
}
private static int getGridY(int y) {
int res = Y_OFFSET;
int tmp = 0;
while (res < y)
{
tmp = res;
res += NODE_HEIGHT + Y_GAP;
}
return tmp;
}
private void process() {
Iterator it = myTaskGraphNodes.iterator();
while (it.hasNext())
{
TaskGraphNode tgn = (TaskGraphNode) it.next();
if (isZeroPosition(tgn))
{
add(0, new GraphicalNode(tgn));
}
}
int col = 0;
List l = getNodesThatAreInASpecificSuccessorPosition(col);
// ici tous les 0 position sont faits.
while (l != null)
{
Iterator it2 = l.iterator();
while (it2.hasNext())
{
TaskGraphNode tnode = (TaskGraphNode) it2.next();
GraphicalNode gnode = this.getGraphicalNodeByID(tnode.getID());
if(gnode == null)
{
gnode = this.createGraphicalNode(tnode);
}
else
{
this.remove(gnode);
}
this.add(col + 1, gnode);
}
col++;
l = getNodesThatAreInASpecificSuccessorPosition(col);
}
}
/**
* Creates or gets the graphical node corresponding to the taskGrahNode
*
* @param taskGraphNode
* @return
*/
private GraphicalNode createGraphicalNode(TaskGraphNode taskGraphNode) {
GraphicalNode res = getGraphicalNodeByID(taskGraphNode.getID());
if (res != null)
return res;
else
return new GraphicalNode(taskGraphNode);
}
private void correctPositionBecauseOfSuperTasks() {
TaskContainmentHierarchyFacade hierarchy = myTaskManager
.getTaskHierarchy();
Task[] tasks = myTaskManager.getTasks();
for (int i = 0; i < tasks.length; i++) {
Task task = tasks[i];
Task[] nestedTasks = hierarchy.getNestedTasks(task);
correctPositions(nestedTasks);
}
}
// recursive
private void correctPositions(Task[] tasks) {
if (tasks == null)
return;
TaskContainmentHierarchyFacade hierarchy = myTaskManager
.getTaskHierarchy();
for (int j = 0; j < tasks.length; j++) {
Task nestedTask = tasks[j];
GraphicalNode gn = getGraphicalNodeByID(nestedTask.getTaskID());
int oldPosition = gn.col;
changePosition(gn, oldPosition + 1);
correctPositions(hierarchy.getNestedTasks(nestedTask));
}
}
private void changePosition(GraphicalNode graphicalNode, int newCol) {
//int oldPosition = graphicalNode.col;
//graphicalNode.col = newCol;
//boolean remove = ((List) myMapPositionListOfNodes.get(new Integer(
// oldPosition))).remove(graphicalNode);
this.remove(graphicalNode);
this.add(newCol, graphicalNode);
}
private void moveDown(GraphicalNode graphicalNode)
{
int row = graphicalNode.row;
while(this.isOccupied(++row, graphicalNode.col));
graphicalNode.row = row;
/*int nbRows = ((Integer)this.rowsList.get(new Integer(graphicalNode.col))).intValue();
if(nbRows-1<row)
{
this.rowsList.put(new Integer(graphicalNode.col), new Integer(row+1));
}*/
}
private GraphicalNode getNode(int row, int col)
{
Iterator inCol = this.getNodeInColumn(col).iterator();
while(inCol.hasNext())
{
GraphicalNode node = (GraphicalNode)inCol.next();
if (node.row ==row)
return node;
}
return null;
}
private void moveRight(GraphicalNode graphicalNode)
{
Iterator successors = graphicalNode.node.getSuccessors().iterator();
while(successors.hasNext())
{
TaskGraphNode successor = (TaskGraphNode)successors.next();
this.moveRight(this.getGraphicalNodeByID(successor.getID()));
}
int newCol = graphicalNode.col +1;
if(this.isOccupied(graphicalNode.row, newCol))
this.moveRight(this.getNode(graphicalNode.row, newCol));
graphicalNode.col = newCol;
if(newCol==this.nbCols)
{
this.nbCols++;
}
}
private void remove(GraphicalNode graphicalNode)
{
this.myGraphicalNodes.remove(graphicalNode);
if(graphicalNode.col == -1)
return;
Iterator gnodes = this.getNodeInColumn(graphicalNode.col).iterator();
while(gnodes.hasNext())
{
GraphicalNode gnode = (GraphicalNode)gnodes.next();
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==this.nbCols-1)
{
List list = this.getNodeInColumn(this.nbCols-1);
while(list.size()==0)
{
this.nbCols--;
list = this.getNodeInColumn(this.nbCols-1);
}
}
graphicalNode.row = -1;
graphicalNode.col = -1;
}
private GraphicalNode getGraphicalNodeByID(int id) {
GraphicalNode res = null;
Iterator it = myGraphicalNodes.iterator();
while (it.hasNext()) {
GraphicalNode gn = (GraphicalNode) it.next();
if (gn.node.getID() == id) {
res = gn;
break;
}
}
return res;
}
// ajoute la graphical node dans la map position/liste des successeurs
private void add(int col, GraphicalNode graphicalNode) {
/*Integer key = new Integer(position);
List l = (List) myMapPositionListOfNodes.get(key);
int oldPosition = graphicalNode.col;
List lOld = (List) myMapPositionListOfNodes
.get(new Integer(oldPosition));
if (lOld != null)
lOld.remove(graphicalNode);
if (l == null)
{
List l2 = new ArrayList();
l2.add(graphicalNode);
myMapPositionListOfNodes.put(key, l2);
}
else
{
l.add(graphicalNode);
}
myGraphicalNodes.remove(graphicalNode);
myGraphicalNodes.add(graphicalNode);
graphicalNode.col = position;*/
myGraphicalNodes.remove(graphicalNode);
if (this.nbCols-1 < col)
this.nbCols = col+1;
int row = 0;
while(this.isOccupied(row, col))
row++;
graphicalNode.row = row;
graphicalNode.col = col;
myGraphicalNodes.add(graphicalNode);
//rowsList.put(new Integer(col), new Integer(iNbRow+1));
}
private List getNodesThatAreInASpecificSuccessorPosition(int col) {
/*
List graphicaleNodes = (List) myMapPositionListOfNodes.get(new Integer(
position));
*/
List graphicaleNodes = getNodeInColumn(col);
if (graphicaleNodes.size()==0)
return null;
List res = new ArrayList();
for (int i = 0; i < graphicaleNodes.size(); i++) {
GraphicalNode gn = (GraphicalNode) graphicaleNodes.get(i);
TaskGraphNode tgn = gn.node;
res.addAll(tgn.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 colum col
*/
private List getNodeInColumn(int col)
{
List list = new ArrayList();
Iterator gnodes = this.myGraphicalNodes.iterator();
while(gnodes.hasNext())
{
GraphicalNode gnode = (GraphicalNode) gnodes.next();
if(gnode.col == col)
list.add(gnode);
}
return list;
}
private boolean isOccupied(int row, int col)
{
List list = this.getNodeInColumn(col);
if(list.size()!=0)
{
Iterator gnodes = list.iterator();
while(gnodes.hasNext())
{
GraphicalNode gnode = (GraphicalNode)gnodes.next();
if(gnode.row==row)
return true;
}
}
return false;
}
private List getAncestor(TaskGraphNode tgn)
{
List ancestors = new ArrayList();
Iterator tnodes = this.myTaskGraphNodes.iterator();
while(tnodes.hasNext())
{
TaskGraphNode tnode = (TaskGraphNode)tnodes.next();
List successor = tnode.getSuccessors();
if(successor.contains(tgn))
ancestors.add(tnode);
}
return ancestors;
}
private boolean isCrossingNode(GraphicalNode gnode)
{
TaskGraphNode tgn = gnode.node;
List list = this.getAncestor(tgn);
if(list.size()>0)
{
Iterator ancestors = list.iterator();
while(ancestors.hasNext())
{
TaskGraphNode ancestor = (TaskGraphNode)ancestors.next();
GraphicalNode gancestor = this.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(this.nbCols==0)
return;
int col = this.nbCols-1;
while(col>0)
{
boolean hasmoved = false;
Iterator gnodes = this.getNodeInColumn(col).iterator();
while(gnodes.hasNext())
{
GraphicalNode gnode = (GraphicalNode)gnodes.next();
while(this.isCrossingNode(gnode))
{
this.moveDown(gnode);
hasmoved = true;
}
}
if(hasmoved && col<this.nbCols-1)
col++;
else
col--;
}
}
private boolean isCrossingArrow(GraphicalNode gnode)
{
//recherche de la position du successeur le plus haut et le plus bas
int maxUp = 1000000, maxDown = -1;
Iterator successors = gnode.node.getSuccessors().iterator();
while(successors.hasNext())
{
GraphicalNode successor = this.getGraphicalNodeByID(((TaskGraphNode)successors.next()).getID());
if(successor.row<maxUp)
maxUp = successor.row;
if(successor.row>maxDown)
maxDown = successor.row;
}
//r�cup�ration de toutes les nodes sur la m�me colonne
List othernodes = this.getNodeInColumn(gnode.col);
othernodes.remove(gnode);
//parcours des nodes sur la m�me colonne
Iterator nodes = othernodes.iterator();
while(nodes.hasNext())
{
GraphicalNode othergnode = (GraphicalNode)nodes.next();
Iterator othersuccessors = othergnode.node.getSuccessors().iterator();
while(othersuccessors.hasNext())
{
TaskGraphNode othersuccessor = (TaskGraphNode)othersuccessors.next();
GraphicalNode othersuccessornode = this.getGraphicalNodeByID(othersuccessor.getID());
if(maxUp < gnode.row)
{
//some arrows are going up
if(othersuccessornode.row <= gnode.row && !gnode.node.getSuccessors().contains(othersuccessor))
return true;
}
if(maxDown > gnode.row)
{
//some arrow are going down
if(othersuccessornode.row >= gnode.row && !gnode.node.getSuccessors().contains(othersuccessor))
return true;
}
}
}
return false;
}
private void avoidCrossingLine()
{
boolean restart = true;
while(restart)
{
restart = false;
for(int col=0; col<this.nbCols; col++)
{
List list = this.getNodeInColumn(col);
if(list.size()>1)
{
Iterator gnodes = list.iterator();
while(gnodes.hasNext())
{
GraphicalNode gnode = (GraphicalNode)gnodes.next();
if(this.isCrossingArrow(gnode))
{
this.moveRight(gnode);
this.avoidCrossingNode();
restart = true;
break;
}
}
if(restart)
break;
}
}
}
}
private void removeEmptyColumn()
{
for(int col=this.nbCols-1; col>=0; col--)
{
if(this.getNodeInColumn(col).size()==0)
{
if(col!=this.nbCols-1)
{
for(int c=col+1;c<this.nbCols;c++)
{
Iterator gnodes = this.getNodeInColumn(c).iterator();
while(gnodes.hasNext())
{
GraphicalNode gnode = (GraphicalNode)gnodes.next();
gnode.col--;
}
}
}
this.nbCols--;
}
}
}
public RenderedImage getRenderedImage(GanttExportSettings settings) {
return getChart(settings);
}
public BufferedImage getChart(GanttExportSettings settings) {
BufferedImage image = new BufferedImage(myMaxX, myMaxY,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.fillRect(0, 0, myMaxX, myMaxY);
paint(g);
return image;
}
public String getName() {
return ourLanguage.getText("pertChartLongName");
}
public void reset() {
this.myPertAbstraction = null;
}
public void paint(Graphics g) {
this.buildPertChart();
myGraphics = g;
super.paint(g);
for (int i = 0; i < myGraphicalNodes.size(); i++) {
((GraphicalNode) myGraphicalNodes.get(i)).paint(g);
}
for (int i = 0; i < myGraphicalArrows.size(); i++) {
((GraphicalArrow) myGraphicalArrows.get(i)).paintMe(g);
}
}
private void calculateGraphicalNodesCoordinates()
{
/*int nb = 0;
int col = 0;
int currentX = 0, currentY = 0;
//List graphicalNodesByPosition = (List) myMapPositionListOfNodes
// .get(new Integer(position));
while (col<this.nbCols)
{
// parcours de toutes les
// positions
List graphicalNodesByCol = this.getNodeInColumn(col);
currentY = 0;
Iterator it = graphicalNodesByCol.iterator();
while (it.hasNext())
{
GraphicalNode gn = (GraphicalNode) it.next();
gn.x += currentX;
gn.y += gn.row*(NODE_HEIGHT + Y_GAP);//currentY;
//currentY += NODE_HEIGHT + Y_GAP;
myMaxY = gn.y > myMaxY ? gn.y : myMaxY;
}
//myMaxY = currentY > myMaxY ? currentY : myMaxY;
currentX += NODE_WIDTH + X_GAP;
//graphicalNodesByC = (List) myMapPositionListOfNodes
// .get(new Integer(++col));
col++;
}
myMaxX = currentX;
*/
this.myMaxX = 0;
this.myMaxY = 0;
Iterator gnodes = this.myGraphicalNodes.iterator();
while(gnodes.hasNext())
{
GraphicalNode gnode = (GraphicalNode)gnodes.next();
gnode.x += (NODE_WIDTH + X_GAP) * gnode.col;
gnode.y += (NODE_HEIGHT + Y_GAP) * gnode.row;
this.myMaxX = gnode.x > this.myMaxX ? gnode.x : this.myMaxX;
this.myMaxY = gnode.y > this.myMaxY ? gnode.y : this.myMaxY;
}
this.myMaxX += NODE_WIDTH + X_GAP;
this.myMaxY += NODE_HEIGHT + Y_GAP;
}
private void calculateArrowsCoordinates() {
Iterator it = myGraphicalNodes.iterator();
while (it.hasNext()) {
GraphicalNode gn = (GraphicalNode) it.next();
Iterator itSuccessors = gn.node.getSuccessors().iterator();
while (itSuccessors.hasNext()) {
TaskGraphNode tgn = (TaskGraphNode) itSuccessors.next();
int id = tgn.getID();
GraphicalArrow arrow = new GraphicalArrow(gn,
getGraphicalNodeByID(id));
myGraphicalArrows.add(arrow);
}
}
}
public Icon getIcon() {
URL iconUrl = getClass().getResource("/icons/pert_16.gif");
return iconUrl==null ? null : new ImageIcon(iconUrl);
}
/**
* @inheritDoc
*/
public Object getAdapter(Class adapter) {
if (adapter.equals(Container.class) || adapter.equals(Chart.class)) {
return this;
}
else {
return null;
}
}
/**
* Graphical node that is rendered on graphics.
*
* @author bbaranne
*
*/
private static class GraphicalNode extends JComponent {
static int xName = 10;
static int yName = 0;
private TaskGraphNode node;
private int col=-1; // conditionne le X
private int row=-1;
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 Color backgroundColor = null;
int x = X_OFFSET, y = X_OFFSET;
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 abtract node.
*/
public void updateData(TaskGraphNode node) {
this.node = node;
}
/**
* Paints the graphical node.
*
* @param g
* Graphics where the graphical node is to be painted.
*/
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) {
g.setFont(g.getFont().deriveFont(11f).deriveFont(Font.BOLD));
FontMetrics fontMetrics = g.getFontMetrics(g.getFont());
int type = this.node.getType();
Color color = NORMAL_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;
}
}
g.setColor(this.backgroundColor);
g.fillRoundRect(x, y, NODE_WIDTH, NODE_HEIGHT, 16, 16);
g.setColor(color);
g.drawRoundRect(x, y, NODE_WIDTH, NODE_HEIGHT, 16, 16);
g.drawRoundRect(x + 1, y + 1, NODE_WIDTH - 2, NODE_HEIGHT - 2, 14,
14);
g.drawLine(x, y + yName + fontMetrics.getHeight() + Y_OFFSET, x
+ NODE_WIDTH, y + yName + fontMetrics.getHeight()
+ Y_OFFSET);
g.setColor(Color.BLACK);
String name = node.getName();
int nameWidth = fontMetrics.stringWidth(name);
g.drawString(getTruncatedString(name, NODE_WIDTH - xName,
fontMetrics), x + xName, y + yName
+ fontMetrics.getHeight());
g.setFont(g.getFont().deriveFont(Font.PLAIN));
fontMetrics = g.getFontMetrics(g.getFont());
g.setColor(Color.BLACK);
g.drawString(ourLanguage.getText("start")
+ ": "
+ node.getStartDate().toString(), x + xName, (int) (y
+ yName + 2.3 * fontMetrics.getHeight()));
g.drawString(ourLanguage.getText("end")
+ ": "
+ node.getEndDate().toString(), x + xName,
(int) (y + yName + 3.3 * fontMetrics.getHeight()));
if (node.getDuration() != null)
g.drawString(ourLanguage.getText("duration")
+ ": "
+ node.getDuration().getLength(), x + xName, (int) (y
+ yName + 4.3 * fontMetrics.getHeight()));
}
/**
* Truncated the <code>str</code> String according to
* <code>width</code> and <code>fontMetrics</code>. Returns the
* truncated String.
*
* @param str
* @param width
* @param fontMetrics
* @return Returns the truncated String.
*/
private static String getTruncatedString(String str, int width,
FontMetrics fontMetrics) {
String res = str;
int strWidth = fontMetrics.stringWidth(str);
int maxwidth = 0;
int i;
if (strWidth > width) {
for (i = 0; i < str.length(); i++) {
char c = str.charAt(i);
int cWidth = fontMetrics.charWidth(c);
maxwidth += cWidth;
if (maxwidth > width)
break;
}
res = str.substring(0, i - 2);
res += "...";
}
return res;
}
public boolean equals(Object o) {
if (o instanceof GraphicalNode) {
return node.equals(((GraphicalNode) o).node);
}
return false;
}
public String toString() {
return "[" + node.getName() + " (" + col + ") "
+ node.getSuccessors() + "]";
}
}
/**
* Graphical arrow that is rendered on graphics.
*
* @author bbaranne
*
*/
private static 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 + NODE_WIDTH;
arrowFromY = from.y + NODE_HEIGHT / 2;
arrowToX = to.x;
arrowToY = to.y + NODE_HEIGHT / 2;
int[] xS = { arrowToX, arrowToX - ARROW_WIDTH,
arrowToX - ARROW_WIDTH };
int[] yS = { arrowToY, arrowToY - ARROW_HEIGHT / 2,
arrowToY + ARROW_HEIGHT / 2 };
int nb = xS.length;
g.fillPolygon(xS, yS, nb); // fl�che
if (arrowFromY != arrowToY) {
int[] middleLineX = {
arrowFromX + X_GAP / 2 - ARROW_CORNER_WIDTH,
arrowFromX + X_GAP / 2, arrowFromX + X_GAP / 2,
arrowFromX + X_GAP / 2 + ARROW_CORNER_WIDTH };
int[] middleLineY = {
arrowFromY,
(arrowFromY < arrowToY ? arrowFromY
+ ARROW_CORNER_WIDTH : arrowFromY
- ARROW_CORNER_WIDTH),
(arrowFromY < arrowToY ? arrowToY - ARROW_CORNER_WIDTH
: arrowToY + ARROW_CORNER_WIDTH), arrowToY };
int middleLineNb = middleLineX.length;
g.drawPolyline(middleLineX, middleLineY, middleLineNb);
g.drawLine(arrowFromX, arrowFromY, middleLineX[0],
middleLineY[0]);
g.drawLine(arrowFromX + X_GAP / 2 + ARROW_CORNER_WIDTH,
arrowToY, arrowToX - ARROW_WIDTH, 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);
}
}
/**
* Graphical nodes width.
*/
private final static int NODE_WIDTH = 110;//205;
/**
* Graphical nodes height.
*/
private final static int NODE_HEIGHT = 70;
/**
* Gap between two TaskGraphNodes with the same X coordinate.
*/
private final static int X_GAP = 30;//60;
/**
* Gap between two TaskGraphNodes with the same Y coordinate.
*/
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;
/**
* X offset for the top left task graph node.
*/
private final static int X_OFFSET = 5;
/**
* Y offset for the top left task graph node.
*/
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;
public TaskLength calculateLength(int posX) {
// TODO Auto-generated method stub
return null;
}
public IGanttProject getProject() {
// TODO Auto-generated method stub
return null;
}
public void paintChart(Graphics g) {
// TODO Auto-generated method stub
}
public void resetRenderers() {
// TODO Auto-generated method stub
}
public void scrollLeft() {
// TODO Auto-generated method stub
}
public void scrollRight() {
// TODO Auto-generated method stub
}
public void setBottomUnit(TimeUnit bottomUnit) {
// TODO Auto-generated method stub
}
public void setBottomUnitWidth(int width) {
// TODO Auto-generated method stub
}
public void setDimensions(int height, int width) {
// TODO Auto-generated method stub
}
public void setStartDate(Date startDate) {
// TODO Auto-generated method stub
}
public void setTopUnit(TimeUnit topUnit) {
// TODO Auto-generated method stub
}
}