package name.abuchen.portfolio.ui.util.chart; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.swtchart.Chart; public abstract class AbstractChartToolTip implements Listener { public static final int PADDING = 5; private Chart chart = null; private Shell tip = null; private Object focus = null; public AbstractChartToolTip(Chart chart) { this.chart = chart; Composite plotArea = chart.getPlotArea(); plotArea.addListener(SWT.MouseDown, this); plotArea.addListener(SWT.MouseMove, this); plotArea.addListener(SWT.MouseUp, this); plotArea.addListener(SWT.Dispose, this); } protected abstract Object getFocusObjectAt(Event event); protected abstract void createComposite(Composite parent); protected Chart getChart() { return chart; } protected Object getFocusedObject() { return focus; } @Override public void handleEvent(Event event) { switch (event.type) { case SWT.Dispose: case SWT.MouseUp: closeToolTip(); break; case SWT.MouseMove: moveToolTip(event); break; case SWT.MouseDown: if (event.button == 1 && event.stateMask != SWT.MOD1) showToolTip(event); break; default: break; } } private void closeToolTip() { if (tip != null) { tip.dispose(); tip = null; } } private Point createAndMeasureTooltip() { createComposite(tip); tip.layout(); return tip.computeSize(SWT.DEFAULT, SWT.DEFAULT); } private void showToolTip(Event event) { focus = getFocusObjectAt(event); if (focus == null) // nothing to show return; if (tip != null && !tip.isDisposed()) tip.dispose(); tip = new Shell(Display.getDefault().getActiveShell(), SWT.ON_TOP | SWT.TOOL); tip.setLayout(new FillLayout()); Point size = createAndMeasureTooltip(); Rectangle bounds = calculateBounds(event, size); tip.setBounds(bounds); tip.setVisible(true); } private void moveToolTip(Event event) { if (tip == null || tip.isDisposed()) return; Object newTipDate = getFocusObjectAt(event); boolean dateChanged = focus != null && !focus.equals(newTipDate); if (dateChanged) { // delete composite for (Control c : tip.getChildren()) c.dispose(); // re-create labels focus = newTipDate; Point size = createAndMeasureTooltip(); size = tip.computeSize(SWT.DEFAULT, SWT.DEFAULT); Rectangle bounds = calculateBounds(event, size); tip.setBounds(bounds); } else { Point size = tip.getSize(); Rectangle bounds = calculateBounds(event, size); tip.setLocation(new Point(bounds.x, bounds.y)); } } private Rectangle calculateBounds(Event event, Point size) { Rectangle plotArea = chart.getPlotArea().getClientArea(); int x = event.x + (size.x / 2) > plotArea.width ? plotArea.width - size.x : event.x - (size.x / 2); x = Math.max(x, 0); int y = event.y + size.y + PADDING > plotArea.height ? event.y - size.y - PADDING : event.y + PADDING; y = Math.max(y, 0); y = Math.min(y, plotArea.height - size.y - PADDING); Point pt = chart.getPlotArea().toDisplay(x, y); return new Rectangle(pt.x, pt.y, size.x, size.y); } }