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 ganttObjects; List ganttResources; List ganttDrawables; Date firstDate; int days; int zoom; boolean timeVisible; boolean relevantChange; JViewport topPane, leftPane; IGanttObject selectedObject; List ganttMouseEventListener; public JGantt() { ganttMouseEventListener = new ArrayList(); resolver = new GanttResolver(this); ganttObjects = new ArrayList(); ganttResources = new ArrayList(); ganttDrawables = new ArrayList(); 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 rl = new LinkedList(); /* for (IGanttObject o:ganttObjects) if (Arrays.asList(o.getResources()).contains(o) */ return rl.toArray(new IGanttObject[0]); } public IGanttObject[] findDependedObjects(IGanttObject object){ LinkedList rl = new LinkedList(); 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 rl = new LinkedList(); 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(); } }