/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.gui.workflow.view.timeline; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import de.rcenvironment.core.gui.resources.api.ColorManager; import de.rcenvironment.core.gui.resources.api.StandardColors; /** * The complete navigation overview control. * * @author Hendrik Abbenhaus * */ public class TimelineNavigationControl extends Canvas implements PaintListener, MouseMoveListener, MouseListener { /** * * * @author Hendrik Abbenhaus */ private enum POSITION { LEFT, RIGHT, MIDDLE, NONE } private boolean mouseDown = false; private final List<AreaChangedListener> areaListeners = new ArrayList<AreaChangedListener>(); private TimelineComponentRow[] allrows = null; private Date workflowStartTime = null; private Date workflowEndTime = null; private Date visibleStartTime = null; private Date visibleEndTime = null; private final int sliderLineWidth = 2; private POSITION currentChoosedPosition = POSITION.NONE; private int startPositionBuffer = 0; private final int accuracy = 6; private final double timeTextFactor = 5.3; private int timeTextWidth; private final int defaultTextHeight = 15; public TimelineNavigationControl(Composite parent) { super(parent, SWT.SINGLE); this.addPaintListener(this); this.addMouseMoveListener(this); this.addMouseListener(this); FontData fontData = getFont().getFontData()[0]; timeTextWidth = (int) (timeTextFactor * fontData.getHeight()); } public Date getVisibleStartTime() { return visibleStartTime; } public Date getVisibleEndTime() { return visibleEndTime; } @Override public void paintControl(PaintEvent e) { if (this.workflowStartTime == null || this.workflowEndTime == null || this.allrows == null || this.allrows.length == 0) { this.setEnabled(false); return; } this.setEnabled(true); // get width of canvas int canvasSizeX = this.getSize().x; // getHeigh of canvass int canvasSizeY = this.getSize().y; if (allrows != null && allrows.length != 0) { int rowhigh = (int) ((float) canvasSizeY / (float) allrows.length); for (int i = 0; i < allrows.length; i++) { TimelineComponentRow currentrow = allrows[i]; for (int j = 0; j < currentrow.getActivities().length; j++) { TimelineActivityPart currentactivity = currentrow.getActivities()[j]; if (currentactivity.getType() == null) { continue; } if (currentactivity.getType().getColor() == null) { continue; } if (currentactivity.getEndDate() == null) { currentactivity.setEndtime(workflowEndTime); } // FIXME: resource leak: The color object is never disposed! e.gc.setBackground(new Color(null, currentactivity.getType().getPreviewColor())); long startdraw = TimelineView.convertDateToPixel(currentactivity.getDate(), canvasSizeX, workflowStartTime, workflowEndTime); long enddraw = TimelineView.convertDateToPixel(currentactivity.getEndDate(), canvasSizeX, workflowStartTime, workflowEndTime) - startdraw; int ystart = (i * rowhigh); int yend = ((i + 1) * rowhigh) - ystart; e.gc.fillRectangle((int) Math.floor(startdraw), ystart, (int) Math.floor(enddraw), yend); } } if (visibleStartTime == null || visibleEndTime == null) { return; } int sliderPositionleft = TimelineView.convertDateToPixel( visibleStartTime, canvasSizeX - (this.sliderLineWidth), this.workflowStartTime, this.workflowEndTime) + (this.sliderLineWidth / 2); int sliderPositionRight = TimelineView.convertDateToPixel( visibleEndTime, canvasSizeX - (this.sliderLineWidth), this.workflowStartTime, this.workflowEndTime) + (this.sliderLineWidth / 2); if (!mouseDown) { e.gc.setAlpha(TimelineViewConstants.CANVAS_SELECTION_AREA_OPACITY); e.gc.setBackground(ColorManager.getInstance().getSharedColor(TimelineViewConstants.CANVAS_COLOR_SELECTION_AREA)); e.gc.fillRectangle(sliderPositionleft, -canvasSizeY, sliderPositionRight - sliderPositionleft, 2 * canvasSizeY + sliderLineWidth / 2); } e.gc.setAlpha(TimelineViewConstants.CANVAS_DEFAULT_OPACITY); e.gc.setForeground(ColorManager.getInstance().getSharedColor(StandardColors.RCE_BLACK)); e.gc.setLineWidth(sliderLineWidth); e.gc.drawRectangle(sliderPositionleft, -canvasSizeY, sliderPositionRight - sliderPositionleft, 2 * canvasSizeY + sliderLineWidth / 2); DateFormat dfmt = new SimpleDateFormat("dd.MM.yy"); int span = 0; if (dfmt.format(visibleStartTime).equals(dfmt.format(visibleEndTime))) { dfmt = new SimpleDateFormat("HH:mm:ss"); span = defaultTextHeight / 2; } else { dfmt = new SimpleDateFormat("yyyy-MM-dd\nHH:mm:ss"); span = defaultTextHeight; } e.gc.drawText(dfmt.format(visibleStartTime), sliderPositionleft + sliderLineWidth, canvasSizeY / 2 - span, true); e.gc.drawText( dfmt.format(visibleEndTime), sliderPositionRight - sliderLineWidth - timeTextWidth, canvasSizeY / 2 - span, true); } } /** * Sets a new Workflow time area. * * @param newWFEndTime the new endtime * @param newWFStartTime the new starttime */ public void setWorflowStartEndTime(Date newWFStartTime, Date newWFEndTime) { this.workflowEndTime = newWFEndTime; this.workflowStartTime = newWFStartTime; this.redraw(); } /** * Sets a new visible time area. * * @param startVisibleTime The beginning of visibility * @param endVisibleTime The end of visibility */ public void setVisibleArea(Date startVisibleTime, Date endVisibleTime) { this.visibleStartTime = startVisibleTime; this.visibleEndTime = endVisibleTime; this.redraw(); } /** * Sets the content. * * @param rows the rows */ public void setTimeTableComponentRows(TimelineComponentRow[] rows) { this.allrows = rows; redraw(); } /** * {@inheritDoc} * * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent) */ @Override public void mouseMove(MouseEvent e) { if (visibleStartTime == null || visibleEndTime == null) { return; } Cursor cursor = null; int sliderPositionleft = TimelineView.convertDateToPixel( visibleStartTime, this.getSize().x - this.sliderLineWidth, this.workflowStartTime, this.workflowEndTime) + (this.sliderLineWidth / 2); int sliderPositionRight = TimelineView.convertDateToPixel( visibleEndTime, this.getSize().x - this.sliderLineWidth, this.workflowStartTime, this.workflowEndTime) + (this.sliderLineWidth / 2); if ((e.x > (sliderPositionleft - (accuracy * sliderLineWidth)) && e.x < (sliderPositionleft + (accuracy * sliderLineWidth))) || ((e.x > (sliderPositionRight - (accuracy * sliderLineWidth)) && e.x < (sliderPositionRight + (accuracy * sliderLineWidth)))) || (currentChoosedPosition == POSITION.LEFT) || (currentChoosedPosition == POSITION.RIGHT)) { cursor = new Cursor(this.getDisplay(), SWT.CURSOR_SIZEWE); } else if (e.x > sliderPositionleft && e.x < sliderPositionRight) { cursor = new Cursor(this.getDisplay(), SWT.CURSOR_SIZEALL); } if (currentChoosedPosition == POSITION.NONE) { this.setCursor(cursor); } setNewPosition(e.x, currentChoosedPosition); } @Override public void mouseDoubleClick(MouseEvent e) { } private void setNewPosition(int x, POSITION position) { if (visibleStartTime == null || visibleEndTime == null) { return; } int sliderPositionleft = TimelineView.convertDateToPixel( visibleStartTime, this.getSize().x - this.sliderLineWidth, this.workflowStartTime, this.workflowEndTime) + (this.sliderLineWidth / 2); int sliderPositionRight = TimelineView.convertDateToPixel( visibleEndTime, this.getSize().x - this.sliderLineWidth, this.workflowStartTime, this.workflowEndTime) + (this.sliderLineWidth / 2); switch (position) { case LEFT: // do not change position with right slider if (x - (this.sliderLineWidth / 2) >= sliderPositionRight - sliderLineWidth) { return; } // do not get out of space if (x - (this.sliderLineWidth / 2) < 0) { x = (this.sliderLineWidth / 2); } visibleStartTime = TimelineView.convertPixelToDate( x - (this.sliderLineWidth / 2), this.getSize().x - this.sliderLineWidth, this.workflowStartTime, this.workflowEndTime); break; case RIGHT: // do not change position with left slider if (x - (this.sliderLineWidth / 2) <= sliderPositionleft + sliderLineWidth) { return; } // do not go out of the space if (x - (this.sliderLineWidth / 2) > this.getSize().x) { x = this.getSize().x - (sliderLineWidth / 2); } visibleEndTime = TimelineView.convertPixelToDate( x - (this.sliderLineWidth / 2), this.getSize().x - this.sliderLineWidth, this.workflowStartTime, this.workflowEndTime); break; case MIDDLE: int newsliderPositionRight = sliderPositionRight + (x - this.sliderLineWidth / 2 - startPositionBuffer); int newsliderPositionleft = sliderPositionleft + (x - this.sliderLineWidth / 2 - startPositionBuffer); if (newsliderPositionRight > this.getSize().x - sliderLineWidth || newsliderPositionleft < 0 - 1) { return; } visibleStartTime = TimelineView.convertPixelToDate( newsliderPositionleft + (this.sliderLineWidth / 2), this.getSize().x - this.sliderLineWidth, this.workflowStartTime, this.workflowEndTime); visibleEndTime = TimelineView.convertPixelToDate( newsliderPositionRight + (this.sliderLineWidth / 2), this.getSize().x - this.sliderLineWidth, this.workflowStartTime, this.workflowEndTime); startPositionBuffer = x; break; default: // do not have to redraw, if nothing is selected return; } this.redraw(); notifyAreaChangeListener(); } @Override public void mouseDown(MouseEvent e) { if (visibleStartTime == null || visibleEndTime == null) { return; } int sliderPositionleft = TimelineView.convertDateToPixel( visibleStartTime, this.getSize().x - this.sliderLineWidth, this.workflowStartTime, this.workflowEndTime) - (this.sliderLineWidth / 2); int sliderPositionRight = TimelineView.convertDateToPixel( visibleEndTime, this.getSize().x - this.sliderLineWidth, this.workflowStartTime, this.workflowEndTime) - (this.sliderLineWidth / 2); if (e.x > (sliderPositionleft - (accuracy * sliderLineWidth)) && e.x < (sliderPositionleft + (accuracy * sliderLineWidth))) { currentChoosedPosition = POSITION.LEFT; } else if (e.x > (sliderPositionRight - (accuracy * sliderLineWidth)) && e.x < (sliderPositionRight + (accuracy * sliderLineWidth))) { currentChoosedPosition = POSITION.RIGHT; } else if (e.x > sliderPositionleft && e.x < sliderPositionRight) { currentChoosedPosition = POSITION.MIDDLE; startPositionBuffer = e.x; } else { currentChoosedPosition = POSITION.NONE; } this.mouseDown = true; } @Override public void mouseUp(MouseEvent e) { setNewPosition(e.x, currentChoosedPosition); currentChoosedPosition = POSITION.NONE; this.mouseDown = false; this.redraw(); } /** * * */ public void notifyAreaChangeListener() { for (AreaChangedListener hl : areaListeners) { hl.selectedAreaChanged(visibleStartTime, visibleEndTime); } } /** * Add {@link AreaChangedListener} to the List of Listener. * * @param newListener the new listener */ public void addAreaChangeListener(AreaChangedListener newListener) { areaListeners.add(newListener); } } /** * Interface contains area changed events. * * @author Hendrik Abbenhaus */ interface AreaChangedListener { void selectedAreaChanged(Date selectedStartTime, Date selectedEndTime); }