/******************************************************************************* * Copyright (c) 2010, 2013 Oak Ridge National Laboratory and others * 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 * * Contributors: * Xihui Chen - initial API and implementation * Torkild U. Resheim - Expose fields and methods for subclassing bug 421670 ******************************************************************************/ package org.eclipse.nebula.visualization.xygraph.figures; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.draw2d.Cursors; import org.eclipse.draw2d.Figure; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.Label; import org.eclipse.draw2d.MouseEvent; import org.eclipse.draw2d.MouseListener; import org.eclipse.draw2d.MouseMotionListener; import org.eclipse.draw2d.Polyline; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.PointList; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.nebula.visualization.internal.xygraph.undo.MovingAnnotationCommand; import org.eclipse.nebula.visualization.internal.xygraph.undo.MovingAnnotationLabelCommand; import org.eclipse.nebula.visualization.xygraph.dataprovider.IDataProvider; import org.eclipse.nebula.visualization.xygraph.dataprovider.IDataProviderListener; import org.eclipse.nebula.visualization.xygraph.dataprovider.ISample; import org.eclipse.nebula.visualization.xygraph.linearscale.Range; import org.eclipse.nebula.visualization.xygraph.util.Preferences; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; /** * Annotation Figure. Annotation could be used to indicate the information for a * particular point. * * @author Xihui Chen * @author Torkild U. Resheim */ public class Annotation extends Figure implements IAxisListener, IDataProviderListener { /** The way how the cursor line will be drawn. */ /** * @author Xihui Chen * */ public enum CursorLineStyle { NONE(0, "None"), UP_DOWN(1, "Up&Down"), LEFT_RIGHT(2, "Left&Right"), FOUR_DIRECTIONS(3, "Four Directions"); private CursorLineStyle(int index, String description) { this.index = index; this.description = description; } private int index; private String description; @Override public String toString() { return description; } public static String[] stringValues() { String[] sv = new String[values().length]; int i = 0; for (CursorLineStyle p : values()) sv[i++] = p.toString(); return sv; } /** * @return the index */ public int getIndex() { return index; } } protected Axis xAxis; protected Axis yAxis; private String name; private FontData fontData; private CursorLineStyle cursorLineStyle = CursorLineStyle.NONE; private Point currentPosition; protected double xValue; protected double yValue; protected Trace trace; protected ISample currentSnappedSample; private boolean showName = true; private boolean showSampleInfo = true; private boolean showPosition = true; private Color annotationColor = null; private RGB annotationColorRGB = null; private Label infoLabel; // label's relative position to currentPosition private double dx = 40; private double dy = -40; // label's relative center position to currentPosition private double x0, y0; private boolean knowX0Y0 = false; private boolean infoLabelArmed = false; private Pointer pointer; private Polyline vLine, hLine; private IXYGraph xyGraph; private final static int POINT_SIZE = 6; private final static int CURSOR_LINE_LENGTH = 3; private final static int ARROW_LINE_LENGTH = 12; protected boolean pointerDragged; private List<IAnnotationListener> listeners; /** * Construct an annotation on a trace. * * @param name * the name of the annotation. * @param trace * the trace which the annotation will snap to. */ public Annotation(String name, Trace trace) { this(name, trace.getXAxis(), trace.getYAxis()); this.trace = trace; trace.getDataProvider().addDataProviderListener(this); } /** * Construct a free annotation. * * @param xAxis * the xAxis of the annotation. * @param yAxis * the yAxis of the annotation. * @param name * the name of the annotation. */ public Annotation(String name, Axis xAxis, Axis yAxis) { this.xAxis = xAxis; this.yAxis = yAxis; this.name = name; trace = null; infoLabel = new Label(); infoLabel.setOpaque(false); infoLabel.setCursor(Cursors.SIZEALL); add(infoLabel); InfoLabelDragger infoLabelDragger = new InfoLabelDragger(); infoLabel.addMouseMotionListener(infoLabelDragger); infoLabel.addMouseListener(infoLabelDragger); pointer = new Pointer(); add(pointer); updateToDefaultPosition(); xAxis.addListener(this); yAxis.addListener(this); } public synchronized void addAnnotationListener(IAnnotationListener listener) { if (listeners == null) listeners = new CopyOnWriteArrayList<IAnnotationListener>(); listeners.add(listener); } private void fireAnnotationMoved(double oldX, double oldY, double newX, double newY) { if (listeners == null) return; for (IAnnotationListener listener : listeners) { listener.annotationMoved(oldX, oldY, newX, newY); } } @Override public boolean containsPoint(int x, int y) { return infoLabel.containsPoint(x, y) || pointer.containsPoint(x, y) || (vLine != null ? vLine.containsPoint(x, y) : false) || (hLine != null ? hLine.containsPoint(x, y) : false); } @Override protected void layout() { super.layout(); if (trace != null && currentSnappedSample == null && !pointerDragged) updateToDefaultPosition(); Dimension size = infoLabel.getPreferredSize(); updateX0Y0Fromdxdy(size); Rectangle infoBounds = new Rectangle((int) (currentPosition.x + x0 - size.width / 2.0), (int) (currentPosition.y + y0 - size.height / 2.0), size.width, size.height); infoLabel.setBounds(infoBounds); pointer.setBounds(new Rectangle(currentPosition.x - POINT_SIZE, currentPosition.y - POINT_SIZE, 2 * POINT_SIZE, 2 * POINT_SIZE)); // layout Cursor Line switch (cursorLineStyle) { case NONE: break; case FOUR_DIRECTIONS: case LEFT_RIGHT: hLine.setPoints(new PointList( new int[] { xAxis.getValuePosition(xAxis.getRange().getLower(), false), currentPosition.y, xAxis.getValuePosition(xAxis.getRange().getUpper(), false), currentPosition.y })); if (cursorLineStyle != CursorLineStyle.FOUR_DIRECTIONS) break; case UP_DOWN: vLine.setPoints(new PointList( new int[] { currentPosition.x, yAxis.getValuePosition(yAxis.getRange().getUpper(), false), currentPosition.x, yAxis.getValuePosition(yAxis.getRange().getLower(), false) })); break; default: break; } } @Override protected void paintFigure(Graphics graphics) { super.paintFigure(graphics); if (trace != null && currentSnappedSample == null && !pointerDragged) updateToDefaultPosition(); if (Preferences.useAdvancedGraphics()) graphics.setAntialias(SWT.ON); Color tempColor; if (annotationColor == null) { tempColor = yAxis.getForegroundColor(); } else tempColor = annotationColor; infoLabel.setForegroundColor(tempColor); pointer.setForegroundColor(tempColor); if (vLine != null) vLine.setForegroundColor(tempColor); if (hLine != null) hLine.setForegroundColor(tempColor); graphics.setForegroundColor(tempColor); if (infoLabelArmed) // draw infoLabel Armed rect graphics.drawRectangle(infoLabel.getBounds()); if (showName || showPosition || showSampleInfo) { // draw indicate line graphics.drawLine(currentPosition.x + (int) dx, currentPosition.y + (int) dy, currentPosition.x, currentPosition.y); // draw Arrow int x1 = (int) (ARROW_LINE_LENGTH * Math.cos(Math.atan(-dy / dx) - Math.PI / 9)); int y1 = (int) (ARROW_LINE_LENGTH * Math.sin(Math.atan(-dy / dx) - Math.PI / 9)); if (dx < 0) { x1 = -x1; y1 = -y1; } graphics.drawLine(currentPosition.x + x1, currentPosition.y - y1, currentPosition.x, currentPosition.y); x1 = (int) (ARROW_LINE_LENGTH * Math.cos(Math.atan(-dy / dx) + Math.PI / 9)); y1 = (int) (ARROW_LINE_LENGTH * Math.sin(Math.atan(-dy / dx) + Math.PI / 9)); if (dx < 0) { x1 = -x1; y1 = -y1; } graphics.drawLine(currentPosition.x + x1, currentPosition.y - y1, currentPosition.x, currentPosition.y); } // draw Cursor Line switch (cursorLineStyle) { case NONE: // left graphics.drawLine(currentPosition.x - POINT_SIZE / 2, currentPosition.y, currentPosition.x - POINT_SIZE / 2 - CURSOR_LINE_LENGTH, currentPosition.y); // right graphics.drawLine(currentPosition.x + POINT_SIZE / 2, currentPosition.y, currentPosition.x + POINT_SIZE / 2 + CURSOR_LINE_LENGTH, currentPosition.y); // up graphics.drawLine(currentPosition.x, currentPosition.y - POINT_SIZE / 2, currentPosition.x, currentPosition.y - POINT_SIZE / 2 - CURSOR_LINE_LENGTH); // down graphics.drawLine(currentPosition.x, currentPosition.y + POINT_SIZE / 2, currentPosition.x, currentPosition.y + POINT_SIZE / 2 + CURSOR_LINE_LENGTH); break; default: break; } } /** * update (x0, y0) if it is unknown. * * @param size * the label size * @return */ public void updateX0Y0Fromdxdy(Dimension size) { if (!knowX0Y0) { knowX0Y0 = true; int h = size.height; int w = size.width; if (dy != 0) { // assume this is the intersection y0 = dy - h / 2.0; x0 = (dx / dy) * y0; if (new Range(0, x0).inRange(dx) && new Range(0, y0).inRange(dy) && new Range(x0 - w / 2.0, x0 + w / 2.0).inRange(dx)) return; y0 = dy + h / 2.0; x0 = (dx / dy) * y0; if (new Range(0, x0).inRange(dx) && new Range(0, y0).inRange(dy) && new Range(x0 - w / 2.0, x0 + w / 2.0).inRange(dx)) return; } else { } if (dx != 0) { x0 = dx + w / 2.0; y0 = (dy / dx) * x0; if (new Range(0, x0).inRange(dx) && new Range(0, y0).inRange(dy) && new Range(y0 - h / 2.0, y0 + h / 2.0).inRange(dy)) return; x0 = dx - w / 2.0; y0 = (dy / dx) * x0; if (new Range(0, x0).inRange(dx) && new Range(0, y0).inRange(dy) && new Range(y0 - h / 2.0, y0 + h / 2.0).inRange(dy)) return; } } else return; } /** * update (dx, dy) if (x0, y0) has been updated by dragging. * * @param size * the label size * @return */ private void updatedxdyFromX0Y0() { Dimension size = infoLabel.getPreferredSize(); int h = size.height; int w = size.width; if (y0 != 0) { dy = y0 + h / 2.0; dx = x0 * dy / y0; if (new Range(0, x0).inRange(dx) && new Range(0, y0).inRange(dy) && new Range(x0 - w / 2.0, x0 + w / 2.0).inRange(dx)) return; dy = y0 - h / 2.0; dx = x0 * dy / y0; if (new Range(0, x0).inRange(dx) && new Range(0, y0).inRange(dy) && new Range(x0 - w / 2.0, x0 + w / 2.0).inRange(dx)) return; } else dy = 0; if (x0 != 0) { dx = x0 - size.width / 2.0; dy = y0 * dx / x0; if (new Range(0, x0).inRange(dx) && new Range(0, y0).inRange(dy) && new Range(y0 - h / 2.0, y0 + h / 2.0).inRange(dy)) return; dx = x0 + size.width / 2.0; dy = y0 * dx / x0; if (new Range(0, x0).inRange(dx) && new Range(0, y0).inRange(dy) && new Range(y0 - h / 2.0, y0 + h / 2.0).inRange(dy)) return; } else dx = 0; } /** * move the annotation to the center of the plot area or trace. */ protected void updateToDefaultPosition() { double oldX = xValue; double oldY = yValue; if (trace != null && trace.getHotSampleList().size() > 0) { currentSnappedSample = trace.getHotSampleList().get(trace.getHotSampleList().size() / 2); currentPosition = new Point(xAxis.getValuePosition(currentSnappedSample.getXValue(), false), yAxis.getValuePosition(currentSnappedSample.getYValue(), false)); xValue = currentSnappedSample.getXValue(); yValue = currentSnappedSample.getYValue(); } else { currentSnappedSample = null; if (xAxis.isLogScaleEnabled()) xValue = Math.pow(10, (Math.log10(xAxis.getRange().getLower()) + Math.log10(xAxis.getRange().getUpper())) / 2); else xValue = (xAxis.getRange().getLower() + xAxis.getRange().getUpper()) / 2; if (yAxis.isLogScaleEnabled()) yValue = Math.pow(10, (Math.log10(yAxis.getRange().getLower()) + Math.log10(yAxis.getRange().getUpper())) / 2); else yValue = (yAxis.getRange().getLower() + yAxis.getRange().getUpper()) / 2; currentPosition = new Point(xAxis.getValuePosition(xValue, false), yAxis.getValuePosition(yValue, false)); } updateInfoLabelText(true); if (xValue != oldX || yValue != oldY) revalidate(); fireAnnotationMoved(oldX, oldY, xValue, yValue); } /** * Set the position of the annotation based on plot values * * @param x * Position as value on the X axis * @param y * Position as value on the Y axis * @see #setCurrentPosition(Point, boolean) for setting the position based * on screen coordinates */ public void setValues(final double x, final double y) { double oldX = xValue; double oldY = yValue; xValue = x; yValue = y; currentPosition = new Point(xAxis.getValuePosition(xValue, false), yAxis.getValuePosition(yValue, false)); updateInfoLabelText(true); revalidate(); if (oldX != xValue || oldY != yValue) fireAnnotationMoved(oldX, oldY, xValue, yValue); } /** * */ private void updateInfoLabelText(boolean updateX0Y0) { String info = ""; if (showName) info = name; if (showSampleInfo && currentSnappedSample != null && !currentSnappedSample.getInfo().equals("")) info += "\n" + currentSnappedSample.getInfo(); if (showPosition) info += "\n" + "(" + xAxis.format(xValue) + ", " + (Double.isNaN(yValue) ? "NaN" : yAxis.format(yValue)) + ")"; infoLabel.setText(info); knowX0Y0 = !updateX0Y0; } private void updateInfoLabelText() { updateInfoLabelText(true); } /** * @param axis * the xAxis to set */ public void setXAxis(Axis axis) { if (this.xAxis == axis) return; xAxis = axis; updateToDefaultPosition(); revalidate(); repaint(); } /** * @param axis * the yAxis to set */ public void setYAxis(Axis axis) { if (this.yAxis == axis) return; yAxis = axis; updateToDefaultPosition(); revalidate(); repaint(); } /** * @param name * the name to set */ public void setName(String name) { this.name = name; updateInfoLabelText(); } @Override public void setFont(Font f) { super.setFont(f); if (f != null) this.fontData = getFont().getFontData()[0]; } public FontData getFontData() { return fontData; } /** * @param trace * the trace to set */ public void setTrace(Trace trace) { if (this.trace == trace) return; this.xAxis = trace.getXAxis(); this.yAxis = trace.getYAxis(); if (!isFree() && this.trace != trace) this.trace.getDataProvider().removeDataProviderListener(this); if (isFree() || this.trace != trace) { this.trace = trace; updateToDefaultPosition(); } this.trace = trace; trace.getDataProvider().addDataProviderListener(this); repaint(); } /** * Make the annotation free. * * @param xAxis * @param yAxis */ public void setFree(Axis xAxis, Axis yAxis) { if (trace != null) { trace.getDataProvider().removeDataProviderListener(this); trace = null; updateToDefaultPosition(); } setXAxis(xAxis); setYAxis(yAxis); revalidate(); repaint(); } /** * @return true if the annotation is free. */ public boolean isFree() { return trace == null; } /** * @param showName * the showName to set */ public void setShowName(boolean showName) { this.showName = showName; updateInfoLabelText(); } /** * @param showSampleInfo * the showSampleInfo to set */ public void setShowSampleInfo(boolean showSampleInfo) { this.showSampleInfo = showSampleInfo; updateInfoLabelText(); } /** * @param showPosition * the showPosition to set */ public void setShowPosition(boolean showPosition) { this.showPosition = showPosition; updateInfoLabelText(); } /** * @param annotationColor * the annotationColor to set */ public void setAnnotationColor(Color annotationColor) { this.annotationColor = annotationColor; if (annotationColor != null) this.annotationColorRGB = annotationColor.getRGB(); infoLabel.setForegroundColor(annotationColor); pointer.setForegroundColor(annotationColor); } /** * @param annotationFont * the annotationFont to set */ public void setAnnotationFont(Font annotationFont) { infoLabel.setFont(annotationFont); } /** * @param cursorLineStyle * the cursorLineStyle to set */ public void setCursorLineStyle(CursorLineStyle cursorLineStyle) { this.cursorLineStyle = cursorLineStyle; if (cursorLineStyle == CursorLineStyle.UP_DOWN || cursorLineStyle == CursorLineStyle.FOUR_DIRECTIONS) { if (vLine == null) { vLine = new Polyline(); vLine.setCursor(Cursors.SIZEWE); AnnotationDragger annotationDragger = new AnnotationDragger(true, false); vLine.addMouseListener(annotationDragger); vLine.addMouseMotionListener(annotationDragger); vLine.setTolerance(3); add(vLine, 0); } } if (cursorLineStyle == CursorLineStyle.LEFT_RIGHT || cursorLineStyle == CursorLineStyle.FOUR_DIRECTIONS) { if (hLine == null) { hLine = new Polyline(); hLine.setCursor(Cursors.SIZENS); AnnotationDragger annotationDragger = new AnnotationDragger(false, true); hLine.addMouseListener(annotationDragger); hLine.addMouseMotionListener(annotationDragger); hLine.setTolerance(3); add(hLine, 0); } } if (cursorLineStyle == CursorLineStyle.UP_DOWN) { if (hLine != null) { remove(hLine); hLine = null; } } if (cursorLineStyle == CursorLineStyle.LEFT_RIGHT) { if (vLine != null) { remove(vLine); vLine = null; } } } /** * @param currentPosition * the currentPosition to set */ public void setCurrentPosition(Point currentPosition, boolean keepLabelPosition, boolean calcValueFromPosition) { if (keepLabelPosition) { int deltaX = this.currentPosition.x - currentPosition.x; int deltaY = this.currentPosition.y - currentPosition.y; x0 += deltaX; y0 += deltaY; knowX0Y0 = true; updatedxdyFromX0Y0(); } this.currentPosition = currentPosition; if (calcValueFromPosition) { double oldX = xValue; double oldY = yValue; xValue = xAxis.getPositionValue(currentPosition.x, false); yValue = yAxis.getPositionValue(currentPosition.y, false); fireAnnotationMoved(oldX, oldY, xValue, yValue); } updateInfoLabelText(keepLabelPosition); revalidate(); repaint(); } public void setCurrentPosition(Point currentPosition, boolean keepLabelPosition) { setCurrentPosition(currentPosition, keepLabelPosition, true); } /** * @param currentSnappedSample * the currentSnappedSample to set * @param keepLabelPosition */ public void setCurrentSnappedSample(ISample currentSnappedSample, boolean keepLabelPosition) { if (!trace.getHotSampleList().contains(currentSnappedSample)) updateToDefaultPosition(); else { this.currentSnappedSample = currentSnappedSample; Point newPosition = new Point(xAxis.getValuePosition(currentSnappedSample.getXValue(), false), yAxis.getValuePosition(currentSnappedSample.getYValue(), false)); double oldX = xValue; double oldY = yValue; xValue = currentSnappedSample.getXValue(); yValue = currentSnappedSample.getYValue(); if (Double.isNaN(currentSnappedSample.getXPlusError())) yValue = Double.NaN; setCurrentPosition(newPosition, keepLabelPosition, false); fireAnnotationMoved(oldX, oldY, xValue, yValue); } repaint(); } public void axisRevalidated(Axis axis) { currentPosition = new Point(xAxis.getValuePosition(xValue, false), yAxis.getValuePosition(yValue, false)); updateInfoLabelText(); if (getParent() != null) layout(); } public void axisRangeChanged(Axis axis, Range old_range, Range new_range) { // do nothing } public void dataChanged(IDataProvider dataProvider) { if (trace == null) return; // run it in next cycle after the trace have been painted. Display.getCurrent().asyncExec(new Runnable() { @Override public void run() { if (trace.getHotSampleList().contains(currentSnappedSample)) { double oldX = xValue; double oldY = yValue; if (yValue != currentSnappedSample.getYValue()) { // When // waveform // index // is // changed, // Y // value // of // the // snapped // sample // is // also // changed. // In // that // case, // the // position // of // this // annotation // must // be // updated // accordingly. yValue = currentSnappedSample.getYValue(); } if (xValue != currentSnappedSample.getXValue()) { xValue = currentSnappedSample.getXValue(); } currentPosition = new Point(xAxis.getValuePosition(xValue, false), yAxis.getValuePosition(yValue, false)); fireAnnotationMoved(oldX, oldY, xValue, yValue); } else if (trace.getHotSampleList().size() > 0) { updateToDefaultPosition(); pointerDragged = false; } } }); } /** * @param xyGraph * the xyGraph to set */ public void setxyGraph(IXYGraph xyGraph) { this.xyGraph = xyGraph; } /** * Use {@link #setxyGraph(IXYGraph)} instead * * @param xyGraph * the xyGraph to set */ public void setxyGraph(XYGraph xyGraph) { this.xyGraph = xyGraph; } public void setdxdy(double dx, double dy) { this.dx = dx; this.dy = dy; knowX0Y0 = false; repaint(); } /** * @return the xAxis */ public Axis getXAxis() { return xAxis; } /** * @return the yAxis */ public Axis getYAxis() { return yAxis; } /** * @return the name */ public String getName() { return name; } /** @return X value, i.e. value of this annotation on the X Axis */ public double getXValue() { return xValue; } /** @return Y value, i.e. value of this annotation on the Y Axis */ public double getYValue() { return yValue; } /** * @return the cursorLineStyle */ public CursorLineStyle getCursorLineStyle() { return cursorLineStyle; } /** * @return the trace */ public Trace getTrace() { return trace; } /** * @return the showName */ public boolean isShowName() { return showName; } /** * @return the showSampleInfo */ public boolean isShowSampleInfo() { return showSampleInfo; } /** * @return the showPosition */ public boolean isShowPosition() { return showPosition; } /** * @return the annotationColor */ public Color getAnnotationColor() { return annotationColor; } class InfoLabelDragger extends MouseMotionListener.Stub implements MouseListener { private MovingAnnotationLabelCommand command; @Override public void mouseDragged(MouseEvent me) { x0 = me.getLocation().x - currentPosition.x; y0 = me.getLocation().y - currentPosition.y; knowX0Y0 = true; updatedxdyFromX0Y0(); Annotation.this.revalidate(); Annotation.this.repaint(); me.consume(); } public void mouseDoubleClicked(MouseEvent me) { } public void mousePressed(MouseEvent me) { command = new MovingAnnotationLabelCommand(Annotation.this); command.setBeforeMovingDxDy(dx, dy); infoLabelArmed = true; Annotation.this.revalidate(); Annotation.this.repaint(); me.consume(); // it must be consumed to make dragging smoothly. } public void mouseReleased(MouseEvent me) { command.setAfterMovingDxDy(dx, dy); xyGraph.getOperationsManager().addCommand(command); infoLabelArmed = false; Annotation.this.revalidate(); Annotation.this.repaint(); me.consume(); } } private class AnnotationDragger extends MouseMotionListener.Stub implements MouseListener { private MovingAnnotationCommand command; private boolean horizontalMoveable; private boolean verticalMoveable; public AnnotationDragger(boolean horizontalMoveable, boolean verticalMoveable) { this.horizontalMoveable = horizontalMoveable; this.verticalMoveable = verticalMoveable; } @Override public void mouseDragged(MouseEvent me) { Point mouseLocation = new Point(horizontalMoveable ? me.getLocation().x : currentPosition.x, verticalMoveable ? me.getLocation().y : currentPosition.y); // free if (trace == null) { setCurrentPosition(mouseLocation, me.getState() == (SWT.BUTTON1 | SWT.CONTROL)); } else { // snap to trace // double tempX = // xAxis.getPositionValue(me.getLocation().x, false); // double tempY = // yAxis.getPositionValue(me.getLocation().y, false); ISample tempSample = null; double minD = Double.POSITIVE_INFINITY; double d; for (ISample s : trace.getHotSampleList()) { d = Math.sqrt(Math.pow(xAxis.getValuePosition(s.getXValue(), false) - mouseLocation.x, 2) + Math.pow(yAxis.getValuePosition(s.getYValue(), false) - mouseLocation.y, 2)); if (minD > d) { minD = d; tempSample = s; } } if (tempSample != null && currentSnappedSample != tempSample) setCurrentSnappedSample(tempSample, me.getState() == (SWT.BUTTON1 | SWT.CONTROL)); else if (tempSample == null) { setCurrentPosition(mouseLocation, me.getState() == (SWT.BUTTON1 | SWT.CONTROL)); pointerDragged = true; } } me.consume(); } public void mouseDoubleClicked(MouseEvent me) { } public void mousePressed(MouseEvent me) { command = new MovingAnnotationCommand(Annotation.this); if (isFree()) command.setBeforeMovePosition(currentPosition); else command.setBeforeMoveSnappedSample(currentSnappedSample); command.setBeforeDxDy(dx, dy); me.consume(); // it must be consumed to make dragging smoothly. } public void mouseReleased(MouseEvent me) { if (command != null) { if (isFree()) command.setAfterMovePosition(currentPosition); else command.setAfterMoveSnappedSample(currentSnappedSample); command.setAfterDxDy(dx, dy); xyGraph.getOperationsManager().addCommand(command); } } } class Pointer extends Figure { public Pointer() { setCursor(Cursors.CROSS); AnnotationDragger dragger = new AnnotationDragger(true, true); addMouseMotionListener(dragger); addMouseListener(dragger); } @Override protected void paintClientArea(Graphics graphics) { super.paintClientArea(graphics); if (Preferences.useAdvancedGraphics()) graphics.setAntialias(SWT.ON); // draw X-cross point Rectangle clientArea = getClientArea().getCopy().shrink(POINT_SIZE / 2, POINT_SIZE / 2); graphics.drawLine(clientArea.x, clientArea.y, clientArea.x + clientArea.width, clientArea.y + clientArea.height); graphics.drawLine(clientArea.x + clientArea.width, clientArea.y, clientArea.x, clientArea.y + clientArea.height); } } public void axisForegroundColorChanged(Axis axis, Color oldColor, Color newColor) { } public void axisTitleChanged(Axis axis, String oldTitle, String newTitle) { } public void axisAutoScaleChanged(Axis axis, boolean oldAutoScale, boolean newAutoScale) { } public void axisLogScaleChanged(Axis axis, boolean old, boolean logScale) { } public RGB getAnnotationColorRGB() { return annotationColorRGB; } }