package plots.views;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
class PlotViewer {
final class PlotMouseMoveListener implements MouseMoveListener {
private int startx = 0;
private boolean dragging = false;
@Override
public void mouseMove(MouseEvent e) {
if (e.stateMask == SWT.BUTTON1) {
// drag
if (!dragging) {
startx = e.x;
dragging = true;
} else {
int diffx = e.x - startx;
if (diffx != 0) {
PlotViewer pv = (PlotViewer) e.widget.getData();
Rectangle r = new Rectangle(pv.plotRect.x + diffx, pv.plotRect.y, pv.plotRect.width, pv.plotRect.height);
pv.modifyPlotRect(r);
}
startx = e.x;
}
} else
dragging = false;
}
}
// UI
public Composite canvas;
public Sash sash;
private Action actionFit;
private Action actionZoomIn;
private Action actionZoomOut;
private Action actionRemove;
// model
public List<IPlottable> plots = new ArrayList<>();
public List<Color> colors = new ArrayList<>();
public Rectangle plotRect = new Rectangle(10, 10, 600, 200);
// others
public Consumer<Rectangle> rectChange = r -> {};
public PlotViewer(Composite parent) {
createCanvas(parent);
makeActions();
hookContextMenu();
}
public PlotViewer onRectChange(Consumer<Rectangle> rc) {
rectChange = rc;
return this;
}
public void addPlot(int[] data) {
plots.add(new Plot(data));
}
public void setPlotRect(Rectangle r) {
if (!plotRect.equals(r)) {
plotRect.x = r.x;
plotRect.y = r.y;
plotRect.width = r.width;
plotRect.height = r.height;
canvas.redraw();
}
}
public void setDomainRange(int start, int end) {
for (IPlottable p : plots) {
p.setDomainRange(start, end);
}
}
private void modifyPlotRect(Rectangle r) {
setPlotRect(r);
rectChange.accept(plotRect);
}
private PlotViewer zoomXAxis(double factor, int x) {
Rectangle r = new Rectangle(plotRect.x, plotRect.y, plotRect.width, plotRect.height);
r.x = x - (int)((x - r.x) * factor);
r.width = (int) (plotRect.width * factor);
modifyPlotRect(r);
return this;
}
PlotViewer zoomIn(int x) {
return zoomXAxis(1.2, x);
}
PlotViewer zoomOut(int x) {
if (plotRect.width < 10)
return this; // do not zoom too much
return zoomXAxis(1/1.2, x);
}
private void fillContextMenu(IMenuManager manager) {
manager.add(actionFit);
manager.add(actionZoomIn);
manager.add(actionZoomOut);
manager.add(actionRemove);
}
private void makeActions() {
actionFit = new Action() {
public void run() {
Point size = canvas.getSize();
Rectangle r = new Rectangle(0, 0, size.x, size.y);
modifyPlotRect(r);
}
};
actionFit.setText("Fit Plot");
actionFit.setToolTipText("Fit the plot to the current size");
actionFit.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
actionZoomIn = new LambdaAction(a -> {zoomIn(0);});
actionZoomIn.setText("><");
actionZoomIn.setToolTipText("Zoom in");
actionZoomOut = new LambdaAction(a -> {zoomOut(0);});
actionZoomOut.setText("<>");
actionZoomOut.setToolTipText("Zoom out");
actionRemove = new LambdaAction(a -> {canvas.dispose();});
actionRemove.setText("Remove");
actionRemove.setToolTipText("Remove this plot");
}
private void createCanvas(Composite parent) {
Composite canvas = new Canvas(parent, SWT.NO_REDRAW_RESIZE | SWT.DOUBLE_BUFFERED | SWT.BORDER);
GridDataFactory.fillDefaults().grab(true, false).hint(SWT.DEFAULT, 100).applyTo(canvas);
canvas.addPaintListener(PlotViewer::paintPlot);
canvas.addMouseMoveListener(new PlotMouseMoveListener());
canvas.setData(this);
this.canvas = canvas;
if (!plots.isEmpty()) {
String toolTip = "";
for (IPlottable p : plots) {
toolTip += "data.len=" + p.getCount() + "\n";
}
canvas.setToolTipText(toolTip );
}
canvas.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
PlotViewer pv = (PlotViewer) e.widget.getData();
// remove associated sash, if present
if (pv.sash != null)
pv.sash.dispose();
}
});
}
private void hookContextMenu() {
MenuManager menuMgr = new MenuManager("#PopupMenu");
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(manager -> {fillContextMenu(manager);});
Menu menu = menuMgr.createContextMenu(canvas);
canvas.setMenu(menu);
}
private static void paintPlot(PaintEvent e) {
PlotViewer pp = (PlotViewer) e.widget.getData();
Color c = e.gc.getForeground();
for (int i = 0; i < pp.plots.size(); i++) {
if (i < pp.colors.size())
e.gc.setForeground(pp.colors.get(i));
else
e.gc.setForeground(c);
pp.plots.get(i).plot(e.gc, pp.plotRect);
}
}
}