blob: 735dfad207206524536b5481a0f615cb9313f183 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2015 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Bernd Hufmann - Initial API and implementation
**********************************************************************/
package org.eclipse.tracecompass.tmf.ui.viewers.xycharts;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.tracecompass.tmf.ui.viewers.TmfAbstractToolTipHandler;
import org.swtchart.Chart;
import org.swtchart.IAxis;
import org.swtchart.ISeries;
/**
* Abstract tooltip provider for xy chart viewers. It displays the y value and y
* value of the data point of the mouse position. Extending classes can provide
* a custom tooltip text.
*
* @author Bernd Hufmann
* @since 2.0
*/
public class TmfClosestDataPointTooltipProvider extends TmfBaseProvider implements MouseTrackListener, MouseMoveListener, PaintListener {
private static final @NonNull String OLD_TOOLTIP = ""; //$NON-NLS-1$
private final class XYToolTipHandler extends TmfAbstractToolTipHandler {
@Override
public void fill(Control control, MouseEvent e, Point pt) {
if ((getChartViewer().getWindowDuration() != 0) && (e != null)) {
Chart chart = getChart();
IAxis xAxis = chart.getAxisSet().getXAxis(0);
IAxis yAxis = chart.getAxisSet().getYAxis(0);
ISeries[] series = chart.getSeriesSet().getSeries();
double smallestDistance = Double.MAX_VALUE;
Parameter param = null;
// go over all series
for (int k = 0; k < series.length; k++) {
ISeries serie = series[k];
double[] xS = serie.getXSeries();
double[] yS = serie.getYSeries();
if ((xS == null) || (yS == null)) {
continue;
}
// go over all data points
for (int i = 0; i < xS.length; i++) {
int xs = xAxis.getPixelCoordinate(xS[i]) - e.x;
int ys = yAxis.getPixelCoordinate(yS[i]) - e.y;
double currentDistance = xs * xs + ys * ys;
/*
* Check for smallest distance to mouse position and
* only consider it if the mouse is close the data
* point.
*/
if ((currentDistance < smallestDistance) && (currentDistance < (HIGHLIGHT_RADIUS * HIGHLIGHT_RADIUS))) {
smallestDistance = currentDistance;
fHighlightX = xs + e.x;
fHighlightY = ys + e.y;
if (param == null) {
param = new Parameter();
}
param.setSeriesIndex(k);
param.setDataIndex(i);
}
}
}
Map<String, Map<String, Object>> tooltip = null;
if (param != null) {
tooltip = createToolTipMap(param);
if (tooltip == null) {
return;
}
fIsHighlight = true;
chart.redraw();
}
if (tooltip == null) {
return;
}
/*
* Note that tooltip might be null which will clear the previous
* tooltip string. This is intentional.
*/
for (Entry<String, Map<String, Object>> entry : tooltip.entrySet()) {
ToolTipString category = entry.getKey().isEmpty() || entry.getKey().equals(OLD_TOOLTIP) ? null : ToolTipString.fromString(entry.getKey());
for (Entry<String, Object> secondEntry : entry.getValue().entrySet()) {
Object value = secondEntry.getValue();
String key = secondEntry.getKey();
if (value instanceof Number) {
addItem(category, ToolTipString.fromString(key), ToolTipString.fromDecimal((Number) value));
} else if (value instanceof ITmfTimestamp) {
addItem(category, ToolTipString.fromString(key), ToolTipString.fromTimestamp(String.valueOf(value), ((ITmfTimestamp) value).toNanos()));
} else {
addItem(category, ToolTipString.fromString(key), ToolTipString.fromString(String.valueOf(value)));
}
}
}
}
}
}
// ------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------
private static final int ALPHA = 128;
private static final int HIGHLIGHT_RADIUS = 5;
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
/** X coordinate for highlighting */
private int fHighlightX;
/** y coordinate for highlighting */
private int fHighlightY;
/** Flag to do highlighting or not */
private boolean fIsHighlight;
/** Tooltip handler */
private TmfAbstractToolTipHandler fTooltipHandler = new XYToolTipHandler();
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Constructor for a tool tip provider.
*
* @param tmfChartViewer
* - the parent chart viewer
*/
public TmfClosestDataPointTooltipProvider(ITmfChartTimeProvider tmfChartViewer) {
super(tmfChartViewer);
register();
}
// ------------------------------------------------------------------------
// TmfBaseProvider
// ------------------------------------------------------------------------
@Override
public void register() {
Chart chart = getChart();
chart.getPlotArea().addMouseMoveListener(this);
chart.getPlotArea().addPaintListener(this);
fTooltipHandler.activateHoverHelp(chart.getPlotArea());
}
@Override
public void deregister() {
Chart chart = getChart();
if ((chart != null) && !chart.isDisposed()) {
chart.getPlotArea().removeMouseMoveListener(this);
chart.getPlotArea().removePaintListener(this);
fTooltipHandler.deactivateHoverHelp(chart.getPlotArea());
}
}
@Override
public void refresh() {
// nothing to do
}
// ------------------------------------------------------------------------
// MouseTrackListener
// ------------------------------------------------------------------------
/**
* @deprecated, do not extend, use {@link #createToolTipMap(Parameter)} to
* populate tooltips
*/
@Deprecated
@Override
public void mouseEnter(MouseEvent e) {
// do nothing
}
/**
* @deprecated, do not extend, use {@link #createToolTipMap(Parameter)} to
* populate tooltips
*/
@Deprecated
@Override
public void mouseExit(MouseEvent e) {
// do nothing
}
/**
* @deprecated, do not extend, use {@link #createToolTipMap(Parameter)} to
* populate tooltips
*/
@Deprecated
@Override
public void mouseHover(MouseEvent e) {
// do nothing
}
// ------------------------------------------------------------------------
// MouseMoveListener
// ------------------------------------------------------------------------
@Override
public void mouseMove(@Nullable MouseEvent e) {
if (fIsHighlight) {
fIsHighlight = false;
getChart().redraw();
}
}
// ------------------------------------------------------------------------
// PaintListener
// ------------------------------------------------------------------------
@Override
public void paintControl(PaintEvent e) {
if (fIsHighlight && e != null) {
e.gc.setBackground(Display.getDefault().getSystemColor(
SWT.COLOR_RED));
e.gc.setAlpha(ALPHA);
e.gc.fillOval(fHighlightX - HIGHLIGHT_RADIUS, fHighlightY - HIGHLIGHT_RADIUS,
2 * HIGHLIGHT_RADIUS, 2 * HIGHLIGHT_RADIUS);
}
}
/**
* Creates the tooltip based on the given parameter.
*
* @param param
* parameter to create the tooltip string
* @return the tooltip based on the given parameter.
* @deprecated use {@link #createToolTipMap(Parameter)} as it will
* categorize data better
*/
@Deprecated
protected String createToolTipText(@NonNull Parameter param) {
ISeries[] series = getChart().getSeriesSet().getSeries();
int seriesIndex = param.getSeriesIndex();
int dataIndex = param.getDataIndex();
if ((series != null) && (seriesIndex < series.length)) {
ISeries serie = series[seriesIndex];
double[] xS = serie.getXSeries();
double[] yS = serie.getYSeries();
if ((xS != null) && (yS != null) && (dataIndex < xS.length) && (dataIndex < yS.length)) {
StringBuilder buffer = new StringBuilder();
buffer.append("x="); //$NON-NLS-1$
buffer.append(TmfTimestamp.fromNanos((long) xS[dataIndex] + getChartViewer().getTimeOffset()).toString());
buffer.append('\n');
buffer.append("y="); //$NON-NLS-1$
buffer.append((long) yS[dataIndex]);
return buffer.toString();
}
}
return null;
}
/**
* Creates the tooltip based on the given parameter.
*
* @param param
* parameter to create the tooltip string
* @return the tooltip map based on the given parameter. The Map<String,
* Map<String, Object>> can be seen as a table. The first element is
* the category, the second is the key, third is the value.
* @since 5.0
*/
protected @Nullable Map<@NonNull String, @NonNull Map<@NonNull String, @NonNull Object>> createToolTipMap(@NonNull Parameter param) {
String toolTipText = createToolTipText(param);
if (toolTipText == null) {
return null;
}
return Collections.singletonMap(OLD_TOOLTIP, Collections.singletonMap(OLD_TOOLTIP, toolTipText));
}
/**
* Parameter class
*/
protected static class Parameter {
/* A series index */
private int seriesIndex;
/* A data point index within a series */
private int dataIndex;
/**
* @return the series index
*/
public int getSeriesIndex() {
return seriesIndex;
}
/**
* @param seriesIndex
* index the seriesIndex to set
*/
public void setSeriesIndex(int seriesIndex) {
this.seriesIndex = seriesIndex;
}
/**
* @return the data index
*/
public int getDataIndex() {
return dataIndex;
}
/**
* @param dataIndex
* the data index to set
*/
public void setDataIndex(int dataIndex) {
this.dataIndex = dataIndex;
}
}
}