/*
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;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import javax.swing.tree.DefaultMutableTreeNode;
import biz.ganttproject.core.chart.render.TextLengthCalculatorImpl;
import net.sourceforge.ganttproject.font.Fonts;
import net.sourceforge.ganttproject.gui.UIConfiguration;
import net.sourceforge.ganttproject.task.BlankLineNode;
import net.sourceforge.ganttproject.task.Task;
class TaskTreeImageGenerator {
private GanttTree2 myTreeView;
private UIConfiguration myUIConfiguration;
private int myWidth;
private final Image myLogo;
TaskTreeImageGenerator(GanttTree2 treeView, UIConfiguration uiConfiguration, Image logo) {
myTreeView = treeView;
myUIConfiguration = uiConfiguration;
myLogo = logo;
}
protected GanttTree2 getTree() {
return myTreeView;
}
protected Dimension calculateDimension(List<DefaultMutableTreeNode> taskNodes) {
BufferedImage tmpImage = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
FontMetrics fmetric = tmpImage.getGraphics().getFontMetrics(Fonts.DEFAULT_CHART_FONT.deriveFont(12f));
int fourEmWidth = fmetric.stringWidth("mmmm");
int width = 0;
int height = getTree().getTreeTable().getRowHeight() * 3 + HEADER_OFFSET;
for (Iterator<DefaultMutableTreeNode> tasks = taskNodes.iterator(); tasks.hasNext();) {
DefaultMutableTreeNode nextTreeNode = tasks.next();
if (nextTreeNode instanceof BlankLineNode) {
height += getTree().getTreeTable().getRowHeight();
continue;
}
Task next = (Task) nextTreeNode.getUserObject();
if ("None".equals(next.toString())) {
continue;
}
if (isVisible(next)) {
height += getTree().getTreeTable().getRowHeight();
int nbchar = fmetric.stringWidth(next.getName()) + next.getManager().getTaskHierarchy().getDepth(next)
* fourEmWidth;
if (nbchar > width) {
width = nbchar;
}
}
}
width += 10;
return new Dimension(width, height);
}
Image createImage(List<DefaultMutableTreeNode> taskNodes) {
Dimension d = calculateDimension(taskNodes);
myWidth = d.width;
BufferedImage image = new BufferedImage(getWidth(), d.height, BufferedImage.TYPE_INT_RGB);
paint(image, d, taskNodes);
return image;
}
protected void paint(Image image, Dimension d, List<DefaultMutableTreeNode> taskNodes) {
Graphics2D g = (Graphics2D) image.getGraphics();
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), d.height);
printTasks(g, taskNodes);
// GanttImagePanel but = new GanttImagePanel("big.png", 300, 47);
g.setColor(new Color(102, 153, 153));
g.drawImage(myLogo, 0, 0, null);
// but.paintComponent(g2);
}
private int getWidth() {
return myWidth;
}
static class PaintState {
int y;
int rowCount = 0;
Stack<DefaultMutableTreeNode> nestingStack = new Stack<DefaultMutableTreeNode>();
int rowHeight;
int indent;
}
private int printTasks(Graphics2D g, List<DefaultMutableTreeNode> taskNodes) {
g.setColor(Color.black);
g.setFont(Fonts.PRINT_CHART_FONT);
PaintState state = new PaintState();
state.y = getTree().getTable().getTableHeader().getHeight() + HEADER_OFFSET;
state.rowHeight = getTree().getTreeTable().getRowHeight();
// int x = 5;
state.indent = TextLengthCalculatorImpl.getTextLength(g, "mmmm");
for (Iterator<DefaultMutableTreeNode> tasks = taskNodes.iterator(); tasks.hasNext();) {
DefaultMutableTreeNode nextTreeNode = tasks.next();
boolean blankline = nextTreeNode instanceof BlankLineNode;
Task next = null;
if (!blankline) {
next = (Task) nextTreeNode.getUserObject();
while (!state.nestingStack.isEmpty()) {
DefaultMutableTreeNode topStackNode = state.nestingStack.pop();
if (nextTreeNode.getParent() == topStackNode) {
state.nestingStack.push(topStackNode);
break;
}
}
state.nestingStack.push(nextTreeNode);
}
if (blankline || isVisible(next)) {
if (state.rowCount % 2 == 1) {
// Make alternating background pattern
// TODO Define background color for the alternating rows (and use that
// everywhere)
g.setColor(new Color(0.933f, 0.933f, 0.933f));
g.fillRect(0, state.y, getWidth() - state.rowHeight / 2, state.rowHeight);
}
g.setColor(Color.black);
g.drawRect(0, state.y, getWidth() - state.rowHeight / 2, state.rowHeight);
if (!blankline) {
paintTask(g, state, next);
}
g.setColor(new Color(0.807f, 0.807f, 0.807f));
g.drawLine(1, state.y + state.rowHeight - 1, getWidth() - 11, state.y + state.rowHeight - 1);
state.y += state.rowHeight;
state.rowCount++;
}
}
return state.y;
}
protected void paintTask(Graphics g, PaintState state, Task t) {
int charH = (int) g.getFontMetrics().getLineMetrics(t.getName(), g).getAscent();
int x = (state.nestingStack.size() - 1) * state.indent + 5;
g.drawString(t.getName(), x, state.y + charH + (state.rowHeight - charH) / 2);
}
private boolean isVisible(Task thetask) {
return getTree().isVisible(thetask);
}
protected static final int HEADER_OFFSET = 44;
}