package com.isti.traceview.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.image.MemoryImageSource;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.TimeZone;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EtchedBorder;
import javax.swing.event.MouseInputListener;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickUnit;
import org.jfree.chart.axis.DateTickUnitType;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;
import com.isti.traceview.CommandHandler;
import com.isti.traceview.ITimeRangeAdapter;
import com.isti.traceview.TraceView;
import com.isti.traceview.common.IEvent;
import com.isti.traceview.common.TimeInterval;
import com.isti.traceview.common.UniqueList;
import com.isti.traceview.data.PlotDataProvider;
import com.isti.traceview.data.Segment;
import com.isti.traceview.data.SelectionContainer;
import com.isti.traceview.filters.IFilter;
import com.isti.traceview.processing.RemoveGain;
import com.isti.traceview.processing.Rotation;
/**
* This is graphics container; it contains a list of ChannelView(s) (panels) and renders them as a
* 1-column table; responsible for ChannelView selecting and ordering selecting time and values
* ranges, holds information about current representation state.
*
* @author Max Kokoulin
*/
public class GraphPanel extends JPanel implements Printable, MouseInputListener, Observer {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The Constant logger. */
private static final Logger logger = Logger.getLogger(GraphPanel.class); // @jve:decl-index=0:
/** The Constant selectionColor. */
private static final Color selectionColor = Color.YELLOW;
/** The axis font. */
private static Font axisFont = null; // @jve:decl-index=0:
/** The hidden cursor. */
private static Cursor hiddenCursor = null;
/** The cross cursor. */
private static Cursor crossCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
static {
int[] pixels = new int[16 * 16];
Image image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(16, 16, pixels, 0, 16));
hiddenCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(0, 0), "HIDDEN_CURSOR");
}
/** Time interval including currently loaded in GraphPanel channels set. */
private TimeInterval timeRange = new TimeInterval();
/** List of graphs */
private List<ChannelView> channelShowSet = null; // @jve:decl-index=0:
/** The selected channel show set. */
private List<ChannelView> selectedChannelShowSet = null;
/** The previously selected channels with the level that they were selected */
private List<SelectionContainer> previousSelectedChannels = new ArrayList<SelectionContainer>();
/** The current level of channel selection */
private int selectionLevel = 0;
/** Amount of units to show simultaneously in this graph panel. */
private int unitsShowCount;
/** The draw area panel. */
private DrawAreaPanel drawAreaPanel = null;
/** The south panel. */
private SouthPanel southPanel = null;
/** The mouse selection enabled. */
private boolean mouseSelectionEnabled = true;
/**
* Current mouse position, X coordinate. Used by repaint()
*/
protected int mouseX;
/**
* Current mouse position, Y coordinate. Used by repaint()
*/
protected int mouseY;
/**
* Mouse position during previous repaint() call, X coordinate.
*/
protected int previousMouseX = -1;
/**
* Mouse position during previous repaint() call, Y coordinate.
*/
protected int previousMouseY = -1;
/** Flag if we need to repaint mouse cross cursor. */
private boolean mouseRepaint = false;
/**
* Flag if we need to force repaint data, in spite of mouseRepaint value.
*/
private boolean forceRepaint = false;
/** Flag if we need to initialize painting (unchanged data loads once). */
protected boolean initialPaint = false;
/** Flag for ChannelView mouse movements (draw crosshair). */
protected boolean cvMouseMoved = false;
/** Flag when exiting ChannelView panel (erase prev crosshair). */
private boolean cvMouseExited = false;
/** Flag when we drag mouse for zooming in ChannelView panels. */
protected boolean mouseDragged = false;
/** Flag if we need to paint now (occurs with repaint()). */
private boolean paintNow = false;
/**
* Mouse button was pressed, X coordinate.
*/
protected int mousePressX;
/**
* Mouse button was pressed, Y coordinate.
*/
protected int mousePressY;
/** Mouse button which was pressed. */
protected int button = MouseEvent.NOBUTTON;
/** The selected area xbegin. */
private long selectedAreaXbegin = Long.MAX_VALUE;
/** The selected area xend. */
private long selectedAreaXend = Long.MIN_VALUE;
/** The selected area ybegin. */
private double selectedAreaYbegin = Double.NaN;
/** The selected area yend. */
private double selectedAreaYend = Double.NaN;
/** The previous selected area xbegin. */
private long previousSelectedAreaXbegin = Long.MAX_VALUE;
/** The previous selected area xend. */
private long previousSelectedAreaXend = Long.MIN_VALUE;
/** The previous selected area ybegin. */
private double previousSelectedAreaYbegin = Double.NaN;
/** The previous selected area yend. */
private double previousSelectedAreaYend = Double.NaN;
/**
* X coordinate of last clicked point, to compute time differences between last two clicks.
*/
private int mouseClickX = -1;
/** The observable. */
public GraphPanelObservable observable = null;
/** The show big cursor. */
private boolean showBigCursor = false;
/** Flag if graphPanel should manage its TimeInterval itself or use given time interval. */
private boolean shouldManageTimeRange = true;
/** The scale mode. */
private IScaleModeState scaleMode = null; // @jve:decl-index=0:
/** The color mode. */
private IColorModeState colorMode = null; // @jve:decl-index=0:
/** The mean state. */
private IMeanState meanState; // @jve:decl-index=0:
/** The offset state. */
private IOffsetState offsetState; // @jve:decl-index=0:
/** The phase state. */
private boolean phaseState = false;
/** The pick state. */
private boolean pickState = false;
/** The overlay. */
private boolean overlay = false;
/** The select. */
private boolean select = false;
/** The filter. */
private IFilter filter = null;
/** The rotation. */
private Rotation rotation = null;
/** The gain */
private RemoveGain gain = new RemoveGain(false);
/** Visible earthquakes to draw on the graphs. */
private Set<IEvent> selectedEarthquakes = null; // @jve:decl-index=0:
/** Visible phases to draw on the graphs. */
private Set<String> selectedPhases = null; // @jve:decl-index=0:
/** The mouse adapter. */
private IMouseAdapter mouseAdapter = null;
/** The time range adapter. */
private ITimeRangeAdapter timeRangeAdapter = null;
/** The channel view factory. */
protected IChannelViewFactory channelViewFactory = new DefaultChannelViewFactory();
/** The mark position image. */
private Image markPositionImage = null;
/** if we need show block header as tooltip. */
private boolean isShowBlockHeader = false;
/**
* Default constructor.
*/
public GraphPanel() {
this(true);
}
/**
* Instantiates a new graph panel.
*
* @param showTimePanel the show time panel
*/
public GraphPanel(boolean showTimePanel) {
super();
initialize(showTimePanel);
addMouseListener(this);
addMouseMotionListener(this);
selectedEarthquakes = new HashSet<IEvent>();
selectedPhases = new HashSet<String>();
meanState = new MeanModeDisabled();
colorMode = new ColorModeBySegment();
scaleMode = new ScaleModeAuto();
offsetState = new OffsetModeDisabled();
setObservable(new GraphPanelObservable());
mouseSelectionEnabled = true;
}
/**
* This method initializes graph panel.
*
* @param showTimePanel the show time panel
*/
private void initialize(boolean showTimePanel) {
if (axisFont == null) {
axisFont = new Font(getFont().getName(), getFont().getStyle(), 10);
}
initialPaint = true;
channelShowSet = Collections.synchronizedList(new ArrayList<ChannelView>());
unitsShowCount = TraceView.getConfiguration().getUnitsInFrame();
setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
this.setLayout(new BorderLayout());
this.add(getDrawAreaPanel(), BorderLayout.CENTER);
this.add(getSouthPanel(showTimePanel), BorderLayout.SOUTH);
/** ----------------- MTH ---------------- **/
// defaultMarkPosition.gif is now found with a change to build.xml
URL url = null;
try {
url = GraphPanel.class.getResource("/defaultMarkPosition.gif");
logger.info(String.format("== MTH: file=%s\n", url.getFile()));
markPositionImage = javax.imageio.ImageIO.read(url);
//} catch (MalformedURLException e) {
} catch (Exception e) {
// Do something appropriate
logger.error("Exception:", e);
}
/** ----------------- MTH ---------------- **/
/**
try {
markPositionImage = javax.imageio.ImageIO.read(ClassLoader.getSystemResource("defaultMarkPosition.gif"));
} catch (IOException e) {
markPositionImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
lg.error("Can't read mark position image: " + e);
}
**/
}
/**
* Pixelizes and paints new data
*
* NOTE: This should only be used with changed data (i.e. zoom, filter, etc.)
*/
public void forceRepaint(){
forceRepaint = true;
repaint();
}
/**
* Sets factory to produce ChannelViews. Library user can define his own factory to produce
* customized ChannelViews.
*
* @param cvf
* User's factory
*/
public void setChannelViewFactory(IChannelViewFactory cvf) {
this.channelViewFactory = cvf;
}
/**
* Getter of time range.
*
* @return currently time range
*/
public TimeInterval getTimeRange() {
return timeRange;
}
/**
* Time range setter.
*
* @param ti time range to set. Graph panel redraws to show this time range.
*/
public void setTimeRange(TimeInterval ti) {
//logger.debug("timerange: " + timeRange);
this.timeRange = ti;
if (timeRangeAdapter != null && TraceView.getFrame() != null) {
timeRangeAdapter.setTimeRange(ti);
}
southPanel.getAxisPanel().setTimeRange(ti);
mouseClickX = -1;
southPanel.getInfoPanel().update(ti);
getObservable().setChanged();
getObservable().notifyObservers(ti);
forceRepaint();
}
/**
* If true, graph panel itself changes time range after data set changing to show all loaded
* data. If false, given time range used.
*
* @param value
* Flag if graphPanel should manage its TimeInterval itself
*/
public void setShouldManageTimeRange(boolean value) {
shouldManageTimeRange = value;
}
/**
* If true, graph panel itself changes time range after data set changing to show all loaded
* data. If false, given time range used.
*
* @return Flag if graphPanel should manage its TimeInterval itself
*/
public boolean isShouldManageTimeRange() {
return shouldManageTimeRange;
}
/**
* If true, graph panel selects blue area while mouse dragging and calls time range or value
* range changing after mouse releasing.
*
* @return Flag if mouse selection enabled
*/
public boolean isMouseSelectionEnabled() {
return mouseSelectionEnabled;
}
/**
* If true, graph panel selects blue area while mouse dragging and calls time range or value
* range changing after mouse releasing.
*
* @param mouseSelectionEnabled Flag if mouse selection enabled
*/
public void setMouseSelectionEnabled(boolean mouseSelectionEnabled) {
this.mouseSelectionEnabled = mouseSelectionEnabled;
}
/**
* Sets X coordinates of selected rectangle.
*
* @param begin left edge position
* @param end right edge position
*/
public void setSelectionX(long begin, long end) {
selectedAreaXbegin = begin;
selectedAreaXend = end;
}
/**
* Sets Y coordinates of selected rectangle.
*
* @param begin top edge position
* @param end bottom edge position
*/
public void setSelectionY(double begin, double end) {
selectedAreaYbegin = begin;
selectedAreaYend = end;
}
/**
* Sets mouse adapter which defines behavior after mouse operations.
*
* @param ma the new mouse adapter
*/
public void setMouseAdapter(IMouseAdapter ma) {
mouseAdapter = ma;
}
/**
* Removes mouse adapter which defines behavior after mouse operations.
*/
public void removeMouseAdapter() {
mouseAdapter = null;
}
/**
* Sets time range adapter which defines behavior after setting time range.
*
* @param tr the new time range adapter
*/
public void setTimeRangeAdapter(ITimeRangeAdapter tr) {
timeRangeAdapter = tr;
}
/**
* Removes time range adapter which defines behavior after setting time range.
*/
public void removeTimeRangeAdapter() {
timeRangeAdapter = null;
}
/**
* Getter of the property <tt>manualValueMin</tt>.
*
* @return Minimum of value axis range, it used in XHair scaling mode
*/
public double getManualValueMin() {
return ScaleModeAbstract.getManualValueMin();
}
/**
* Setter of the property <tt>manualValueMin</tt>.
*
* @param manualValueMin Minimum of value axis range, it used in XHair scaling mode
*/
public void setManualValueMin(double manualValueMin) {
ScaleModeAbstract.setManualValueMin(manualValueMin);
forceRepaint();
}
/**
* Getter of the property <tt>manualValueMax</tt>.
*
* @return Maximum of value axis range, it used in XHair scaling mode
*/
public double getManualValueMax() {
return ScaleModeAbstract.getManualValueMax();
}
/**
* Setter of the property <tt>manualValueMax</tt>.
*
* @param manualValueMax Maximum of value axis range, it used in XHair scaling mode
*/
public void setManualValueMax(double manualValueMax) {
ScaleModeAbstract.setManualValueMax(manualValueMax);
forceRepaint();
}
/**
* Gets the show block header.
*
* @return the show block header
*/
public boolean getShowBlockHeader(){
return isShowBlockHeader;
}
/**
* Sets the show block header.
*
* @param isShowBlockHeader the new show block header
*/
public void setShowBlockHeader(boolean isShowBlockHeader){
this.isShowBlockHeader = isShowBlockHeader;
if(!isShowBlockHeader){
ChannelView.tooltipVisible = false;
}
}
/**
* Gets the channel set.
*
* @return Returns List of currently loaded channels.
*/
public List<PlotDataProvider> getChannelSet() {
List<PlotDataProvider> ret = new ArrayList<PlotDataProvider>();
for (ChannelView cv: getChannelShowSet()) {
for (PlotDataProvider channel: cv.getPlotDataProviders()) {
ret.add(channel);
}
}
return ret;
}
/**
* Getter of the property <tt>channelShowSet</tt>. Returns list of views for current page
* without influence of selection commands, like select or overlay
*
* @return Returns the channelShowSet.
*/
public List<ChannelView> getChannelShowSet() {
return channelShowSet;
}
/**
* Returns list of views for current page with influence of selection commands, like select or
* overlay.
*
* @return the current channel show set
*/
public List<ChannelView> getCurrentChannelShowSet() {
List<ChannelView> ret = new ArrayList<ChannelView>();
Component[] comp = drawAreaPanel.getComponents();
for (Component c: comp) {
ret.add((ChannelView) c);
}
return ret;
}
/**
* Gets the selected channel show set.
*
* @return Returns List of selected graphs Here we mean graph selected if it was selected on the
* initial screen, without selected or overlayed mode.
*/
public List<ChannelView> getSelectedChannelShowSet() {
return selectedChannelShowSet;
}
/**
* Gets the current selected channel show set.
*
* @return Returns List of selected graphs based on screen selection. Differ from
* getSelectedChannelShowSet() while mode selected or overlay enabled.
*/
public List<ChannelView> getCurrentSelectedChannelShowSet() {
List<ChannelView> ret = new ArrayList<ChannelView>();
for (ChannelView cv: getCurrentChannelShowSet()) {
if (cv.isSelected()) {
ret.add(cv);
}
}
Collections.sort(ret);
return ret;
}
/**
* Unchecks all selected ChannelView's in the GraphPanel
*/
public void clearSelectedChannels() {
List<ChannelView> cvList = getCurrentChannelShowSet();
for(ChannelView cv : cvList){
cv.clearCheckBox();
}
}
/**
* Gets the current selected channels.
*
* @return Returns List of selected channels, based on screen selection
*/
public List<PlotDataProvider> getCurrentSelectedChannels() {
List<PlotDataProvider> ret = new ArrayList<PlotDataProvider>();
for (ChannelView cv: getCurrentSelectedChannelShowSet()) {
ret.addAll(cv.getPlotDataProviders());
}
return ret;
}
/**
* Setter of the property <tt>channelShowSet</tt> each channel in it's own graph or group by
* location in each graph.
*
* @param channels list of traces
*/
public void setChannelShowSet(List<PlotDataProvider> channels) {
synchronized (TraceView.getDataModule().getAllChannels()) {
if (channels != null) {
clearChannelShowSet();
CommandHandler.getInstance().clearCommandHistory();
// This is the main method for all station channels for one
// GraphPanel (i.e. one station multiple channels per panel)
if (!TraceView.getConfiguration().getMergeLocations()) {
// This submits each single channel as a List<> which doesn't
// make sense. Why not submit all channels to addChannelShowSet()?
// or submit each channel individually?
// **NOTE: addChannelShowSet() calls the addGraph() method which
// creates a graph panel for each channel submitted
for (PlotDataProvider channel: channels) {
//logger.debug("== handle channel=" + channel);
List<PlotDataProvider> toAdd = new ArrayList<PlotDataProvider>();
toAdd.add(channel);
addChannelShowSet(toAdd);
}
// Loops through ChannelView objects and loads segment data
List<PlotDataProvider> pdpList = new ArrayList<PlotDataProvider>();
TimeInterval ti = null;
System.out.println("Loading channel segment data:");
long startl = System.nanoTime();
for (ChannelView cv: channelShowSet) {
pdpList = cv.getPlotDataProviders();
if (pdpList.size() > 1) {
for (PlotDataProvider channel: pdpList)
channel.load(ti);
} else {
PlotDataProvider channel = pdpList.get(0);
channel.load(ti);
}
}
long endl = System.nanoTime() - startl;
double end = endl * Math.pow(10, -9);
//logger.debug("Channels are done loading");
System.out.println("Channel segment data load time = " + end + " sec\n");
} else {
List<PlotDataProvider> toAdd = new ArrayList<PlotDataProvider>();
PlotDataProvider prevChannel = null;
for (PlotDataProvider channel: channels) {
// This block checks for channels with the same location code
// regardless of the network, channel, or station name
// adds list of channels based on {XX}location to one graph panel
// **NOTE: The toAdd List<> still needs to be revised, doesn't
// make sense when only one channel is being submitted
if (prevChannel != null
&& (!prevChannel.getNetworkName().equals(channel.getNetworkName())
|| !prevChannel.getStation().getName().equals(channel.getStation().getName()) || !prevChannel
.getChannelName().equals(channel.getChannelName()))) {
ChannelView cv = channelViewFactory.getChannelView(toAdd);
addGraph(cv);
toAdd = new ArrayList<PlotDataProvider>();
}
toAdd.add(channel);
prevChannel = channel;
}
if (toAdd.size() > 0) {
addChannelShowSet(toAdd);
}
// Will add loop for List<ChannelView> channelShowSet to load
// channels in List<PlotDataProvider> (see above)
}
selectedChannelShowSet = Collections.synchronizedList(new UniqueList<ChannelView>());
if (overlay) {
overlay = false;
getObservable().setChanged();
getObservable().notifyObservers("OVR OFF");
}
if (select) {
select = false;
getObservable().setChanged();
getObservable().notifyObservers("SEL OFF");
}
if (rotation != null) {
rotation = null;
getObservable().setChanged();
getObservable().notifyObservers("ROT OFF");
}
repaint(); // why repaint when adding channels to Graph?
}
getObservable().setChanged();
getObservable().notifyObservers(channels);
}
}
/**
* Add one graph with list of channels inside it.
*
* @param channels the channels
*/
public void addChannelShowSet(List<PlotDataProvider> channels) {
if (channels != null) {
ChannelView cv = channelViewFactory.getChannelView(channels);
addGraph(cv);
if (this.shouldManageTimeRange) {
if (timeRange == null) {
setTimeRange(cv.getLoadedTimeRange());
} else {
setTimeRange(TimeInterval.getAggregate(timeRange, cv.getLoadedTimeRange()));
}
}
repaint(); // why repaint when adding channels to set?
getObservable().setChanged();
getObservable().notifyObservers(channels);
}
}
/**
* Clears loaded set of traces.
*/
public void clearChannelShowSet() {
for (ChannelView cv: channelShowSet) {
for (PlotDataProvider channel: cv.getPlotDataProviders()) {
channel.deleteObserver(cv);
}
}
ChannelView.currentSelectionNumber = 0;
removeAll();
if (this.shouldManageTimeRange) {
setTimeRange(null);
}
}
/**
* Getter of the property <tt>unitsShowCount</tt>.
*
* @return Count of display units used to determine which subset of loaded traced should be
* shown
*/
public int getUnitsShowCount() {
return unitsShowCount;
}
/**
* Setter of the property <tt>unitsShowCount</tt>.
*
* @param unitsShowCount Count of display units used to determine which subset of loaded traced should be
* shown
*/
public void setUnitsShowCount(int unitsShowCount) {
this.unitsShowCount = unitsShowCount;
}
/**
* Gets the show big cursor.
*
* @return Flag if panel should use full-size cross hairs cursor
*/
public boolean getShowBigCursor() {
return showBigCursor;
}
/**
* Sets the show big cursor.
*
* @param showBigCursor Flag if panel should use full-size cross hairs cursor
*/
public void setShowBigCursor(boolean showBigCursor) {
this.showBigCursor = showBigCursor;
if (showBigCursor) {
for (ChannelView cv: channelShowSet) {
cv.setCursor(hiddenCursor);
}
} else {
for (ChannelView cv: channelShowSet) {
cv.setCursor(crossCursor);
}
}
mouseRepaint = false;
repaint();
}
/**
* Sets the wait cursor.
*
* @param status if panel should use wait cursor, used during long operations
*/
public void setWaitCursor(boolean status) {
synchronized (channelShowSet) {
if (status) {
Cursor waitCursor = new Cursor(Cursor.WAIT_CURSOR);
for (ChannelView cv: channelShowSet) {
cv.setCursor(waitCursor);
}
} else {
if (showBigCursor) {
for (ChannelView cv: channelShowSet) {
cv.setCursor(hiddenCursor);
}
} else {
for (ChannelView cv: channelShowSet) {
cv.setCursor(crossCursor);
}
}
}
}
}
/**
* Getter of the property <tt>scaleMode</tt>.
*
* @return current scaling mode
*/
public IScaleModeState getScaleMode() {
return scaleMode;
}
/**
* Setter of the property <tt>scaleMode</tt>.
*
* @param scaleMode scaling mode which panel should use
*/
public void setScaleMode(IScaleModeState scaleMode) {
this.scaleMode = scaleMode;
// returns XHair mode to all data after scale mode switching
// manualValueMax = Integer.MIN_VALUE;
// manualValueMin = Integer.MAX_VALUE;
getObservable().setChanged();
getObservable().notifyObservers(scaleMode);
repaint();
}
/**
* Getter of the property <tt>colorMode</tt>.
*
* @return current color mode
*/
public IColorModeState getColorMode() {
return colorMode;
}
/**
* Setter of the property <tt>colorMode</tt>.
*
* @param colorMode color mode which panel should use
*/
public void setColorMode(IColorModeState colorMode) {
this.colorMode = colorMode;
getObservable().setChanged();
getObservable().notifyObservers(colorMode);
repaint();
}
/**
* Getter of the property <tt>meanState</tt>.
*
* @return current meaning mode
*/
public IMeanState getMeanState() {
return meanState;
}
/**
* Setter of the property <tt>meanState</tt>.
*
* @param meanState meaning mode which panel should use
*/
public void setMeanState(IMeanState meanState) {
this.meanState = meanState;
// returns XHair mode to all data after scale mode switching
// manualValueMax = Integer.MIN_VALUE;
// manualValueMin = Integer.MAX_VALUE;
getObservable().setChanged();
getObservable().notifyObservers(meanState);
repaint();
}
/**
* Getter of the property <tt>offsetState</tt>.
*
* @return current offset mode.
*/
public IOffsetState getOffsetState() {
return offsetState;
}
/**
* Setter of the property <tt>offsetState</tt>.
*
* @param offsetState offset mode which panel should use
*/
public void setOffsetState(IOffsetState offsetState) {
this.offsetState = offsetState;
getObservable().setChanged();
getObservable().notifyObservers(offsetState);
repaint();
}
/**
* Getter of the property <tt>phaseState</tt>.
*
* @return current phase mode.
*/
public boolean getPhaseState() {
return phaseState;
}
/**
* Setter of the property <tt>phaseState</tt>.
*
* @param phaseState phase mode which panel should use
*/
public void setPhaseState(boolean phaseState) {
this.phaseState = phaseState;
repaint();
}
/**
* Getter of the property <tt>pickState</tt>.
*
* @return current pick mode.
*/
public boolean getPickState() {
return pickState;
}
/**
* Setter of the property <tt>pickState</tt>.
*
* @param pickState pick mode which panel should use
*/
public void setPickState(boolean pickState) {
this.pickState = pickState;
String message = null;
if (pickState) {
message = "PICK ON";
} else {
message = "PICK OFF";
}
getObservable().setChanged();
getObservable().notifyObservers(message);
repaint();
}
/**
* Sets filter. Null means filter doesn't affected. Shown traces will be redrawn with filtering.
*
* @param filter
* IFilter to set
*/
public void setFilter(IFilter filter) {
logger.debug("filter " + filter);
if(filter != null){
if(getMaxDataLength()>filter.getMaxDataLength()){
if(JOptionPane.showConfirmDialog(TraceView.getFrame(), "Too many datapoints are selected. Processing could take time. Do you want to continue?", "Warning", JOptionPane.OK_CANCEL_OPTION)==JOptionPane.OK_OPTION){
this.filter = filter;
getObservable().setChanged();
getObservable().notifyObservers(filter);
forceRepaint();
}
} else {
this.filter = filter;
getObservable().setChanged();
getObservable().notifyObservers(filter);
forceRepaint();
}
} else {
this.filter = filter;
getObservable().setChanged();
getObservable().notifyObservers(filter);
forceRepaint();
}
}
/**
* Gets the filter.
*
* @return current filter, null if filter is not present
*/
public IFilter getFilter() {
return filter;
}
/**
* Sets rotation. Null means rotation doesn't affected. Selected traces will be redrawn with
* rotation with using of "selection" mode.
*
* @param rotation
* rotation to set to set
*/
/*public void setRotation(Rotation rotation) {
if (rotation == null) {
select = false;
overlay = false;
this.rotation = rotation;
}
else {
if(rotation.getMatrix()==null){
forceRepaint();
}
else {
List<ChannelView> selected = getCurrentSelectedChannelShowSet();
boolean dataFound = true;
for (ChannelView cv: selected) {
for (PlotDataProvider ch: cv.getPlotDataProviders()) {
if (!Rotation.isComplementaryChannelTrupleExist(ch)) {
dataFound = false;
break;
}
}
if (!dataFound)
break;
}
if (dataFound) {
this.rotation = rotation;
observable.setChanged();
observable.notifyObservers("ROT");
forceRepaint();
}
}
}
}*/
/**
* Gets the rotation.
*
* @return current rotation, null if rotation is not present
*/
/*public Rotation getRotation() {
return rotation;
}*/
/**
* Sets gain factor to scale data by.
*
* @param gain
* gain to set
*/
public void setRemoveGainState(RemoveGain gain) {
List<ChannelView> currentChannelShowSet = getCurrentChannelShowSet();
drawAreaPanel.removeAll();
for (ChannelView cv: currentChannelShowSet) {
drawAreaPanel.add(cv);
}
select = false;
overlay = false;
this.gain = gain;
getObservable().setChanged();
getObservable().notifyObservers("REMOVE GAIN");
forceRepaint();
}
public RemoveGain getRemoveGain(){
return this.gain;
}
/**
* Gets the max data length.
*
* @return Maximum length of visible data among all visible channels
*/
public int getMaxDataLength(){
int maxDataLength = 0;
for(PlotDataProvider channel: getChannelSet()){
int dataLength = channel.getDataLength(getTimeRange());
if(dataLength>maxDataLength){
maxDataLength = dataLength;
}
}
return maxDataLength;
}
/**
* Gets the mark position image.
*
* @return Image currently used to render position marker
*/
public Image getMarkPositionImage() {
return markPositionImage;
}
/**
* Sets the mark position image.
*
* @param image image to render position marker
*/
public void setMarkPositionImage(Image image) {
markPositionImage = image;
}
/**
* This method initializes drawAreaPanel.
*
* @return javax.swing.JPanel
*/
private DrawAreaPanel getDrawAreaPanel() {
if (drawAreaPanel == null) {
drawAreaPanel = new DrawAreaPanel();
}
return drawAreaPanel;
}
/**
* This method initializes southPanel.
*
* @param showTimePanel the show time panel
* @return javax.swing.JPanel
*/
private JPanel getSouthPanel(boolean showTimePanel) {
if (southPanel == null) {
southPanel = new SouthPanel(showTimePanel);
southPanel.setBackground(this.getBackground());
}
return southPanel;
}
/**
* Adds ChannelView to this panel.
*
* @param comp ChannelView to add
* @return added Component
*/
public Component addGraph(ChannelView comp) {
comp.setGraphPanel(this);
channelShowSet.add(comp);
if (showBigCursor) {
comp.setCursor(hiddenCursor);
} else {
comp.setCursor(crossCursor);
}
Component ret = drawAreaPanel.add(comp);
ret.doLayout();
return ret;
}
/**
* Gets the overlay state.
*
* @return current overlay mode. If overlay mode turned on, all traces loaded in graph panel
* will be shown in one shared ChannelView
*/
public boolean getOverlayState() {
return overlay;
}
/**
* Switch overlay mode on/off. If overlay mode turned on, all traces loaded in graph panel will
* be shown in one shared ChannelView
*/
public void overlay() {
if (overlay) {
drawAreaPanel.removeAll();
if (select) {
for (ChannelView cv: getSelectedChannelShowSet()) {
drawAreaPanel.add(cv);
}
} else {
for (ChannelView cv: channelShowSet) {
drawAreaPanel.add(cv);
}
}
overlay = false;
ChannelView.currentSelectionNumber = 0;
} else {
List<ChannelView> selected = getCurrentSelectedChannelShowSet();
if (selected.size() > 0) {
overlay = true;
drawAreaPanel.removeAll();
List<PlotDataProvider> toProcess = new ArrayList<PlotDataProvider>();
for (ChannelView cv: selected) {
toProcess.addAll(cv.getPlotDataProviders());
}
ChannelView overlay = channelViewFactory.getChannelView(toProcess);
overlay.setGraphPanel(this);
drawAreaPanel.add(overlay);
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(TraceView.getFrame(), "Please click check-boxes for panels to overlay", "Selection missing",
JOptionPane.WARNING_MESSAGE);
}
});
}
}
getObservable().setChanged();
getObservable().notifyObservers(overlay ? "OVR ON" : "OVR OFF");
forceRepaint(); // needed when selecting certain channels with new paint() method
}
/**
* Gets the select state.
*
* @return current selection mode. If selection mode turned on, will be shown only ChannelViews
* with selected selection checkboxes.
*/
public boolean getSelectState() {
return select;
}
/**
* Switch selection mode on/off. If selection mode turned on, will be shown only ChannelViews
* with selected selection checkboxes.
*/
public void select(Boolean undo) {
if (undo) {
selectionLevel--;
drawAreaPanel.removeAll();
if(selectionLevel > 0){
for(SelectionContainer sc: previousSelectedChannels){
if(sc.getSelectionLevel() == selectionLevel){
for (PlotDataProvider channel: sc.getChannelView().getPlotDataProviders()) {
ChannelView sel_cv = channelViewFactory.getChannelView(channel);
sel_cv.setGraphPanel(this);
drawAreaPanel.add(sel_cv);
}
previousSelectedChannels.remove(sc.getChannelView());
}
}
} else {
for (ChannelView cv: channelShowSet) {
for (PlotDataProvider channel: cv.getPlotDataProviders()) {
ChannelView sel_cv = channelViewFactory.getChannelView(channel);
sel_cv.setGraphPanel(this);
drawAreaPanel.add(sel_cv);
}
}
}
select = false;
overlay = false;
rotation = null;
ChannelView.currentSelectionNumber = 0;
selectedChannelShowSet.clear();
} else {
List<ChannelView> selected = getCurrentSelectedChannelShowSet();
if (selected.size() > 0) {
select = true;
drawAreaPanel.removeAll();
selectionLevel++;
for (ChannelView cv: selected) {
//List<PlotDataProvider> toProcess = new ArrayList<PlotDataProvider>();
previousSelectedChannels.add(new SelectionContainer(selectionLevel, cv));
for (PlotDataProvider channel: cv.getPlotDataProviders()) {
ChannelView sel_cv = channelViewFactory.getChannelView(channel);
sel_cv.setGraphPanel(this);
drawAreaPanel.add(sel_cv);
}
}
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(TraceView.getFrame(), "Please click check-boxes for panels to select", "Selection missing",
JOptionPane.WARNING_MESSAGE);
}
});
}
}
getObservable().setChanged();
getObservable().notifyObservers(select ? "SEL ON" : "SEL OFF");
getObservable().setChanged();
getObservable().notifyObservers(overlay ? "OVR ON" : "OVR OFF");
forceRepaint(); // needed for new paint() method
}
/* (non-Javadoc)
* @see java.awt.Container#remove(int)
*/
public void remove(int index) {
drawAreaPanel.remove(index);
channelShowSet.remove(index);
}
/* (non-Javadoc)
* @see java.awt.Container#remove(java.awt.Component)
*/
public void remove(Component comp) {
drawAreaPanel.remove(comp);
channelShowSet.remove(comp);
}
/* (non-Javadoc)
* @see java.awt.Container#removeAll()
*/
public void removeAll() {
drawAreaPanel.removeAll();
channelShowSet.clear();
}
/**
* Gets the axis font.
*
* @return font to draw axis
*/
public static Font getAxisFont() {
return axisFont;
}
/**
* Gets the selected earthquakes.
*
* @return earthquakes to draw on the graphs
*/
public Set<IEvent> getSelectedEarthquakes() {
return selectedEarthquakes;
}
/**
* Gets the selected phases.
*
* @return set of phases names to draw on the graphs
*/
public Set<String> getSelectedPhases() {
return selectedPhases;
}
/**
* Sets earthquakes and phase names to draw on the graphs. Will be drawn only phases which
* satisfy both sets.
*
* @param earthquakes
* set of earthquakes
* @param phases
* set of phase names
*/
public void setSelectedPhases(Set<IEvent> earthquakes, Set<String> phases) {
selectedEarthquakes = earthquakes;
selectedPhases = phases;
repaint(); // potential bug with redrawing quake/phase on graph
}
/**
* Adds the observer.
*
* @param o the o
*/
public void addObserver(Observer o) {
getObservable().addObserver(o);
}
/**
* Delete observer.
*
* @param o the o
*/
public void deleteObserver(Observer o) {
getObservable().deleteObserver(o);
}
/* (non-Javadoc)
* @see javax.swing.JComponent#paint(java.awt.Graphics)
*/
public void paint(Graphics g) {
if(!paintNow){
paintNow = true;
int infoPanelWidth = channelViewFactory.getInfoAreaWidth();
// Only pixelize and paint data if initial load or when data is changed
//!mouseRepaint
if (initialPaint || forceRepaint || !mouseRepaint || ChannelView.tooltipVisible) {
//RepaintManager rm = RepaintManager.currentManager(this);
//rm.markCompletelyDirty(this);
// Pixelization should only occur for data changes
// (i.e. filtering, spectral density, zooming, etc.)
long startl = System.nanoTime();
// need to create a boolean for mouseDragging (i.e. zooming)
// mouse clicked, pressed, released, dragged
if (initialPaint || forceRepaint) {
if (initialPaint) {
System.out.print("Pixelizing channel data...");
}
final List<String> channelsWithErrors = new ArrayList<String>();
for (Component component: drawAreaPanel.getComponents()) {
ChannelView view = (ChannelView) component;
if (view.getHeight() == 0 || view.getWidth() == 0) {
// Ugly hack to avoid lack of screen redraw sometimes
//logger.debug("DrawAreaPanel: rebuilding corrupted layout");
drawAreaPanel.doLayout();
for (Component comp: drawAreaPanel.getComponents()) {
comp.doLayout();
}
}
if (initialPaint) {
System.out.print("...");
}
String errorChannel = view.updateData();
if(errorChannel != "")
channelsWithErrors.add(errorChannel);
}
if(channelsWithErrors.size() > 0){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(TraceView.getFrame(), "Error with:" + "\n" + StringUtils.join(channelsWithErrors, "\n"), "Warning", JOptionPane.WARNING_MESSAGE);
}
});
}
if (initialPaint) {
System.out.print("\n");
}
}
if (initialPaint) {
System.out.print("Drawing channel data...");
}
super.paint(g); // calls ChannelView.paint(Graphics g)
long endl = System.nanoTime() - startl;
double end = endl * Math.pow(10, -9);
if (initialPaint) {
System.out.println("\nPixelizing/painting duration = " + end + " sec");
}
// Drawing cursor
g.setXORMode(new Color(204, 204, 51));
if (mouseX > infoPanelWidth && mouseY < getHeight() - southPanel.getHeight() && showBigCursor) {
// g.setXORMode(selectionColor); Hack for java 6
g.drawLine(infoPanelWidth, mouseY, getWidth(), mouseY);
g.drawLine(mouseX, 0, mouseX, getHeight());
previousMouseX = mouseX;
previousMouseY = mouseY;
}
// Drawing selection area
paintSelection(g, selectedAreaXbegin, selectedAreaXend, selectedAreaYbegin, selectedAreaYend, "Drawing");
previousSelectedAreaXbegin = selectedAreaXbegin;
previousSelectedAreaXend = selectedAreaXend;
previousSelectedAreaYbegin = selectedAreaYbegin;
previousSelectedAreaYend = selectedAreaYend;
if (initialPaint) {
System.out.print("\n"); // skip to next line for next repaint() readout
}
initialPaint = false;
forceRepaint = false;
} else { // Regular MouseMovements in and between ChannelView and GraphPanel panels
g.setXORMode(selectionColor);
if (previousMouseX >= 0 && previousMouseY >= 0) {
// Erasing cursor
if (showBigCursor) {
g.drawLine(infoPanelWidth, previousMouseY, getWidth(), previousMouseY);
g.drawLine(previousMouseX, 0, previousMouseX, getHeight());
}
previousMouseX = -1;
previousMouseY = -1;
}
paintSelection(g, previousSelectedAreaXbegin, previousSelectedAreaXend, previousSelectedAreaYbegin, previousSelectedAreaYend, "Erasing");
previousSelectedAreaXbegin = Long.MAX_VALUE;
previousSelectedAreaXend = Long.MIN_VALUE;
previousSelectedAreaYbegin = Double.NaN;
previousSelectedAreaYend = Double.NaN;
if (mouseX > infoPanelWidth && mouseY < getHeight() - southPanel.getHeight()) {
// Drawing cursor
if (showBigCursor) {
g.drawLine(infoPanelWidth, mouseY, getWidth(), mouseY);
g.drawLine(mouseX, 0, mouseX, getHeight());
}
previousMouseX = mouseX;
previousMouseY = mouseY;
}
//logger.debug("Drawing selection area");
paintSelection(g, selectedAreaXbegin, selectedAreaXend, selectedAreaYbegin, selectedAreaYend, "Drawing");
previousSelectedAreaXbegin = selectedAreaXbegin;
previousSelectedAreaXend = selectedAreaXend;
previousSelectedAreaYbegin = selectedAreaYbegin;
previousSelectedAreaYend = selectedAreaYend;
forceRepaint = false;
mouseRepaint = false;
cvMouseMoved = false;
}
paintNow = false;
}
}
/**
* Paint selection.
*
* @param g the g
* @param Xbegin the xbegin
* @param Xend the xend
* @param Ybegin the ybegin
* @param Yend the yend
* @param message the message
*/
private void paintSelection(Graphics g, long Xbegin, long Xend, double Ybegin, double Yend, String message) {
int infoPanelWidth = channelViewFactory.getInfoAreaWidth();
if (Xbegin != Long.MAX_VALUE && Xend != Long.MIN_VALUE && mouseSelectionEnabled) {
logger.debug(message + " selection X: " + getXposition(Xbegin) + ", " + getXposition(Xend));
if (Xend > Xbegin) {
int begPos = getXposition(Xbegin);
int leftPos = begPos >= 0 ? begPos + infoPanelWidth + getInsets().left : infoPanelWidth + getInsets().left;
int rightPos = begPos >= 0 ? getXposition(Xend) - getXposition(Xbegin) : getXposition(Xend) - getXposition(Xbegin) + begPos;
g.fillRect(leftPos, 0, rightPos, getHeight());
} else {
int begPos = getXposition(Xend);
int leftPos = begPos >= 0 ? begPos + infoPanelWidth + getInsets().left : infoPanelWidth + getInsets().left;
int rightPos = begPos >= 0 ? getXposition(Xbegin) - getXposition(Xend) : getXposition(Xbegin) - getXposition(Xend) + begPos;
g.fillRect(leftPos, 0, rightPos, getHeight());
}
}
if (!new Double(Ybegin).isNaN() && !new Double(Yend).isNaN()) {
// lg.debug(message + " selection Y: " + getScaleMode().getY(Ybegin) + ", " +
// getScaleMode().getY(Yend));
if (Yend > Ybegin) {
g.fillRect(infoPanelWidth, getScaleMode().getY(Yend), getWidth(), getScaleMode().getY(Ybegin)
- getScaleMode().getY(Yend));
} else {
g.fillRect(infoPanelWidth, getScaleMode().getY(Ybegin), getWidth(), getScaleMode().getY(Yend)
- getScaleMode().getY(Ybegin));
}
}
}
/**
* Gets the last clicked time.
*
* @return time of last click (in internal Java format)
*/
public long getLastClickedTime() {
if (mouseClickX == -1)
return Long.MAX_VALUE;
else {
return getTime(mouseClickX - channelViewFactory.getInfoAreaWidth() - getInsets().left);
}
}
/**
* Gets the selection time.
*
* @return time of first selection point while dragging (in internal Java format)
*/
public long getSelectionTime() {
if (mousePressX == -1)
return Long.MAX_VALUE;
else {
return getTime(mousePressX - channelViewFactory.getInfoAreaWidth() - getInsets().left);
}
}
/**
* Computes trace time value.
*
* @param x screen panel coordinate
* @return time value in internal Java format
*/
public long getTime(int x) {
// lg.debug("GraphPanel getTime: " + x);
Insets i = getInsets();
double sr = new Double(getTimeRange().getDuration()) / (getWidth() - i.left - i.right - channelViewFactory.getInfoAreaWidth());
return new Double(getTimeRange().getStart() + x * sr).longValue();
}
/**
* Computes screen panel coordinate.
*
* @param date trace time value in internal Java format
* @return screen panel coordinate
*/
public int getXposition(long date) {
if (getTimeRange() == null)
return Integer.MAX_VALUE;
else
return new Double((getWidth() - channelViewFactory.getInfoAreaWidth() - getInsets().left - getInsets().right)
* new Double((date - getTimeRange().getStart())) / new Double(getTimeRange().getDuration())).intValue();
}
/**
* Methods from MouseInputListener interface to handle mouse events.
*
* @param e the e
*/
public void mouseMoved(MouseEvent e) {
if (cvMouseMoved) { // checks if we have a ChannelView movement
if ((button != MouseEvent.NOBUTTON) && (e.isControlDown() || e.isShiftDown())) {
mouseDragged(e);
} else {
// ChannelView JPanel mouse movements
mouseX = e.getX();
mouseY = e.getY();
mouseRepaint = false;
repaint();
}
}
}
/* (non-Javadoc)
* @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
*/
public void mouseDragged(MouseEvent e) {
// need a check in paint(Graphics) for mouse
// clicking and dragging for zooming (forceRepaint?)
mouseX = e.getX();
mouseY = e.getY();
mouseDragged = true; // this may cause errs
mouseRepaint = false;
repaint();
}
// What are the orders for Button 1,2,3
/* (non-Javadoc)
* @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
*/
// Left/Middle/Right?
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
if (mouseAdapter != null) {
mouseAdapter.mouseClickedButton3(e.getX(), e.getY(), this);
}
} else if (e.getButton() == MouseEvent.BUTTON2
|| ((e.getButton() == MouseEvent.BUTTON1) && (e.isShiftDown() == true))) {
if (mouseAdapter != null) {
mouseAdapter.mouseClickedButton2(e.getX(), e.getY(), this);
}
} else if (e.getButton() == MouseEvent.BUTTON1) {
mouseClickX = e.getX();
if (mouseAdapter != null) {
mouseAdapter.mouseClickedButton1(e.getX(), e.getY(), this);
}
}
}
/**
* Enter routines for XMAXframe/ChannelView/GraphPanel.
*
* @param e the e
*/
// XMAXFrame mouse entering (for later use)
public void xframeMouseEntered(MouseEvent e) {
if (cvMouseExited) {
// need mouse(X,Y) = (-1,-1) so no crosshair
// cursor is drawn, the previous is erased
mouseX = -1;
mouseY = -1;
cvMouseExited = false;
mouseRepaint = false;
}
}
/**
* Cv mouse entered.
*
* @param e the e
*/
// Method for ChannelView mouse entering
public void cvMouseEntered(MouseEvent e) {
cvMouseExited = false;
}
/**
* Method is called when the mouse enters the chart area.
*
* @param e the event
* @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
*/
// Method for GraphPanel mouse entering
public void mouseEntered(MouseEvent e) {
if (cvMouseExited) {
// need mouse(X,Y) = (-1,-1) so no crosshair
// cursor is drawn, the previous is erased
mouseX = -1;
mouseY = -1;
cvMouseExited = false;
mouseRepaint = false;
}
}
/**
* Exit routines for XMAXframe/ChannelView/GraphPanel.
*
* @param e the e
*/
// Method for XMAXframe mouse exiting (later use)
public void xframeMouseExited(MouseEvent e) {
}
/**
* Cv mouse exited.
*
* @param e the e
*/
// Method for ChannelView mouse exiting
public void cvMouseExited(MouseEvent e) {
cvMouseExited = true;
mouseX = -1;
mouseY = -1;
repaint();
}
/* (non-Javadoc)
* @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
*/
// Method for GraphPanel mouse exiting (later use)
public void mouseExited(MouseEvent e) {
if (mouseX != -1 || mouseY != -1) {
mouseX = -1;
mouseY = -1;
repaint();
}
}
/**
* Mouse clicked.
*
* @param e the event
* @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
*/
public void mousePressed(MouseEvent e) {
mousePressX = e.getX();
mousePressY = e.getY();
// one-button mouse Mac OSX behaviour emulation
if (e.getButton() == MouseEvent.BUTTON1) {
if (e.isShiftDown()) {
button = MouseEvent.BUTTON2;
} else if (e.isControlDown()) {
button = MouseEvent.BUTTON3;
} else {
button = MouseEvent.BUTTON1;
}
} else {
button = e.getButton();
}
}
/**
* Mouse released.
*
* @param e the event
* @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
*/
public void mouseReleased(MouseEvent e) {
if (mouseSelectionEnabled) {
button = MouseEvent.NOBUTTON;
if (mouseDragged) { // forceRepaint() when zooming
//mouseRepaint = false;
//forceRepaint(); // forceRepaint=true, repaint()
mouseRepaint = true;
repaint();
mouseDragged = false; //reset mouse dragged to false after repainting
} else { // mouse clicked => erase cursor
forceRepaint = false;
mouseRepaint = false;
repaint();
}
}
}
// From Printable interface
// **NOTE: This method is not working correctly.
/* (non-Javadoc)
* @see java.awt.print.Printable#print(java.awt.Graphics, java.awt.print.PageFormat, int)
*/
// Default printer is not assigned
public int print(Graphics pg, PageFormat pf, int pageNum) {
if (pageNum > 0) {
return Printable.NO_SUCH_PAGE;
}
Graphics2D g2 = (Graphics2D) pg;
g2.translate(pf.getImageableX(), pf.getImageableY());
g2.scale(g2.getClipBounds().width / new Double(this.getWidth()), g2.getClipBounds().height / new Double(this.getHeight()));
this.paint(g2);
return Printable.PAGE_EXISTS;
}
/**
* Gets the nearest segment begin.
*
* @param time to start nearest segment searching
* @return Time of nearest segment's begin(among all loaded traces) after given time
*/
public Date getNearestSegmentBegin(Date time) {
long nearestSegment = Long.MAX_VALUE;
List<PlotDataProvider> channels = getChannelSet();
for (PlotDataProvider channel: channels) {
for (Segment segment: channel.getRawData()) {
long segmentStart = segment.getStartTime().getTime();
if (segmentStart > time.getTime() && segmentStart < nearestSegment) {
nearestSegment = segmentStart;
}
}
}
if (nearestSegment == Long.MAX_VALUE) {
return null;
} else {
return new Date(nearestSegment);
}
}
/**
* Gets the nearest segment end.
*
* @param time to start nearest segment searching
* @return Time of nearest segment's end(among all loaded traces) before given time
*/
public Date getNearestSegmentEnd(Date time) {
long nearestSegment = Long.MIN_VALUE;
List<PlotDataProvider> channels = getChannelSet();
for (PlotDataProvider channel: channels) {
for (Segment segment: channel.getRawData()) {
long segmentEnd = segment.getEndTime().getTime();
if (segmentEnd < time.getTime() && segmentEnd > nearestSegment) {
nearestSegment = segmentEnd;
}
}
}
if (nearestSegment == Long.MIN_VALUE) {
return null;
} else {
return new Date(nearestSegment);
}
}
/* (non-Javadoc)
* @see javax.swing.JComponent#setBackground(java.awt.Color)
*/
public void setBackground(Color color){
super.setBackground(color);
if(southPanel != null){
southPanel.setBackground(color);
}
}
/**
* Time-axis panel used by GraphPanel.
*/
class AxisPanel extends JPanel {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The axis. */
private DateAxis axis = null;
/**
* Instantiates a new axis panel.
*/
public AxisPanel() {
super();
// BorderLayout: Ignores the width dimension for NORTH/SOUTH components
setMinimumSize(new Dimension(200, 20));
setPreferredSize(new Dimension(200, 20));
axis = new DateAxis();
axis.setTimeZone(TimeZone.getTimeZone("GMT"));
axis.setDateFormatOverride(TimeInterval.df_long);
//axis.setMinorTickCount(10);
axis.setMinorTickMarksVisible(true);
axis.setTickMarkOutsideLength(6F);
axis.setTickLabelInsets( new RectangleInsets(6., 4., 2., 4.) );
}
/**
* Sets the date format.
*
* @param df date format to use in axis
* @see TimeInterval
*/
@SuppressWarnings("unused")
private void setDateFormat(SimpleDateFormat df) {
if (!axis.getDateFormatOverride().equals(df)) {
axis.setDateFormatOverride(df);
}
}
/**
* Sets the time range.
*
* @param ti time interval of axis
*/
public void setTimeRange(TimeInterval ti) {
final long ONE_DAY = 1L*86400000;
final long TWO_DAYS = 2L*86400000;
final long THREE_DAYS = 3L*86400000;
final long FOUR_DAYS = 4L*86400000;
final long ONE_WEEK = 7L*86400000;
//final long TWO_WEEKS = 14L*86400000;
//final long FOUR_WEEKS = 28L*86400000;
//final long EIGHT_WEEKS= 56L*86400000;
boolean needwait = false;
if (axis.getMinimumDate().getTime() == 0 && axis.getMaximumDate().getTime() == 1) {
needwait = true;
}
if (ti != null) {
axis.setMinimumDate(ti.getStartTime());
axis.setMaximumDate(ti.getEndTime());
if (ti.getDuration() < 10000) {
axis.setDateFormatOverride(TimeInterval.df);
} else if (ti.getDuration() < 300000) {
axis.setDateFormatOverride(TimeInterval.df_middle);
} else {
axis.setDateFormatOverride(TimeInterval.df_long);
}
} else {
axis.setMinimumDate(new Date(0));
axis.setMaximumDate(new Date(1000000));
axis.setDateFormatOverride(TimeInterval.df_long);
}
if (needwait) {
// to let finish previous repaint() and avoid blank axis
try {
Thread.sleep(60);
} catch (InterruptedException e) {
// do nothing
logger.error("InterruptedException:", e);
}
}
if (ti != null) {
int minorTickCount = 4;
double nDays = ti.getDuration()/(double)ONE_DAY;
int interval = (int)(nDays/11.);
double remainder = nDays%11.;
double x = remainder/(double)interval;
if (x > 0.5) {
interval++;
}
if (ti.getDuration() > ONE_WEEK) { // tD > 1 Week
axis.setTickUnit( new DateTickUnit(DateTickUnitType.DAY, interval) );
axis.setMinorTickCount(minorTickCount);
}
else if (ti.getDuration() > FOUR_DAYS) { // 4 Days < tD <= 1 Week
axis.setTickUnit( new DateTickUnit(DateTickUnitType.HOUR, 12) );
axis.setMinorTickCount(minorTickCount);
}
else if (ti.getDuration() > THREE_DAYS) { // 3 Days < tD <= 4 Days
axis.setTickUnit( new DateTickUnit(DateTickUnitType.HOUR, 8) );
axis.setMinorTickCount(minorTickCount);
}
else if (ti.getDuration() > TWO_DAYS) { // 2 Days < tD <= 3 Days
axis.setTickUnit( new DateTickUnit(DateTickUnitType.HOUR, 6) );
axis.setMinorTickCount(minorTickCount);
}
else if (ti.getDuration() > ONE_DAY) { // 1 Day < tD <= 2 Days
axis.setTickUnit( new DateTickUnit(DateTickUnitType.HOUR, 4) );
axis.setMinorTickCount(minorTickCount);
}
else if (ti.getDuration() > 36000000) { // 8 - 24hrs
axis.setTickUnit( new DateTickUnit(DateTickUnitType.HOUR, 2) );
axis.setMinorTickCount(minorTickCount);
}
else if (ti.getDuration() > 18000000) { // 4 - 8 hrs
axis.setTickUnit( new DateTickUnit(DateTickUnitType.HOUR, 1) );
axis.setMinorTickCount(minorTickCount);
}
else if (ti.getDuration() > 7200000) { // 2 - 4 hrs
axis.setTickUnit( new DateTickUnit(DateTickUnitType.MINUTE, 30) );
axis.setMinorTickCount(15);
}
else if (ti.getDuration() > 3600000) { // 1 - 2 hrs
axis.setTickUnit( new DateTickUnit(DateTickUnitType.MINUTE, 15) );
axis.setMinorTickCount(15);
}
else if (ti.getDuration() > 1600000) { // 30min - 1 hr
axis.setTickUnit( new DateTickUnit(DateTickUnitType.MINUTE, 5) );
axis.setMinorTickCount(5);
}
else if (ti.getDuration() > 600000) { // 10min - 30min
axis.setTickUnit( new DateTickUnit(DateTickUnitType.MINUTE, 2) );
axis.setMinorTickCount(4);
}
else if (ti.getDuration() > 120000) { // 2 min < tD <= 10 min
axis.setTickUnit( new DateTickUnit(DateTickUnitType.MINUTE, 1) );
axis.setMinorTickCount(4);
}
else if (ti.getDuration() > 30000) { // 30 sec < tD <= 2 min
axis.setTickUnit( new DateTickUnit(DateTickUnitType.SECOND, 30) );
axis.setMinorTickCount(30);
}
else if (ti.getDuration() > 12000) { // 12 sec < tD <= 30 sec
axis.setTickUnit( new DateTickUnit(DateTickUnitType.SECOND, 3) );
axis.setMinorTickCount(1);
}
else { // tD < 12 sec
axis.setTickUnit( new DateTickUnit(DateTickUnitType.SECOND, 1) );
axis.setMinorTickCount(10);
}
}
repaint();
}
/* (non-Javadoc)
* @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
public void paintComponent(Graphics g) {
//lg.debug("AxisPanel paintComponent");
super.paintComponent(g);
int infoPanelWidth = channelViewFactory.getInfoAreaWidth();
if (axis.getMinimumDate().getTime() != 0 && axis.getMaximumDate().getTime() != 1) {
logger.debug("min date " + axis.getMinimumDate() + ", max date " + axis.getMaximumDate());
//axis.draw((Graphics2D) g, 0, new Rectangle(infoPanelWidth + getInsets().left, 0, getWidth(), getHeight()), new Rectangle(
//infoPanelWidth + getInsets().left, 0, getWidth(), 10), RectangleEdge.BOTTOM, null);
// MTH: The line above is incorrect: The Rectangle width should = (axisPanel - infoPanelWidth)
// Where infoPanel is the leftmost panel (showing the trace amplitude values)
// Rectangle(int x, int y, int width, int height) - (x,y)=upper-left corner
// Note that getInsets() will try to get the border widths of "this" = axisPanel (which doesn't have a border!)
// e.g., getInsets().left = 0
// The +2 was added to the width as an empirical correction to try to match x-axis times with the trace
// time (given by left clicking on the trace)
// jfreechart: DateAxis.draw( Graphics2D, double cursor, Rectangle2D plotArea, Rectangle2D drawArea, ...)
axis.draw((Graphics2D) g, 0,
new Rectangle(infoPanelWidth+getInsets().left, 0, getWidth()-infoPanelWidth + 2, getHeight()),
new Rectangle(infoPanelWidth+getInsets().left, 0, getWidth()-infoPanelWidth + 2, 10),
RectangleEdge.BOTTOM, null);
}
}
}
/**
* Bottom panel with general timing information - start time, shown duration, end time, used in
* GraphPanel.
*/
class TimeInfoPanel extends JPanel {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The start. */
private JLabel start = null;
/** The duration. */
private JLabel duration = null;
/** The end. */
private JLabel end = null;
/** The grid layout. */
GridLayout gridLayout = null;
/**
* Instantiates a new time info panel.
*/
public TimeInfoPanel() {
super();
setFont(getAxisFont());
gridLayout = new GridLayout();
start = new JLabel("", SwingConstants.LEFT);
duration = new JLabel("", SwingConstants.CENTER);
end = new JLabel("", SwingConstants.RIGHT);
setLayout(gridLayout);
gridLayout.setColumns(3);
gridLayout.setRows(1);
add(start);
add(duration);
add(end);
}
/**
* Update.
*
* @param ti the ti
*/
public void update(TimeInterval ti) {
logger.debug("updating, ti = " + ti);
if (ti != null) {
start.setText(TimeInterval.formatDate(ti.getStartTime(), TimeInterval.DateFormatType.DATE_FORMAT_NORMAL));
duration.setText(ti.convert());
end.setText(TimeInterval.formatDate(ti.getEndTime(), TimeInterval.DateFormatType.DATE_FORMAT_NORMAL));
}
}
}
/**
* The Class SouthPanel.
*/
class SouthPanel extends JPanel {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The axis panel. */
private AxisPanel axisPanel;
/** The info panel. */
private TimeInfoPanel infoPanel;
/**
* Instantiates a new south panel.
*
* @param showTimePanel the show time panel
*/
public SouthPanel(boolean showTimePanel) {
super();
GridLayout gridLayout = new GridLayout();
axisPanel = new AxisPanel();
axisPanel.setBackground(this.getBackground());
infoPanel = new TimeInfoPanel();
infoPanel.setBackground(this.getBackground());
setLayout(gridLayout);
gridLayout.setColumns(1);
gridLayout.setRows(0);
add(axisPanel);
//axisPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK,2));
if(showTimePanel){
add(infoPanel);
//infoPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK,2));
}
}
/**
* Gets the axis panel.
*
* @return the axis panel
*/
public AxisPanel getAxisPanel() {
return axisPanel;
}
/**
* Gets the info panel.
*
* @return the info panel
*/
public TimeInfoPanel getInfoPanel() {
return infoPanel;
}
/* (non-Javadoc)
* @see javax.swing.JComponent#setBackground(java.awt.Color)
*/
public void setBackground(Color color){
super.setBackground(color);
if(axisPanel!=null){
axisPanel.setBackground(color);
//axisPanel.setBackground(Color.BLUE);
}
if(infoPanel!=null){
infoPanel.setBackground(color);
//infoPanel.setBackground(Color.RED);
}
}
}
/**
* The Class DrawAreaPanel.
*/
class DrawAreaPanel extends JPanel {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/**
* Instantiates a new draw area panel.
*/
public DrawAreaPanel() {
super();
GridLayout gridLayout = new GridLayout();
gridLayout.setColumns(1);
gridLayout.setRows(0);
setLayout(gridLayout);
}
/* (non-Javadoc)
* @see javax.swing.JComponent#paint(java.awt.Graphics)
*/
public void paint(Graphics g) {
//logger.debug("paint() Height: " + getHeight() + ", width: " + getWidth() + ", " + getComponents().length + " ChannelViews");
super.paint(g);
}
}
/* (non-Javadoc)
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
public void update(Observable observable, Object obj) {
logger.debug(this + ": update request from " + observable);
if (obj instanceof IScaleModeState) {
setScaleMode((IScaleModeState) obj);
} else if (obj instanceof IColorModeState) {
setColorMode((IColorModeState) obj);
} else if (obj instanceof IMeanState) {
setMeanState((IMeanState) obj);
} else if (obj instanceof IOffsetState) {
setOffsetState((IOffsetState) obj);
}
}
public GraphPanelObservable getObservable() {
return observable;
}
public void setObservable(GraphPanelObservable observable) {
this.observable = observable;
}
/**
* The Class GraphPanelObservable.
*/
public class GraphPanelObservable extends Observable {
/* (non-Javadoc)
* @see java.util.Observable#setChanged()
*/
public void setChanged() {
super.setChanged();
}
}
}