java-org.hwo.ui/src/org/hwo/ui/gantt/JGantt.java

532 lines
13 KiB
Java

package org.hwo.ui.gantt;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import org.hwo.datetime.Date;
import org.hwo.datetime.DateTime;
import org.hwo.datetime.TimeOfDay;
public class JGantt extends JComponent {
GanttResolver resolver;
List<IGanttObject> ganttObjects;
List<IGanttResource> ganttResources;
List<IGanttDrawable> ganttDrawables;
Date firstDate;
int days;
int zoom;
boolean timeVisible;
boolean relevantChange;
JViewport topPane,
leftPane;
IGanttObject selectedObject;
List<GanttMouseEventListener> ganttMouseEventListener;
public JGantt() {
ganttMouseEventListener = new ArrayList<GanttMouseEventListener>();
resolver = new GanttResolver(this);
ganttObjects = new ArrayList<IGanttObject>();
ganttResources = new ArrayList<IGanttResource>();
ganttDrawables = new ArrayList<IGanttDrawable>();
firstDate = new Date();
days = 31;
zoom = 1;
timeVisible = false;
relevantChange = true;
setDoubleBuffered(true);
topPane = new JViewport();
topPane.add(new TopPane());
leftPane = new JViewport();
leftPane.add(new Leftpane());
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
switch (e.getButton()){
case MouseEvent.BUTTON1:
if (e.getClickCount()==1){
DateTime dt = getDateTimeAt(e.getPoint().x);
IGanttResource r = getResourceAt(e.getPoint().y);
IGanttObject o = null;
if (r != null)
o = getObjectAt(r, dt);
fireGanttMouseClick(JGantt.this, r, dt, o);
}
break;
}
}
});
addGanttMouseListener(new GanttMouseEventListener() {
@Override
public void mouseClick(JGantt gantt, IGanttResource ganttResource,
DateTime dateTime, IGanttObject ganttObject) {
setSelectedObject(ganttObject);
}
});
}
public void addGanttMouseListener(GanttMouseEventListener listener){
ganttMouseEventListener.add(listener);
}
public void removeGanttMouseListener(GanttMouseEventListener listener){
ganttMouseEventListener.add(listener);
}
public void fireGanttMouseClick(JGantt gantt,IGanttResource ganttResource,DateTime dateTime,IGanttObject ganttObject){
for (GanttMouseEventListener listener: ganttMouseEventListener){
listener.mouseClick(this, ganttResource, dateTime, ganttObject);
}
};
public int getZoom() {
return zoom;
}
public void setZoom(int zoom) {
this.zoom = zoom;
relevantChange = true;
repaint();
}
public void addGanttObject(IGanttObject object){
if (!this.ganttObjects.contains(object)){
this.ganttObjects.add(object);
if (IGanttDrawable.class.isInstance(object)){
ganttDrawables.add((IGanttDrawable)object);
}
resolver.leftOptimize(object);
object.addNotify(this);
}
for (IGanttResource res: object.getResources()){
addGanttResource(res);
}
repaint();
}
public void removeGanttObject(IGanttObject object){
if (this.ganttObjects.contains(object)){
this.ganttObjects.remove(object);
if (this.ganttDrawables.contains(object)){
this.ganttDrawables.remove(object);
}
object.removeNotify(this);
}
repaint();
}
public IGanttObject[] getGanttObjects(){
return this.ganttObjects.toArray(new IGanttObject[0]);
}
public void addGanttResource(IGanttResource resource){
if (!this.ganttResources.contains(resource))
this.ganttResources.add(resource);
}
public void removeGanttResource(IGanttResource resource){
this.ganttResources.remove(resource);
}
public IGanttResource[] getGanttResources(){
return this.ganttResources.toArray(new IGanttResource[0]);
}
public Date getFirstDate() {
return new Date(firstDate);
}
public void setFirstDate(Date firstDate) {
this.firstDate = new Date(firstDate);
}
public int getDays() {
return days;
}
public void setDays(int days) {
this.days = days;
}
public void setTimeVisible(boolean timeVisible) {
this.timeVisible = timeVisible;
}
public boolean isTimeVisible(){
return this.timeVisible;
}
public IGanttObject getSelectedObject() {
return selectedObject;
}
public void setSelectedObject(IGanttObject selectedObject) {
this.selectedObject = selectedObject;
repaint();
}
public IGanttObject findObjectAt(IGanttResource resource,DateTime dateTime){
System.err.println(String.format("JGantt: Find Object on Resource %s at %s",resource.toString(),dateTime.toString()));
for (IGanttObject o: ganttObjects){
if (o.getStartTime().isBefore(dateTime) && o.getEndTime().isAfter(dateTime) && Arrays.asList(o.getResources()).contains(resource)){
return o;
}
}
return null;
}
public IGanttResource getResourceAt(int y){
if (y > minHeight)
return null;
IGanttResource res = ganttResources.get((y-6) / lineHeight);
return res;
}
public DateTime getDateTimeAt(int x){
Date date = new Date(firstDate);
date.addDays(x / dayColumnWidth);
TimeOfDay time = new TimeOfDay( TimeOfDay.SECONDSPERDAY * (x % dayColumnWidth) / dayColumnWidth );
return new DateTime(date, time);
}
private IGanttObject getObjectAt(IGanttResource resource,DateTime dateTime){
IGanttObject o = findObjectAt(resource,dateTime);
return o;
}
private IGanttObject[] findParallelObjects(IGanttObject object,IGanttResource resource){
LinkedList<IGanttObject> rl = new LinkedList<IGanttObject>();
/* for (IGanttObject o:ganttObjects)
if (Arrays.asList(o.getResources()).contains(o)
*/
return rl.toArray(new IGanttObject[0]);
}
public IGanttObject[] findDependedObjects(IGanttObject object){
LinkedList<IGanttObject> rl = new LinkedList<IGanttObject>();
for (IGanttObject o:ganttObjects)
if (Arrays.asList(o.getDependencies()).contains(object))
rl.add(o);
return rl.toArray(new IGanttObject[0]);
}
public IGanttObject[] findDependedObjects(IGanttObject object,IGanttResource resource){
LinkedList<IGanttObject> rl = new LinkedList<IGanttObject>();
for (IGanttObject o:ganttObjects)
if (Arrays.asList(o.getDependencies()).contains(object) && Arrays.asList(o.getResources()).contains(resource))
rl.add(o);
return rl.toArray(new IGanttObject[0]);
}
/*********
* Layout variables...
*/
int resourceLines;
int lineHeight,
headerLineHeight;
int resourceBaseHeight;
int leftColumnWidth;
int dayColumnWidth;
int minHeight;
int minWidth;
void paintObject(Graphics2D g,Rectangle rect,IGanttObject object){
if (ganttDrawables.contains(object)){
((IGanttDrawable)object).paint(g, rect);
} else {
if (selectedObject == object)
g.setColor(Color.RED);
else
g.setColor(new Color(255,192,192));
g.fillRoundRect(rect.x,rect.y,rect.width,rect.height, 8, 8);
g.drawRoundRect(rect.x,rect.y,rect.width,rect.height, 8, 8);
if (selectedObject == object)
g.setColor(Color.WHITE);
else
g.setColor(Color.BLACK);
g.drawString(object.getShortLabel(), rect.x + 4, rect.y + lineHeight - 8);
}
}
@Override
public void paint(Graphics _g) {
Graphics2D g = (Graphics2D)_g;
int n;
updateSizes();
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLACK);
// g.drawLine(leftColumnWidth, 0, leftColumnWidth, getHeight());
Date lastDate = getFirstDate();
lastDate.addDays(days-1);
for (n = 0; n < days; n++){
int l = ((n) * dayColumnWidth);
g.drawLine(((n+1) * dayColumnWidth), 0, ((n+1) * dayColumnWidth), minHeight);
}
n = 0;
for (IGanttResource resource: this.ganttResources){
n += resource.getMaxParallelUsage();
g.setColor(Color.LIGHT_GRAY);
g.drawLine(0, 4 + (n * lineHeight), minWidth, 4 + (n * lineHeight));
}
for (IGanttObject object: ganttObjects){
if (object.getEndTime().getDate().isBefore(firstDate) || object.getStartTime().getDate().isAfter(lastDate))
continue;
int pos = 2 + (object.getStartTime().getDate().daysAfter(firstDate) * dayColumnWidth);
int width = (object.getEndTime().getDate().daysAfter(object.getStartTime().getDate()) * dayColumnWidth) + dayColumnWidth - 4;
int posOffset = dayColumnWidth * object.getStartTime().getTime().getSecondsOfTheDay() / TimeOfDay.SECONDSPERDAY;
pos += posOffset;
width -= posOffset + dayColumnWidth * (TimeOfDay.SECONDSPERDAY - object.getEndTime().getTime().getSecondsOfTheDay()) / TimeOfDay.SECONDSPERDAY;
for (IGanttResource res: object.getResources()){
int vpos = 6 + (ganttResources.indexOf(res) * lineHeight);
Rectangle rect = new Rectangle(pos, vpos, width, lineHeight - 4);
paintObject(g, rect, object);
}
}
}
private void updateSizes(){
if (!relevantChange)
return;
Graphics2D g = (Graphics2D)getGraphics();
FontMetrics fm = getFontMetrics(getFont());
//lineHeight = (int)(getFont().getSize2D()*72) + 4;
lineHeight = getFont().getSize() + 8;
headerLineHeight = getFont().getSize() + 2;
resourceBaseHeight = headerLineHeight * 4;
if (isTimeVisible())
dayColumnWidth = 96;
else
dayColumnWidth = ((int)fm.getStringBounds("WWW", g).getWidth()) + 10;
dayColumnWidth *= zoom;
resourceLines = 0;
leftColumnWidth = 0;
for (IGanttResource resource: this.ganttResources){
int lw = (int)fm.getStringBounds(resource.getLabel(), g).getWidth();
if (lw > leftColumnWidth)
leftColumnWidth = lw;
resourceLines += resource.getMaxParallelUsage();
}
leftColumnWidth += 15;
minHeight = 4 + (lineHeight * resourceLines);
minWidth = ( dayColumnWidth * days );
setPreferredSize(new Dimension(minWidth, minHeight));
leftPane.setSize(new Dimension(leftColumnWidth,minHeight));
topPane.setSize(new Dimension(minWidth, resourceBaseHeight));
relevantChange = false;
}
public class TopPane extends JComponent {
@Override
public Dimension getPreferredSize() {
return new Dimension(minWidth,resourceBaseHeight);
}
@Override
public void paint(Graphics _g) {
Graphics2D g = (Graphics2D)_g;
int n;
int ayear = 0;
int aweek = 0;
updateSizes();
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLACK);
// g.drawLine(leftColumnWidth, 0, leftColumnWidth, getHeight());
g.drawLine(0, resourceBaseHeight, getWidth(), resourceBaseHeight);
g.drawLine(0, headerLineHeight, minWidth, headerLineHeight);
g.drawLine(0, 2 * headerLineHeight, minWidth, 2 * headerLineHeight);
Date date = getFirstDate();
for (n = 0; n < days; n++){
int l = ((n) * dayColumnWidth);
g.drawLine(((n+1) * dayColumnWidth), resourceBaseHeight - (2 * headerLineHeight), ((n+1) * dayColumnWidth), resourceBaseHeight * 4);
g.drawString(String.format("%02d.%02d", date.getDay(),date.getMonth()), l + 2, resourceBaseHeight - headerLineHeight - 2);
g.drawString(String.format("%s", date.weekdays[date.getWeekDay()]), l + 2, resourceBaseHeight - 2);
if (ayear != date.getYear()){
g.drawString(String.format("%02d", date.getYear()), l + 2, resourceBaseHeight - (3 * headerLineHeight) - 2);
if (ayear != 0){
g.drawLine(((n) * dayColumnWidth), resourceBaseHeight - (4 * headerLineHeight), ((n) * dayColumnWidth), resourceBaseHeight - (3 * headerLineHeight));
}
ayear = date.getYear();
}
int w = date.getWeek();
if (aweek != w){
g.drawString(String.format("%02d", w), l + 2, resourceBaseHeight - (2 * headerLineHeight) - 2);
if (aweek != 0){
g.drawLine(((n) * dayColumnWidth), resourceBaseHeight - (3 * headerLineHeight), ((n) * dayColumnWidth), resourceBaseHeight - (2 * headerLineHeight));
}
aweek = w;
}
date.addDays(1);
}
}
}
public class Leftpane extends JComponent {
public Leftpane() {
}
@Override
public Dimension getPreferredSize() {
return new Dimension(leftColumnWidth,minHeight);
}
@Override
public void paint(Graphics _g) {
Graphics2D g = (Graphics2D)_g;
int n;
updateSizes();
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLACK);
g.drawLine(leftColumnWidth - 1, 0, leftColumnWidth - 1, getHeight());
// g.drawLine(0, 0, getWidth(), 0);
n = 0;
for (IGanttResource resource: ganttResources){
n += resource.getMaxParallelUsage();
g.setColor(Color.BLACK);
g.drawString(resource.getLabel(), 2, 1 + (n * lineHeight));
g.setColor(Color.LIGHT_GRAY);
g.drawLine(0, 4 + (n * lineHeight), getWidth(), 4 + (n * lineHeight));
}
}
}
@Override
public void addNotify() {
super.addNotify();
if (JViewport.class.isInstance(getParent()) && JScrollPane.class.isInstance(getParent().getParent()))
{
JScrollPane sp = (JScrollPane)getParent().getParent();
sp.setColumnHeader(topPane);
sp.setRowHeader(leftPane);
}
}
@Override
public void removeNotify() {
super.removeNotify();
}
}