/*******************************************************************************
* Mission Control Technologies, Copyright (c) 2009-2012, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* The MCT platform is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* MCT includes source code licensed under additional open source licenses. See
* the MCT Open Source Licenses file included with this distribution or the About
* MCT Licenses dialog available at runtime from the MCT Help menu for additional
* information.
*******************************************************************************/
package gov.nasa.arc.mct.fastplot.bridge;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.AxisOrientationSetting;
import gov.nasa.arc.mct.fastplot.view.Pinnable;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.GregorianCalendar;
import plotter.xy.XYAxis;
import plotter.xy.XYMarkerLine;
import plotter.xy.XYPlotContents;
/**
* Manages the time sync line that appears on plots.
* Each instance of this class manages the sync line on a single plot or, in the case of stack plots, a single subplot.
*/
public class PlotTimeSyncLine implements MouseListener, MouseMotionListener{
private PlotterPlot plot;
private XYMarkerLine timeSyncLinePlot;
private GregorianCalendar syncTime;
private boolean mouseDown;
public PlotTimeSyncLine(PlotterPlot thePlot) {
plot = thePlot;
plot.getPlotView().addMouseListener(this);
plot.getPlotView().addMouseMotionListener(this);
}
public boolean inTimeSyncMode() {
return timeSyncLinePlot != null;
}
private GregorianCalendar getTime(int x, int y) {
// Adjust for left edge of YAxis - otherwise, legends throw off reference point
Point2D location = new Point2D.Double(x, y);
// convert from location within the JPanel to location within the plot axis
plot.getPlotView().toLogical(location, location);
GregorianCalendar clickTime;
if (plot.getAxisOrientationSetting() == AxisOrientationSetting.X_AXIS_AS_TIME) {
clickTime = new GregorianCalendar();
clickTime.setTimeInMillis((long) location.getX());
} else {
clickTime = new GregorianCalendar();
clickTime.setTimeInMillis((long) location.getY());
}
return clickTime;
}
/**
* Returns true if a time sync line is visible on the plot. False otherwise.
* @return
*/
boolean timeSyncLineVisible() {
if (timeSyncLinePlot!=null) {
return true;
} else {
return false;
}
}
/**
* Draws a time sync line on the plot at the specified time. We take the approach of plotting the time
* sync line like another data series in preference to using Quinn-Curtis' line drawing function.
* @param time at which to draw the time sync line.
*/
void drawTimeSyncLineAtTime(GregorianCalendar time) {
assert time != null;
// Peg timesync line to the plot area by setting the time
// parameter to be on the plot max or plot min.
if (time.before(plot.getCurrentTimeAxisMin())) {
time = plot.getCurrentTimeAxisMin();
} else if (time.after(plot.getCurrentTimeAxisMax())) {
time = plot.getCurrentTimeAxisMax();
}
syncTime = time;
if(timeSyncLinePlot == null) {
AbstractAxis timeAxis = plot.getTimeAxis();
if (timeAxis instanceof XYAxis) { // Only decorate time-like axes; TODO: move to AbstractAxis?
timeSyncLinePlot = new XYMarkerLine((XYAxis) timeAxis, time.getTimeInMillis());
timeSyncLinePlot.setForeground(PlotConstants.TIME_SYNC_LINE_COLOR);
XYPlotContents contents = plot.getPlotView().getContents();
contents.add(timeSyncLinePlot);
contents.revalidate();
}
} else {
timeSyncLinePlot.setValue(time.getTimeInMillis());
}
}
/**
* Remove the time sync line from the plot.
*/
void removeTimeSyncLine() {
if (timeSyncLinePlot != null) {
plot.setUserOperationLockedState(false);
XYPlotContents contents = plot.getPlotView().getContents();
contents.remove(timeSyncLinePlot);
contents.repaint(); // TODO: Only repaint the relevant portion
// plot.refreshDisplay();
timeSyncLinePlot = null;
syncTime = null;
}
}
/**
* Let the timesync line know that the shift key was pressed.
*/
public void informShiftKeyState(boolean state) {
// This mouseDown stuff is a hack so that only one time sync line in a PlotView will change the global time sync state.
if(timeSyncLinePlot != null && mouseDown) {
assert syncTime != null;
if(state) {
plot.getPlotAbstraction().initiateGlobalTimeSync(syncTime);
} else {
GregorianCalendar time = syncTime;
Pinnable pin = plot.getPlotAbstraction().createPin();
pin.setPinned(true);
plot.notifyGlobalTimeSyncFinished();
syncTime = time;
plot.getPlotAbstraction().showTimeSyncLine(syncTime);
pin.setPinned(false);
}
}
}
/**
* Respond to a mouse drag event by moving the time sync line.
* @param e mouse drag event.
*/
private void dragTimeSyncLine(MouseEvent e) {
if (plot.isInitialized()) {
int x = e.getX();
int y = e.getY();
syncTime = getTime(x, y);
plot.setUserOperationLockedState(true);
// If the shift key modifier is present, initiate the time sync mode on the workstation.
if (e.isShiftDown()) {
plot.getPlotAbstraction().updateGlobalTimeSync(syncTime);
} else {
plot.getPlotAbstraction().showTimeSyncLine(syncTime);
}
}
}
private void processEndTimeSyncLineMouseEvent() {
if (plot.isInitialized()) {
plot.notifyGlobalTimeSyncFinished();
}
}
@Override
public void mouseClicked(MouseEvent e) {
// do nothing.
}
@Override
public void mouseEntered(MouseEvent e) {
// do nothing.
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
plot.getPlotView().requestFocus();
if (plot.isInitialized() && !plot.isUserOperationsLocked() ) {
// Test event is inside the time axis area.
int x = e.getX();
int y = e.getY();
Rectangle2D plotRect = plot.getPlotView().getContents().getBounds();
boolean drawLine = false;
if (plot.getAxisOrientationSetting() == AxisOrientationSetting.X_AXIS_AS_TIME) {
double labelHeight = plot.getXAxisLabelHeight();
if (x >= (int) plotRect.getMinX() &&
x <= (int) plotRect.getMaxX() &&
y >= (int) plotRect.getMaxY() &&
y <= (int) (plotRect.getMaxY() + labelHeight)) {
drawLine = true;
}
} else {
double labelWidth = plot.getYAxisLabelWidth();
if (y >= (int) plotRect.getMinY() &&
y <= (int) plotRect.getMaxY() &&
x <= (int) plotRect.getMinX() &&
x >= (int) (plotRect.getMinX() - labelWidth)) {
drawLine = true;
}
}
if(drawLine) {
mouseDown = true;
syncTime = getTime(x, y);
plot.setUserOperationLockedState(true);
// If the shift key modifier is present, initiate the time sync mode on the workstation.
if (e.isShiftDown()) {
plot.initiateGlobalTimeSync(syncTime);
} else {
plot.getPlotAbstraction().showTimeSyncLine(syncTime);
}
}
}
}
@Override
public void mouseReleased(MouseEvent e) {
mouseDown = false;
processEndTimeSyncLineMouseEvent();
}
@Override
public void mouseDragged(MouseEvent e) {
if (timeSyncLinePlot != null) {
dragTimeSyncLine(e);
}
}
@Override
public void mouseMoved(MouseEvent e) {
// do nothing
}
}