|
1 | | -/******************************************************************************* |
2 | | - * Copyright (c) 2014, 2020 École Polytechnique de Montréal and others |
3 | | - * |
4 | | - * All rights reserved. This program and the accompanying materials are |
5 | | - * made available under the terms of the Eclipse Public License 2.0 which |
6 | | - * accompanies this distribution, and is available at |
7 | | - * https://www.eclipse.org/legal/epl-2.0/ |
8 | | - * |
9 | | - * SPDX-License-Identifier: EPL-2.0 |
10 | | - * |
11 | | - * Contributors: |
12 | | - * Geneviève Bastien - Initial API and implementation |
13 | | - *******************************************************************************/ |
14 | | - |
15 | | -package org.eclipse.tracecompass.tmf.ui.viewers.xychart.linechart; |
16 | | - |
17 | | -import java.text.Format; |
18 | | -import java.util.Arrays; |
19 | | -import java.util.List; |
20 | | - |
21 | | -import org.eclipse.swt.events.MouseEvent; |
22 | | -import org.eclipse.swt.graphics.Color; |
23 | | -import org.eclipse.swt.graphics.Point; |
24 | | -import org.eclipse.swt.graphics.RGBA; |
25 | | -import org.eclipse.swt.widgets.Control; |
26 | | -import org.eclipse.tracecompass.internal.tmf.ui.Messages; |
27 | | -import org.eclipse.tracecompass.tmf.core.presentation.RGBAColor; |
28 | | -import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; |
29 | | -import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; |
30 | | -import org.eclipse.tracecompass.tmf.ui.viewers.TmfAbstractToolTipHandler; |
31 | | -import org.eclipse.tracecompass.tmf.ui.viewers.xychart.IAxis; |
32 | | -import org.eclipse.tracecompass.tmf.ui.viewers.xychart.ITmfChartTimeProvider; |
33 | | -import org.eclipse.tracecompass.tmf.ui.viewers.xychart.IXYSeries; |
34 | | -import org.eclipse.tracecompass.tmf.ui.viewers.xychart.TmfBaseProvider; |
35 | | - |
36 | | -/** |
37 | | - * Displays a tooltip on line charts. For each series, it shows the y value at |
38 | | - * the selected x value. This tooltip assumes that all series share a common set |
39 | | - * of X axis values. If the X series is not common, the tooltip text may not be |
40 | | - * accurate. |
41 | | - * |
42 | | - * @since 6.0 |
43 | | - */ |
44 | | -public class TmfCommonXLineChartTooltipProvider extends TmfBaseProvider { |
45 | | - |
46 | | - private final class XYToolTipHandler extends TmfAbstractToolTipHandler { |
47 | | - private static final String HTML_COLOR_TOOLTIP = "<span style=\"color:%s;\">%s</span>"; //$NON-NLS-1$ |
48 | | - |
49 | | - private boolean isValid(int index, IXYSeries serie) { |
50 | | - double[] ySeries = serie.getYSeries(); |
51 | | - return serie.isVisible() && ySeries != null && ySeries.length > index; |
52 | | - } |
53 | | - |
54 | | - @Override |
55 | | - public void fill(Control control, MouseEvent event, Point pt) { |
56 | | - if (getChartViewer().getWindowDuration() != 0) { |
57 | | - IAxis xAxis = getXAxis(); |
58 | | - |
59 | | - double xCoordinate = xAxis.getDataCoordinate(pt.x); |
60 | | - |
61 | | - List<IXYSeries> series = getSeries(); |
62 | | - |
63 | | - if ((xCoordinate < 0) || (series.isEmpty())) { |
64 | | - return; |
65 | | - } |
66 | | - |
67 | | - /* Find the index of the value we want */ |
68 | | - double[] xS = series.get(0).getXSeries(); |
69 | | - if (xS == null) { |
70 | | - return; |
71 | | - } |
72 | | - int index = Arrays.binarySearch(xS, xCoordinate); |
73 | | - index = index >= 0 ? index : -index - 1; |
74 | | - int maxLen = 0; |
75 | | - for (IXYSeries serie : series) { |
76 | | - /* Make sure the series values and the value at index exist */ |
77 | | - if (isValid(index, serie)) { |
78 | | - maxLen = Math.max(maxLen, serie.getId().length()); |
79 | | - } |
80 | | - } |
81 | | - |
82 | | - TmfCommonXAxisChartViewer viewer = null; |
83 | | - Format format = null; |
84 | | - ITmfChartTimeProvider timeProvider = getChartViewer(); |
85 | | - if (timeProvider instanceof TmfCommonXAxisChartViewer) { |
86 | | - viewer = (TmfCommonXAxisChartViewer) timeProvider; |
87 | | - format = viewer.getSwtChart().getAxisSet().getYAxes()[0].getTick().getFormat(); |
88 | | - } |
89 | | - ITmfTimestamp time = TmfTimestamp.fromNanos((long) xCoordinate + getChartViewer().getTimeOffset()); |
90 | | - addItem(null, ToolTipString.fromString(Messages.TmfCommonXLineChartTooltipProvider_time), ToolTipString.fromTimestamp(time.toString(), time.toNanos())); |
91 | | - /* For each series, get the value at the index */ |
92 | | - for (IXYSeries serie : series) { |
93 | | - double[] yS = serie.getYSeries(); |
94 | | - /* Make sure the series values and the value at index exist */ |
95 | | - if (isValid(index, serie)) { |
96 | | - String key = serie.getId(); |
97 | | - Color color = serie.getColor(); |
98 | | - if (key != null && color != null && viewer != null) { |
99 | | - RGBA rgba = color.getRGBA(); |
100 | | - RGBAColor rgbaColor = new RGBAColor(rgba.rgb.red, rgba.rgb.green, rgba.rgb.blue, rgba.alpha); |
101 | | - key = String.format(HTML_COLOR_TOOLTIP, rgbaColor, key); |
102 | | - } |
103 | | - if (key == null) { |
104 | | - key = ""; //$NON-NLS-1$ |
105 | | - } |
106 | | - double yValue = yS[index]; |
107 | | - if (format == null) { |
108 | | - addItem(null, ToolTipString.fromHtml(key), ToolTipString.fromDecimal(yValue)); |
109 | | - } else { |
110 | | - addItem(null, ToolTipString.fromHtml(key), ToolTipString.fromString(format.format(yValue))); |
111 | | - } |
112 | | - } |
113 | | - } |
114 | | - } |
115 | | - } |
116 | | - |
117 | | - } |
118 | | - |
119 | | - private XYToolTipHandler fToolTipHandler = new XYToolTipHandler(); |
120 | | - |
121 | | - /** |
122 | | - * Constructor for the tooltip provider |
123 | | - * |
124 | | - * @param tmfChartViewer |
125 | | - * The parent chart viewer |
126 | | - */ |
127 | | - public TmfCommonXLineChartTooltipProvider(ITmfChartTimeProvider tmfChartViewer) { |
128 | | - super(tmfChartViewer); |
129 | | - register(); |
130 | | - } |
131 | | - |
132 | | - // ------------------------------------------------------------------------ |
133 | | - // TmfBaseProvider |
134 | | - // ------------------------------------------------------------------------ |
135 | | - |
136 | | - @Override |
137 | | - public TmfAbstractToolTipHandler getTooltipHandler() { |
138 | | - return fToolTipHandler; |
139 | | - } |
140 | | - |
141 | | - @Override |
142 | | - public void refresh() { |
143 | | - // nothing to do |
144 | | - } |
145 | | -} |
| 1 | +/******************************************************************************* |
| 2 | + * Copyright (c) 2014, 2020 École Polytechnique de Montréal and others |
| 3 | + * |
| 4 | + * All rights reserved. This program and the accompanying materials are |
| 5 | + * made available under the terms of the Eclipse Public License 2.0 which |
| 6 | + * accompanies this distribution, and is available at |
| 7 | + * https://www.eclipse.org/legal/epl-2.0/ |
| 8 | + * |
| 9 | + * SPDX-License-Identifier: EPL-2.0 |
| 10 | + * |
| 11 | + * Contributors: |
| 12 | + * Geneviève Bastien - Initial API and implementation |
| 13 | + *******************************************************************************/ |
| 14 | + |
| 15 | +package org.eclipse.tracecompass.tmf.ui.viewers.xychart.linechart; |
| 16 | + |
| 17 | +import java.text.Format; |
| 18 | +import java.util.Arrays; |
| 19 | +import java.util.List; |
| 20 | + |
| 21 | +import org.eclipse.swt.events.MouseEvent; |
| 22 | +import org.eclipse.swt.graphics.Color; |
| 23 | +import org.eclipse.swt.graphics.Point; |
| 24 | +import org.eclipse.swt.graphics.RGBA; |
| 25 | +import org.eclipse.swt.widgets.Control; |
| 26 | +import org.eclipse.tracecompass.internal.tmf.ui.Messages; |
| 27 | +import org.eclipse.tracecompass.tmf.core.presentation.RGBAColor; |
| 28 | +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; |
| 29 | +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; |
| 30 | +import org.eclipse.tracecompass.tmf.ui.viewers.TmfAbstractToolTipHandler; |
| 31 | +import org.eclipse.tracecompass.tmf.ui.viewers.TmfAbstractToolTipHandler.ToolTipString; |
| 32 | +import org.eclipse.tracecompass.tmf.ui.viewers.xychart.IAxis; |
| 33 | +import org.eclipse.tracecompass.tmf.ui.viewers.xychart.ITmfChartTimeProvider; |
| 34 | +import org.eclipse.tracecompass.tmf.ui.viewers.xychart.IXYSeries; |
| 35 | +import org.eclipse.tracecompass.tmf.ui.viewers.xychart.TmfBaseProvider; |
| 36 | + |
| 37 | +/** |
| 38 | + * Displays a tooltip on line charts. For each series, it shows the y value at |
| 39 | + * the selected x value. This tooltip assumes that all series share a common set |
| 40 | + * of X axis values. If the X series is not common, the tooltip text may not be |
| 41 | + * accurate. |
| 42 | + * |
| 43 | + * @since 6.0 |
| 44 | + */ |
| 45 | +public class TmfCommonXLineChartTooltipProvider extends TmfBaseProvider { |
| 46 | + |
| 47 | + private static final String HTML_COLOR_TOOLTIP = "<span style=\"color:%s;\">%s</span>"; //$NON-NLS-1$ |
| 48 | + private final CommonToolTipHandler fToolTipHandler = new CommonToolTipHandler(); |
| 49 | + |
| 50 | + /** |
| 51 | + * Constructor for the tooltip provider |
| 52 | + * |
| 53 | + * @param tmfChartViewer |
| 54 | + * The parent chart viewer |
| 55 | + */ |
| 56 | + public TmfCommonXLineChartTooltipProvider(ITmfChartTimeProvider tmfChartViewer) { |
| 57 | + super(tmfChartViewer); |
| 58 | + register(); |
| 59 | + } |
| 60 | + |
| 61 | + // ------------------------------------------------------------------------ |
| 62 | + // TmfBaseProvider |
| 63 | + // ------------------------------------------------------------------------ |
| 64 | + |
| 65 | + @Override |
| 66 | + public TmfAbstractToolTipHandler getTooltipHandler() { |
| 67 | + return fToolTipHandler; |
| 68 | + } |
| 69 | + |
| 70 | + @Override |
| 71 | + public void refresh() { |
| 72 | + // nothing to do |
| 73 | + } |
| 74 | + |
| 75 | + // ====================================================================== |
| 76 | + // HOOK METHODS |
| 77 | + // ====================================================================== |
| 78 | + |
| 79 | + @FunctionalInterface |
| 80 | + protected interface ITooltipItemAdder { |
| 81 | + void addItem(ToolTipString key, ToolTipString value); |
| 82 | + } |
| 83 | + |
| 84 | + protected void addAdditionalTooltipItems(ITooltipItemAdder adder, double xCoordinate) { |
| 85 | + long timeNanos = Math.round(xCoordinate) + getChartViewer().getTimeOffset(); |
| 86 | + ITmfTimestamp time = TmfTimestamp.fromNanos(timeNanos); |
| 87 | + adder.addItem( |
| 88 | + ToolTipString.fromString(Messages.TmfCommonXLineChartTooltipProvider_time), |
| 89 | + ToolTipString.fromTimestamp(time.toString(), time.toNanos())); |
| 90 | + } |
| 91 | + |
| 92 | + protected void addSeriesTooltipItem(ITooltipItemAdder adder, IXYSeries xySeries, int index, Format format) { |
| 93 | + double[] ySeries = xySeries.getYSeries(); |
| 94 | + if (ySeries == null || index < 0 || index >= ySeries.length) { |
| 95 | + return; |
| 96 | + } |
| 97 | + |
| 98 | + String label = formatSeriesLabel(xySeries); |
| 99 | + double yValue = ySeries[index]; |
| 100 | + if (format == null) { |
| 101 | + adder.addItem( |
| 102 | + ToolTipString.fromHtml(label), |
| 103 | + ToolTipString.fromDecimal(yValue)); |
| 104 | + } else { |
| 105 | + adder.addItem( |
| 106 | + ToolTipString.fromHtml(label), |
| 107 | + ToolTipString.fromString(format.format(yValue))); |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + protected String formatSeriesLabel(IXYSeries xySeries) { |
| 112 | + String label = xySeries.getId(); |
| 113 | + if (label == null) { |
| 114 | + label = ""; //$NON-NLS-1$ |
| 115 | + } |
| 116 | + |
| 117 | + Color color = xySeries.getColor(); |
| 118 | + if (color != null) { |
| 119 | + RGBA rgba = color.getRGBA(); |
| 120 | + RGBAColor rgbaColor = new RGBAColor(rgba.rgb.red, rgba.rgb.green, rgba.rgb.blue, rgba.alpha); |
| 121 | + label = String.format(TmfCommonXLineChartTooltipProvider.HTML_COLOR_TOOLTIP, rgbaColor, label); |
| 122 | + } |
| 123 | + |
| 124 | + return label; |
| 125 | + } |
| 126 | + |
| 127 | + protected String getFirstValidSeriesKey() { |
| 128 | + return fToolTipHandler.firstValidSeriesKey; |
| 129 | + } |
| 130 | + |
| 131 | + // ====================================================================== |
| 132 | + // TOOLTIP HANDLER |
| 133 | + // ====================================================================== |
| 134 | + |
| 135 | + private final class CommonToolTipHandler extends TmfAbstractToolTipHandler { |
| 136 | + |
| 137 | + private String firstValidSeriesKey; |
| 138 | + |
| 139 | + @Override |
| 140 | + public void fill(Control control, MouseEvent event, Point pt) { |
| 141 | + if (!isTooltipAvailable()) { |
| 142 | + return; |
| 143 | + } |
| 144 | + |
| 145 | + IAxis xAxis = getXAxis(); |
| 146 | + double xCoordinate = xAxis.getDataCoordinate(pt.x); |
| 147 | + if (xCoordinate < 0) { |
| 148 | + return; |
| 149 | + } |
| 150 | + |
| 151 | + List<IXYSeries> series = getSeries(); |
| 152 | + int index = getHoveredIndex(series, xCoordinate); |
| 153 | + if (index < 0) { |
| 154 | + return; |
| 155 | + } |
| 156 | + |
| 157 | + Format format = null; |
| 158 | + if (getChartViewer() instanceof TmfCommonXAxisChartViewer chartViewer) { |
| 159 | + format = chartViewer.getSwtChart().getAxisSet().getYAxes()[0].getTick().getFormat(); |
| 160 | + } |
| 161 | + |
| 162 | + boolean firstValid = true; |
| 163 | + for (IXYSeries xySeries : series) { |
| 164 | + if (!isValidSeriesIndex(xySeries, index)) { |
| 165 | + continue; |
| 166 | + } |
| 167 | + |
| 168 | + if (firstValid) { |
| 169 | + firstValid = false; |
| 170 | + firstValidSeriesKey = xySeries.getId(); |
| 171 | + addAdditionalTooltipItems((key, value) -> addItem(null, key, value), xCoordinate); |
| 172 | + } |
| 173 | + |
| 174 | + addSeriesTooltipItem((key, value) -> addItem(null, key, value), xySeries, index, format); |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + private boolean isTooltipAvailable() { |
| 179 | + return getChartViewer().getWindowDuration() != 0; |
| 180 | + } |
| 181 | + |
| 182 | + private int getHoveredIndex(List<IXYSeries> series, double xCoordinate) { |
| 183 | + if (series.isEmpty()) { |
| 184 | + return -1; |
| 185 | + } |
| 186 | + |
| 187 | + double[] xSeries = series.get(0).getXSeries(); |
| 188 | + if (xSeries == null || xSeries.length == 0) { |
| 189 | + return -1; |
| 190 | + } |
| 191 | + |
| 192 | + int index = Arrays.binarySearch(xSeries, xCoordinate); |
| 193 | + if (index < 0) { |
| 194 | + index = -index - 1; |
| 195 | + index = Math.max(0, index - 1); |
| 196 | + } |
| 197 | + |
| 198 | + return index < xSeries.length ? index : -1; |
| 199 | + } |
| 200 | + |
| 201 | + private boolean isValidSeriesIndex(IXYSeries series, int index) { |
| 202 | + double[] ySeries = series.getYSeries(); |
| 203 | + return series.isVisible() |
| 204 | + && ySeries != null |
| 205 | + && index >= 0 |
| 206 | + && index < ySeries.length; |
| 207 | + } |
| 208 | + } |
| 209 | +} |
0 commit comments