/*******************************************************************************
* Copyright (c) 2010 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
******************************************************************************/
package org.eclipse.nebula.visualization.xygraph.figures;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseListener;
import org.eclipse.draw2d.MouseMotionListener;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.nebula.visualization.internal.xygraph.undo.AxisPanOrZoomCommand;
import org.eclipse.nebula.visualization.internal.xygraph.undo.SaveStateCommand;
import org.eclipse.nebula.visualization.xygraph.dataprovider.IDataProvider;
import org.eclipse.nebula.visualization.xygraph.linearscale.LinearScale;
import org.eclipse.nebula.visualization.xygraph.linearscale.Range;
import org.eclipse.nebula.visualization.xygraph.util.GraphicsUtil;
import org.eclipse.nebula.visualization.xygraph.util.Log10;
import org.eclipse.nebula.visualization.xygraph.util.SWTConstants;
import org.eclipse.nebula.visualization.xygraph.util.XYGraphMediaFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
/**
* The axis figure.
*
* @author Xihui Chen
* @author Kay Kasemir - Axis zoom/pan tweaks
*/
public class Axis extends LinearScale {
/** The ratio of the shrink/expand area for one zoom. */
final static double ZOOM_RATIO = 0.1;
/** The auto zoom interval in ms. */
final static int ZOOM_SPEED = 200;
// private static final Color GRAY_COLOR =
// XYGraphMediaFactory.getInstance().getColor(
// XYGraphMediaFactory.COLOR_GRAY);
private String title;
@Override
public void setFont(Font font) {
super.setFont(font);
this.scaleFontData = getFont().getFontData()[0];
}
private final List<Trace> traceList = new ArrayList<Trace>();
private IXYGraph xyGraph;
private Grid grid;
private Font titleFont;
// title FontData : Add because of SWT illegal thread access
private FontData titleFontData;
// title FontData : Add because of SWT illegal thread access
private FontData scaleFontData;
private boolean autoScale = false;
private boolean showMajorGrid = false;
private boolean showMinorGrid = false;
private boolean isInverted = false;
private Color majorGridColor;
private Color minorGridColor;
private boolean dashGridLine = true;
private double autoScaleThreshold = 0.01;
protected final List<IAxisListener> listeners = new ArrayList<IAxisListener>();
private ZoomType zoomType = ZoomType.NONE;
private Point start;
private Point end;
private boolean armed;
private Range startRange;
private final Cursor grabbing;
private Color revertBackColor;
private RGB colorRGB;
private RGB majorGridColorRGB;
public FontData getTitleFontData() {
return titleFontData;
}
public FontData getScaleFontData() {
return scaleFontData;
}
/**
* Constructor
*
* @param title
* title of the axis
* @param yAxis
* true if this is the Y-Axis, false if this is the X-Axis.
*/
public Axis(final String title, final boolean yAxis) {
super();
this.title = title;
if (yAxis)
setOrientation(Orientation.VERTICAL);
// Save drawing line operation in RAP.
if (GraphicsUtil.isRAP())
setMinorTicksVisible(false);
final AxisMouseListener panner = new AxisMouseListener();
addMouseListener(panner);
addMouseMotionListener(panner);
grabbing = XYGraphMediaFactory.getInstance().getCursor(XYGraphMediaFactory.CURSOR_GRABBING_ON_AXIS_PATH);
Font sysFont = Display.getCurrent().getSystemFont();
titleFont = XYGraphMediaFactory.getInstance()
.getFont(new FontData(sysFont.getFontData()[0].getName(), 12, SWT.BOLD)); // $NON-NLS-1$
if (getBackgroundColor() != null) {
RGB backRGB = getBackgroundColor().getRGB();
revertBackColor = XYGraphMediaFactory.getInstance().getColor(255 - backRGB.red, 255 - backRGB.green,
255 - backRGB.blue);
} else
revertBackColor = XYGraphMediaFactory.getInstance().getColor(100, 100, 100);
}
public void addListener(final IAxisListener listener) {
if (listeners.contains(listener))
return;
listeners.add(listener);
}
public boolean removeListener(final IAxisListener listener) {
return listeners.remove(listener);
}
protected void fireRevalidated() {
for (IAxisListener listener : listeners)
listener.axisRevalidated(this);
}
protected void fireAxisRangeChanged(final Range old_range, final Range new_range) {
for (IAxisListener listener : listeners)
listener.axisRangeChanged(this, old_range, new_range);
}
@Override
public void setRange(final double lower, final double upper) {
Range old_range = getRange();
super.setRange(lower, upper);
fireAxisRangeChanged(old_range, getRange());
}
/**
* Inverts the axis if set to True
*
* @param isInverted
*/
public void setInverted(boolean isInverted) {
if (this.isInverted == isInverted)
return;
this.isInverted = isInverted;
double min = getRange().getLower();
double max = getRange().getUpper();
if ((isInverted && (min < max)) || (!isInverted && (min > max))) {
setRange(new Range(max, min));
}
xyGraph.repaint();
}
/**
* Returns True if axis is inverted, False otherwise
*
* @return
*/
public boolean isInverted() {
return isInverted;
}
@Override
protected void layout() {
super.layout();
fireRevalidated();
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
revalidate();
}
@Override
public void setForegroundColor(final Color color) {
Color oldColor = getForegroundColor();
super.setForegroundColor(color);
colorRGB = color.getRGB();
if (xyGraph != null)
xyGraph.repaint();
fireAxisForegroundColorChanged(oldColor, color);
}
@Override
public void setMinorTicksVisible(boolean minorTicksVisible) {
// Save line operation in RAP.
if (GraphicsUtil.isRAP())
super.setMinorTicksVisible(false);
else
super.setMinorTicksVisible(minorTicksVisible);
}
public RGB getForegroundColorRGB() {
return colorRGB;
}
private void fireAxisForegroundColorChanged(Color oldColor, Color newColor) {
for (IAxisListener listener : listeners)
listener.axisForegroundColorChanged(this, oldColor, newColor);
}
@Override
public void setBackgroundColor(Color bg) {
RGB backRGB = bg.getRGB();
revertBackColor = XYGraphMediaFactory.getInstance().getColor(255 - backRGB.red, 255 - backRGB.green,
255 - backRGB.blue);
super.setBackgroundColor(bg);
}
@Override
public Dimension getPreferredSize(final int wHint, final int hHint) {
final Dimension d = super.getPreferredSize(wHint, hHint);
if (isVisible()) {
if (isHorizontal())
d.height += FigureUtilities.getTextExtents(title, titleFont).height;
else
d.width += FigureUtilities.getTextExtents(title, titleFont).height;
} else { // Not visible, flatten it to use zero height resp. width
if (isHorizontal())
d.height = 0;
else
d.width = 0;
}
return d;
}
@Override
protected void paintClientArea(final Graphics graphics) {
// Don't do anything when hidden
if (!isVisible())
return;
super.paintClientArea(graphics);
// graphics.pushState();
graphics.setFont(titleFont);
final Dimension titleSize = FigureUtilities.getTextExtents(title, titleFont);
if (isHorizontal()) {
if (getTickLabelSide() == LabelSide.Primary)
graphics.drawText(title, bounds.x + bounds.width / 2 - titleSize.width / 2,
bounds.y + bounds.height - titleSize.height);
else
graphics.drawText(title, bounds.x + bounds.width / 2 - titleSize.width / 2, bounds.y);
} else {
final int w = titleSize.height;
final int h = titleSize.width + 1;
if (getTickLabelSide() == LabelSide.Primary) {
GraphicsUtil.drawVerticalText(graphics, title, bounds.x, bounds.y + bounds.height / 2 - h / 2, false);
} else {
GraphicsUtil.drawVerticalText(graphics, title, bounds.x + bounds.width - w,
bounds.y + bounds.height / 2 - h / 2, true);
}
}
// graphics.popState();
// Show the start/end cursor or the 'rubberband' of a zoom operation?
if (armed && end != null && start != null) {
switch (zoomType) {
case RUBBERBAND_ZOOM:
case HORIZONTAL_ZOOM:
case VERTICAL_ZOOM:
case DYNAMIC_ZOOM:
graphics.setLineStyle(SWTConstants.LINE_DOT);
graphics.setLineWidth(1);
graphics.setForegroundColor(revertBackColor);
graphics.drawRectangle(start.x, start.y, end.x - start.x - 1, end.y - start.y - 1);
break;
default:
break;
}
}
}
/**
* @return Range that reflects the minimum and maximum value of all traces
* on this axis. Returns <code>null</code> if there is no trace
* data.
*/
public Range getTraceDataRange() {
double low = Double.POSITIVE_INFINITY;
double high = Double.NEGATIVE_INFINITY;
for (Trace trace : traceList) {
if (trace.getDataProvider() == null || !trace.isVisible())
continue;
final Range range;
if (isHorizontal())
range = trace.getDataProvider().getXDataMinMax();
else
range = trace.getDataProvider().getYDataMinMax();
if (range == null)
continue;
if (Double.isInfinite(range.getLower()) || Double.isInfinite(range.getUpper())
|| Double.isNaN(range.getLower()) || Double.isNaN(range.getUpper()))
continue;
if (low > range.getLower())
low = range.getLower();
if (high < range.getUpper())
high = range.getUpper();
}
if (Double.isInfinite(low) || Double.isInfinite(high))
return null;
return new Range(low, high);
}
/**
* Perform an auto-scale: Axis limits are set to the value range of the
* traces on this axis. Includes some optimization: Axis range is set a
* little wider than exact trace data range. When auto-scale would only
* perform a minor axis adjustment, axis is left unchanged.
*
* @param force
* If true, the axis will be auto-scaled by force regardless the
* autoScale field. Otherwise, it will use the autoScale field to
* judge whether an auto-scale will be performed.
* @return true if the axis is repainted due to range change.
*
* @see #autoScaleThreshold
*/
public boolean performAutoScale(final boolean force) {
// Anything to do? Autoscale not enabled nor forced?
if (traceList.size() <= 0 || !(force || autoScale))
return false;
// Idea: Stop Autoscale on XYGraph whenever one of the Zoom buttons is
// toggled!
// Seems reasonable, but doesn't work:
// User may have selected HORIZONTAL_ZOOM to configure a time range,
// but still expects the vertical value axes to auto-zoom.
// Or user selected a VERTICAL_ZOOM to adjust _one_ axis,
// but still expects _other_ vertical axes to auto-zoom
// -> Since the zoom type is not specific to a certain axis,
// it is impossible to determine if maybe _this_ axis
// should suspend auto-zoom.
// -> Just to the auto-zoom, don't second-guess.
// Above sounds correct, but it is not a good user experience.
if (!force && xyGraph.getZoomType() != ZoomType.NONE)
return false;
// Get range of data in all traces
final Range range = getTraceDataRange();
if (range == null)
return false;
double tempMin = range.getLower();
double tempMax = range.getUpper();
// Get current axis range, determine how 'different' they are
double max = getRange().getUpper();
double min = getRange().getLower();
if (isLogScaleEnabled()) {
if (tempMax <= 0) {
tempMax = 1;
}
if (tempMin <= 0) {
tempMin = 0.1;
}
// Transition into log space
tempMin = Log10.log10(tempMin);
tempMax = Log10.log10(tempMax);
max = Log10.log10(max);
min = Log10.log10(min);
}
double thr = (tempMax - tempMin) * autoScaleThreshold;
if (tempMax == tempMin) {
if (tempMax == 0) {
thr = autoScaleThreshold;
} else {
thr = Math.abs(tempMax) * autoScaleThreshold;
}
}
// if both the changes are lower than threshold, return
if (((tempMin - min) >= thr / 2 && (tempMin - min) <= thr)
&& ((max - tempMax) >= thr / 2 && (max - tempMax) <= thr)) {
return false;
} else { // expand more space than needed
if ((tempMin - min) <= thr / 2 || (tempMin - min) > thr)
tempMin -= thr;
else
tempMin = min;
if ((max - tempMax) <= thr / 2 || (max - tempMax) > thr)
tempMax += thr;
else
tempMax = max;
}
// Any change at all?
if ((Double.doubleToLongBits(tempMin) == Double.doubleToLongBits(min)
&& Double.doubleToLongBits(tempMax) == Double.doubleToLongBits(max)) || Double.isInfinite(tempMin)
|| Double.isInfinite(tempMax) || Double.isNaN(tempMin) || Double.isNaN(tempMax))
return false;
if (isLogScaleEnabled()) { // Revert from log space
tempMin = Log10.pow10(tempMin);
tempMax = Log10.pow10(tempMax);
}
// Update axis
setRange(tempMin, tempMax, true);
repaint();
return true;
}
/**
* Add a trace to the axis.
*
* @param trace
* the trace to be added.
*/
public void addTrace(final Trace trace) {
if (traceList.contains(trace))
return;
traceList.add(trace);
addListener(trace);
performAutoScale(false);
}
/**
* Remove a trace from the axis.
*
* @param trace
* @return true if this axis contained the specified trace
*/
public boolean removeTrace(final Trace trace) {
final boolean r = traceList.remove(trace);
removeListener(trace);
performAutoScale(false);
return r;
}
/**
* @param title
* the title to set
*/
public void setTitle(final String title) {
String oldTitle = this.title;
this.title = title;
if (xyGraph != null)
xyGraph.repaint();
fireAxisTitleChanged(oldTitle, title);
}
private void fireAxisTitleChanged(String oldTitle, String newTitle) {
for (IAxisListener listener : listeners)
listener.axisTitleChanged(this, oldTitle, newTitle);
}
/**
* @return the title
*/
public String getTitle() {
return title;
}
/**
* @return the autoScale
*/
public boolean isAutoScale() {
return autoScale;
}
/**
* @param autoScale
* the autoScale to set
*/
public void setAutoScale(final boolean autoScale) {
boolean oldAutoScale = this.autoScale;
this.autoScale = autoScale;
performAutoScale(false);
fireAxisAutoScaleChanged(oldAutoScale, this.autoScale);
}
private void fireAxisAutoScaleChanged(boolean oldAutoScale, boolean newAutoScale) {
for (IAxisListener listener : listeners)
listener.axisAutoScaleChanged(this, oldAutoScale, newAutoScale);
}
/**
* @return the showMajorGrid
*/
public boolean isShowMajorGrid() {
return showMajorGrid;
}
/**
* @param showMajorGrid
* the showMajorGrid to set
*/
public void setShowMajorGrid(final boolean showMajorGrid) {
this.showMajorGrid = showMajorGrid;
if (xyGraph != null)
xyGraph.repaint();
}
/**
* @return the showMinorGrid
*/
public boolean isShowMinorGrid() {
return showMinorGrid;
}
/**
* @param showMinorGrid
* the showMinorGrid to set
*/
public void setShowMinorGrid(final boolean showMinorGrid) {
this.showMinorGrid = showMinorGrid;
if (xyGraph != null)
xyGraph.repaint();
}
/**
* @return the majorGridColor
*/
public Color getMajorGridColor() {
if (majorGridColor == null)
majorGridColor = XYGraphMediaFactory.getInstance().getColor(XYGraphMediaFactory.COLOR_GRAY);
return majorGridColor;
}
/**
* @param majorGridColor
* the majorGridColor to set
*/
public void setMajorGridColor(final Color majorGridColor) {
this.majorGridColor = majorGridColor;
this.majorGridColorRGB = majorGridColor.getRGB();
if (xyGraph != null)
xyGraph.repaint();
}
public RGB getMajorGridColorRGB() {
return majorGridColorRGB;
}
/**
* @return the minorGridColor
*/
public Color getMinorGridColor() {
if (minorGridColor == null)
minorGridColor = XYGraphMediaFactory.getInstance().getColor(XYGraphMediaFactory.COLOR_GRAY);
return minorGridColor;
}
/**
* @param minorGridColor
* the minorGridColor to set
*/
public void setMinorGridColor(final Color minorGridColor) {
this.minorGridColor = minorGridColor;
if (xyGraph != null)
xyGraph.repaint();
}
/**
* @param titleFont
* the titleFont to set
*/
public void setTitleFont(final Font titleFont) {
this.titleFont = titleFont;
this.titleFontData = titleFont.getFontData()[0];
repaint();
}
/**
* @return the dashGridLine
*/
public boolean isDashGridLine() {
return dashGridLine;
}
/**
* @param dashGridLine
* the dashGridLine to set
*/
public void setDashGridLine(final boolean dashGridLine) {
this.dashGridLine = dashGridLine;
if (xyGraph != null)
xyGraph.repaint();
}
/**
* @param xyGraph
* the xyGraph to set
*/
public void setXyGraph(final IXYGraph xyGraph) {
this.xyGraph = xyGraph;
}
/**
* @return xyGraph
*/
public IXYGraph getXyGraph() {
return xyGraph;
}
/**
* Use {@link #setXyGraph(IXYGraph)} instead
*
* @param xyGraph
* the xyGraph to set
*/
@Deprecated
public void setXyGraph(final XYGraph xyGraph) {
this.xyGraph = xyGraph;
}
/**
* @return traceList
*/
protected List<Trace> getTraceList() {
return traceList;
}
@Override
public String toString() {
return title;
}
public void dataChanged(final IDataProvider dataProvider) {
if (autoScale)
performAutoScale(false);
}
/**
* The autoScaleThreshold must be a value in range [0,1], which represents a
* percentage of the plot area for the threshold when autoScale is
* performed.The autoScale will performed only if the spare space exceeds
* this threshold. So it can reduce the CPU usage by increasing the
* threshold.
*
* @param autoScaleThreshold
* the autoScaleThreshold to set
*/
public void setAutoScaleThreshold(final double autoScaleThreshold) {
if (autoScaleThreshold > 1 || autoScaleThreshold < 0)
throw new RuntimeException("The autoScaleThreshold must be a value in range [0,1]!"); //$NON-NLS-1$
this.autoScaleThreshold = autoScaleThreshold;
}
/**
* @param zoom
* Zoom Type
* @return <code>true</code> if the zoom type is applicable to this axis
*/
private boolean isValidZoomType(final ZoomType zoom) {
return zoom == ZoomType.PANNING || zoom == ZoomType.RUBBERBAND_ZOOM || zoom == ZoomType.DYNAMIC_ZOOM
|| zoom == ZoomType.ZOOM_IN || zoom == ZoomType.ZOOM_OUT
|| (isHorizontal() && (zoom == ZoomType.HORIZONTAL_ZOOM || zoom == ZoomType.ZOOM_IN_HORIZONTALLY
|| zoom == ZoomType.ZOOM_OUT_HORIZONTALLY))
|| (!isHorizontal() && (zoom == ZoomType.VERTICAL_ZOOM || zoom == ZoomType.ZOOM_OUT_VERTICALLY
|| zoom == ZoomType.ZOOM_IN_VERTICALLY));
}
/**
* @param zoomType
* the zoomType to set
*/
public void setZoomType(final ZoomType zoomType) {
this.zoomType = zoomType;
// Set zoom's cursor if axis allows that type of zoom
if (isValidZoomType(zoomType))
setCursor(zoomType.getCursorOnAxis(isHorizontal()));
else
setCursor(ZoomType.NONE.getCursor());
}
/**
* @return the titleFont
*/
public Font getTitleFont() {
return titleFont;
}
/**
* @return the autoScaleThreshold
*/
public double getAutoScaleThreshold() {
return autoScaleThreshold;
}
/**
* @return autoScale
*/
public boolean getAutoScale() {
return autoScale;
}
/**
* Set this axis as Y-Axis or X-Axis.
*
* @param isYAxis
* set true if the axis is Y-Axis; false if it is X-Axis.
*/
public void setYAxis(boolean isYAxis) {
if (xyGraph != null)
xyGraph.removeAxis(this);
setOrientation(isYAxis ? Orientation.VERTICAL : Orientation.HORIZONTAL);
if (xyGraph != null)
xyGraph.addAxis(this);
}
/**
* Set the axis on primary side (Bottom/Left) or secondary side (Top/Right).
*
* @param onPrimarySide
* set true if the axis on primary side(Bottom/Left); false if it
* is not on the primary side of xy graph(Top/Right).
*/
public void setPrimarySide(boolean onPrimarySide) {
setTickLabelSide(onPrimarySide ? LabelSide.Primary : LabelSide.Secondary);
}
/**
* @return true if the axis is Y-Axis; false if it is X-Axis;
*/
public boolean isYAxis() {
return !isHorizontal();
}
/**
* @return true if the axis is on the primary side of xy graph(Bottom/Left);
* false if it is on the secondary side(Top/Right).
*/
public boolean isOnPrimarySide() {
return getTickLabelSide() == LabelSide.Primary;
}
/** Pan axis according to start/end from mouse listener */
private void pan() {
if (isHorizontal())
pan(startRange, getPositionValue(start.x, false), getPositionValue(end.x, false));
else
pan(startRange, getPositionValue(start.y, false), getPositionValue(end.y, false));
}
/**
* Pan the axis
*
* @param temp
* Original axis range before the panning started
* @param t1
* Start of the panning move
* @param t2
* End of the panning move
*/
protected void pan(final Range temp, double t1, double t2) {
if (isLogScaleEnabled()) {
final double m = Math.log10(t2) - Math.log10(t1);
t1 = Math.pow(10, Math.log10(temp.getLower()) - m);
t2 = Math.pow(10, Math.log10(temp.getUpper()) - m);
} else {
final double m = t2 - t1;
t1 = temp.getLower() - m;
t2 = temp.getUpper() - m;
}
setRange(t1, t2);
}
/**
* Zoom axis
*
* @param center
* Axis position at the 'center' of the zoom
* @param factor
* Zoom factor. Positive to zoom 'in', negative 'out'.
*/
public void zoomInOut(final double center, final double factor) {
final double t1, t2;
if (isLogScaleEnabled()) {
final double l = Math.log10(getRange().getUpper()) - Math.log10(getRange().getLower());
final double r1 = (Math.log10(center) - Math.log10(getRange().getLower())) / l;
final double r2 = (Math.log10(getRange().getUpper()) - Math.log10(center)) / l;
t1 = Math.pow(10, Math.log10(getRange().getLower()) + r1 * factor * l);
t2 = Math.pow(10, Math.log10(getRange().getUpper()) - r2 * factor * l);
} else {
final double l = getRange().getUpper() - getRange().getLower();
final double r1 = (center - getRange().getLower()) / l;
final double r2 = (getRange().getUpper() - center) / l;
t1 = getRange().getLower() + r1 * factor * l;
t2 = getRange().getUpper() - r2 * factor * l;
}
setRange(t1, t2, true);
}
/**
* @param grid
* the grid to set
*/
public void setGrid(Grid grid) {
this.grid = grid;
}
/**
* @return the grid
*/
public Grid getGrid() {
return grid;
}
@Override
public void setLogScale(boolean enabled) throws IllegalStateException {
boolean old = isLogScaleEnabled();
super.setLogScale(enabled);
fireAxisLogScaleChanged(old, logScaleEnabled);
}
private void fireAxisLogScaleChanged(boolean old, boolean logScale) {
if (old == logScale)
return;
for (IAxisListener listener : listeners)
listener.axisLogScaleChanged(this, old, logScale);
}
/**
* Field used to remember the previous zoom type used
*/
private ZoomType previousZoomType = ZoomType.NONE;
/**
* Listener to mouse events, performs panning and some zooms Is very similar
* to the PlotMouseListener, but unclear how easy/useful it would be to base
* them on the same code.
*/
class AxisMouseListener extends MouseMotionListener.Stub implements MouseListener {
private SaveStateCommand command;
@Override
public void mousePressed(final MouseEvent me) {
// Only react to 'main' mouse button, only react to 'real' zoom
if ((me.button != PlotArea.BUTTON1 || !isValidZoomType(zoomType)) && me.button != PlotArea.BUTTON2)
return;
// Remember last used zoomtype
previousZoomType = zoomType;
// if mousewheel is pressed
if (me.button == PlotArea.BUTTON2) {
zoomType = ZoomType.PANNING;
}
armed = true;
// get start position
switch (zoomType) {
case RUBBERBAND_ZOOM:
case DYNAMIC_ZOOM:
if (isHorizontal())
start = new Point(me.getLocation().x, bounds.y);
else
start = new Point(bounds.x, me.getLocation().y);
end = null;
break;
case HORIZONTAL_ZOOM:
start = new Point(me.getLocation().x, bounds.y);
end = null;
break;
case VERTICAL_ZOOM:
start = new Point(bounds.x, me.getLocation().y);
end = null;
break;
case PANNING:
setCursor(grabbing);
start = me.getLocation();
end = null;
startRange = getRange();
break;
case ZOOM_IN:
case ZOOM_IN_HORIZONTALLY:
case ZOOM_IN_VERTICALLY:
case ZOOM_OUT:
case ZOOM_OUT_HORIZONTALLY:
case ZOOM_OUT_VERTICALLY:
start = me.getLocation();
end = new Point();
// Start timer that will zoom while mouse button is pressed
Display.getCurrent().timerExec(ZOOM_SPEED, new Runnable() {
@Override
public void run() {
if (!armed)
return;
performInOutZoom();
Display.getCurrent().timerExec(ZOOM_SPEED, this);
}
});
break;
default:
break;
}
// add command for undo operation
command = new AxisPanOrZoomCommand(zoomType.getDescription(), Axis.this);
me.consume();
}
@Override
public void mouseDoubleClicked(final MouseEvent me) { /* Ignored */
}
@Override
public void mouseDragged(final MouseEvent me) {
if (!armed)
return;
switch (zoomType) {
case RUBBERBAND_ZOOM:
case DYNAMIC_ZOOM:
// Treat rubberband zoom on axis like horiz/vert. zoom
if (isHorizontal())
end = new Point(me.getLocation().x, bounds.y + bounds.height);
else
end = new Point(bounds.x + bounds.width, me.getLocation().y);
break;
case HORIZONTAL_ZOOM:
end = new Point(me.getLocation().x, bounds.y + bounds.height);
break;
case VERTICAL_ZOOM:
end = new Point(bounds.x + bounds.width, me.getLocation().y);
break;
case PANNING:
end = me.getLocation();
pan();
break;
default:
break;
}
Axis.this.repaint();
}
@Override
public void mouseExited(final MouseEvent me) {
// Treat like releasing the button to stop zoomIn/Out timer
switch (zoomType) {
case ZOOM_IN:
case ZOOM_IN_HORIZONTALLY:
case ZOOM_IN_VERTICALLY:
case ZOOM_OUT:
case ZOOM_OUT_HORIZONTALLY:
case ZOOM_OUT_VERTICALLY:
mouseReleased(me);
default:
}
}
@Override
public void mouseReleased(final MouseEvent me) {
if (!armed)
return;
armed = false;
if (zoomType == ZoomType.PANNING)
setCursor(zoomType.getCursorOnAxis(isHorizontal()));
if (end == null || start == null || command == null)
return;
switch (zoomType) {
case RUBBERBAND_ZOOM:
case DYNAMIC_ZOOM:
case HORIZONTAL_ZOOM:
case VERTICAL_ZOOM:
performStartEndZoom();
break;
case PANNING:
pan();
break;
case ZOOM_IN:
case ZOOM_IN_HORIZONTALLY:
case ZOOM_IN_VERTICALLY:
case ZOOM_OUT:
case ZOOM_OUT_HORIZONTALLY:
case ZOOM_OUT_VERTICALLY:
performInOutZoom();
break;
default:
break;
}
// mousewheel is pressed and last zoom type was not panning, we set
// the zoomtype to the previous state.
if (me.button == PlotArea.BUTTON2 && previousZoomType != ZoomType.PANNING) {
setZoomType(previousZoomType);
}
command.saveState();
xyGraph.getOperationsManager().addCommand(command);
command = null;
start = null;
end = null;
}
/** Perform the zoom to mouse start/end */
private void performStartEndZoom() {
final double t1 = getPositionValue(isHorizontal() ? start.x : start.y, false);
final double t2 = getPositionValue(isHorizontal() ? end.x : end.y, false);
// Properly set the range given the values of t1 and t2: if the
// maximum value of the range is smaller than the minimum,
// accordingly set the min and max given the values of t1 and t2 and
// vice versa.
boolean isMinBigger = getRange().isMinBigger();
if (isMinBigger == (t1 > t2)) {
setRange(t1, t2);
} else {
setRange(t2, t1);
}
}
/** Perform the in or out zoom according to zoomType */
private void performInOutZoom() {
final int pixel_pos = isHorizontal() ? start.x : start.y;
final double center = getPositionValue(pixel_pos, false);
switch (zoomType) {
case ZOOM_IN:
zoomInOut(center, ZOOM_RATIO);
break;
case ZOOM_IN_HORIZONTALLY:
zoomInOut(center, ZOOM_RATIO);
break;
case ZOOM_IN_VERTICALLY:
zoomInOut(center, ZOOM_RATIO);
break;
case ZOOM_OUT:
zoomInOut(center, -ZOOM_RATIO);
break;
case ZOOM_OUT_HORIZONTALLY:
zoomInOut(center, -ZOOM_RATIO);
break;
case ZOOM_OUT_VERTICALLY:
zoomInOut(center, -ZOOM_RATIO);
break;
default: // NOP
}
}
}
}