Upgrade to Tycho 0.23.0
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / viewers / xycharts / linecharts / TmfCommonXLineChartViewer.java
CommitLineData
2e427755 1/*******************************************************************************
4e72adee 2 * Copyright (c) 2014, 2015 École Polytechnique de Montréal and others.
2e427755
GB
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
12
2bdf0193 13package org.eclipse.tracecompass.tmf.ui.viewers.xycharts.linecharts;
2e427755
GB
14
15import java.util.LinkedHashMap;
16import java.util.Map;
17import java.util.Map.Entry;
18
00968516
GB
19import org.eclipse.core.runtime.IProgressMonitor;
20import org.eclipse.core.runtime.NullProgressMonitor;
c85a741e 21import org.eclipse.swt.SWT;
54404589 22import org.eclipse.swt.graphics.Point;
2e427755
GB
23import org.eclipse.swt.widgets.Composite;
24import org.eclipse.swt.widgets.Display;
54404589 25import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
2bdf0193 26import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
54404589
MAL
27import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
28import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentSignal;
2bdf0193
AM
29import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfChartTimeStampFormat;
30import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfXYChartViewer;
2e427755
GB
31import org.swtchart.IAxisTick;
32import org.swtchart.ILineSeries;
33import org.swtchart.ILineSeries.PlotSymbolType;
34import org.swtchart.ISeries;
35import org.swtchart.ISeries.SeriesType;
c85a741e 36import org.swtchart.ISeriesSet;
2e427755
GB
37import org.swtchart.LineStyle;
38import org.swtchart.Range;
39
40/**
41 * Abstract line chart viewer class implementation. All series in this viewer
42 * use the same X axis values. They are automatically created as values are
43 * provided for a key. Series by default will be displayed as a line. Each
44 * series appearance can be overridden when creating it.
45 *
46 * @author - Geneviève Bastien
2e427755
GB
47 */
48public abstract class TmfCommonXLineChartViewer extends TmfXYChartViewer {
49
50 private static final double DEFAULT_MAXY = Double.MIN_VALUE;
51 private static final double DEFAULT_MINY = Double.MAX_VALUE;
52
53 /* The desired number of points per pixel */
54 private static final double RESOLUTION = 1.0;
55
c85a741e
GB
56 private static final int[] LINE_COLORS = { SWT.COLOR_BLUE, SWT.COLOR_RED, SWT.COLOR_GREEN,
57 SWT.COLOR_MAGENTA, SWT.COLOR_CYAN,
58 SWT.COLOR_DARK_BLUE, SWT.COLOR_DARK_RED, SWT.COLOR_DARK_GREEN,
59 SWT.COLOR_DARK_MAGENTA, SWT.COLOR_DARK_CYAN, SWT.COLOR_DARK_YELLOW,
60 SWT.COLOR_BLACK, SWT.COLOR_GRAY };
61 private static final LineStyle[] LINE_STYLES = { LineStyle.SOLID, LineStyle.DASH, LineStyle.DOT, LineStyle.DASHDOT };
62
2e427755
GB
63 private final Map<String, double[]> fSeriesValues = new LinkedHashMap<>();
64 private double[] fXValues;
9392459a 65 private double fResolution;
2e427755 66
00968516
GB
67 private UpdateThread fUpdateThread;
68
2e427755
GB
69 /**
70 * Constructor
71 *
72 * @param parent
73 * The parent composite
74 * @param title
75 * The title of the viewer
76 * @param xLabel
77 * The label of the xAxis
78 * @param yLabel
79 * The label of the yAXIS
80 */
81 public TmfCommonXLineChartViewer(Composite parent, String title, String xLabel, String yLabel) {
82 super(parent, title, xLabel, yLabel);
83
9392459a 84 setResolution(RESOLUTION);
2e427755
GB
85 setTooltipProvider(new TmfCommonXLineChartTooltipProvider(this));
86 }
87
9392459a
GB
88 /**
89 * Set the number of requests per pixel that should be done on this chart
90 *
91 * @param resolution
92 * The number of points per pixels
93 */
94 protected void setResolution(double resolution) {
95 fResolution = resolution;
96 }
97
2e427755
GB
98 @Override
99 public void loadTrace(ITmfTrace trace) {
100 super.loadTrace(trace);
47cb22a7
GB
101 reinitialize();
102 }
103
104 /**
105 * Forces a reinitialization of the data sources, even if it has already
106 * been initialized for this trace before
47cb22a7
GB
107 */
108 protected void reinitialize() {
2e427755
GB
109 fSeriesValues.clear();
110 Thread thread = new Thread() {
111 @Override
112 public void run() {
113 initializeDataSource();
9b592962 114 getDisplay().asyncExec(new Runnable() {
2e427755
GB
115 @Override
116 public void run() {
117 if (!getSwtChart().isDisposed()) {
118 /* Delete the old series */
119 clearContent();
120 createSeries();
121 }
122 }
123 });
124 }
125 };
126 thread.start();
127 }
128
129 /**
130 * Initialize the source of the data for this viewer. This method is run in
131 * a separate thread, so this is where for example one can execute an
132 * analysis module and wait for its completion to initialize the series
133 */
134 protected void initializeDataSource() {
135
136 }
137
00968516
GB
138 private class UpdateThread extends Thread {
139 private final IProgressMonitor fMonitor;
140 private final int fNumRequests;
141
142 public UpdateThread(int numRequests) {
143 super("Line chart update"); //$NON-NLS-1$
144 fNumRequests = numRequests;
145 fMonitor = new NullProgressMonitor();
146 }
147
148 @Override
149 public void run() {
150 updateData(getWindowStartTime(), getWindowEndTime(), fNumRequests, fMonitor);
151 updateThreadFinished(this);
152 }
153
154 public void cancel() {
155 fMonitor.setCanceled(true);
156 }
157 }
158
159 private synchronized void newUpdateThread() {
160 cancelUpdate();
161 final int numRequests = (int) (getSwtChart().getPlotArea().getBounds().width * fResolution);
162 fUpdateThread = new UpdateThread(numRequests);
163 fUpdateThread.start();
164 }
165
166 private synchronized void updateThreadFinished(UpdateThread thread) {
167 if (thread == fUpdateThread) {
168 fUpdateThread = null;
169 }
170 }
171
172 /**
173 * Cancels the currently running update thread. It is automatically called
174 * when the content is updated, but child viewers may want to call it
175 * manually to do some operations before calling
176 * {@link TmfCommonXLineChartViewer#updateContent}
177 */
178 protected synchronized void cancelUpdate() {
179 if (fUpdateThread != null) {
180 fUpdateThread.cancel();
181 }
182 }
183
2e427755
GB
184 @Override
185 protected void updateContent() {
186 getDisplay().asyncExec(new Runnable() {
2e427755
GB
187 @Override
188 public void run() {
00968516 189 newUpdateThread();
2e427755
GB
190 }
191 });
192 }
193
194 /**
195 * Convenience method to compute the values of the X axis for a given time
196 * range. This method will return nb values depending, equally separated
197 * from start to end.
198 *
199 * The returned time values are in internal time, ie to get trace time, the
200 * time offset needs to be added to those values.
201 *
202 * @param start
203 * The start time of the time range
204 * @param end
205 * End time of the range
206 * @param nb
207 * The number of steps in the x axis.
208 * @return The time values (converted to double) to match every step.
209 */
5637b809 210 protected static final double[] getXAxis(long start, long end, int nb) {
2e427755
GB
211
212 double timestamps[] = new double[nb];
213 long steps = (end - start);
214 double step = steps / (double) nb;
215
216 double curTime = 1;
217 for (int i = 0; i < nb; i++) {
218 timestamps[i] = curTime;
219 curTime += step;
220 }
221 return timestamps;
222 }
223
224 /**
225 * Set the values of the x axis. There is only one array of values for the x
226 * axis for all series of a line chart so it needs to be set once here.
227 *
228 * @param xaxis
229 * The values for the x axis. The values must be in internal
230 * time, ie time offset have been subtracted from trace time
231 * values.
232 */
233 protected final void setXAxis(double[] xaxis) {
234 fXValues = xaxis;
235 }
236
237 /**
238 * Update the series data because the time range has changed. The x axis
239 * values for this data update can be computed using the
c85a741e
GB
240 * {@link TmfCommonXLineChartViewer#getXAxis(long, long, int)} method which
241 * will return a list of uniformely separated time values.
2e427755
GB
242 *
243 * Each series values should be set by calling the
244 * {@link TmfCommonXLineChartViewer#setSeries(String, double[])}.
245 *
246 * This method is responsible for calling the
c85a741e
GB
247 * {@link TmfCommonXLineChartViewer#updateDisplay()} when needed for the new
248 * values to be displayed.
2e427755
GB
249 *
250 * @param start
251 * The start time of the range for which the get the data
252 * @param end
253 * The end time of the range
254 * @param nb
255 * The number of 'points' in the chart.
00968516
GB
256 * @param monitor
257 * The progress monitor object
2e427755 258 */
00968516 259 protected abstract void updateData(long start, long end, int nb, IProgressMonitor monitor);
2e427755
GB
260
261 /**
262 * Set the data for a given series of the graph. The series does not need to
263 * be created before calling this, but it needs to have at least as many
264 * values as the x axis.
265 *
266 * If the series does not exist, it will automatically be created at display
267 * time, with the default values.
268 *
269 * @param seriesName
270 * The name of the series for which to set the values
271 * @param seriesValues
272 * The array of values for the series
273 */
274 protected void setSeries(String seriesName, double[] seriesValues) {
275 if (fXValues.length > seriesValues.length) {
276 throw new IllegalStateException();
277 }
278 fSeriesValues.put(seriesName, seriesValues);
279 }
280
281 /**
282 * Add a new series to the XY line chart. By default, it is a simple solid
283 * line.
284 *
2e427755
GB
285 * @param seriesName
286 * The name of the series to create
287 * @return The series so that the concrete viewer can modify its properties
288 * if required
289 */
290 protected ILineSeries addSeries(String seriesName) {
c85a741e
GB
291 ISeriesSet seriesSet = getSwtChart().getSeriesSet();
292 int seriesCount = seriesSet.getSeries().length;
293 ILineSeries series = (ILineSeries) seriesSet.createSeries(SeriesType.LINE, seriesName);
2e427755
GB
294 series.setVisible(true);
295 series.enableArea(false);
c85a741e 296 series.setLineStyle(LINE_STYLES[(seriesCount / (LINE_COLORS.length)) % LINE_STYLES.length]);
2e427755 297 series.setSymbolType(PlotSymbolType.NONE);
c85a741e 298 series.setLineColor(Display.getDefault().getSystemColor(LINE_COLORS[seriesCount % LINE_COLORS.length]));
2e427755
GB
299 return series;
300 }
301
302 /**
303 * Delete a series from the chart and its values from the viewer.
304 *
305 * @param seriesName
306 * Name of the series to delete
307 */
308 protected void deleteSeries(String seriesName) {
309 ISeries series = getSwtChart().getSeriesSet().getSeries(seriesName);
310 if (series != null) {
311 getSwtChart().getSeriesSet().deleteSeries(series.getId());
312 }
313 fSeriesValues.remove(seriesName);
314 }
315
316 /**
317 * Update the chart's values before refreshing the viewer
318 */
319 protected void updateDisplay() {
320 Display.getDefault().asyncExec(new Runnable() {
321 final TmfChartTimeStampFormat tmfChartTimeStampFormat = new TmfChartTimeStampFormat(getTimeOffset());
322
323 @Override
324 public void run() {
325 if (!getSwtChart().isDisposed()) {
326 double maxy = DEFAULT_MAXY;
327 double miny = DEFAULT_MINY;
328 for (Entry<String, double[]> entry : fSeriesValues.entrySet()) {
329 ILineSeries series = (ILineSeries) getSwtChart().getSeriesSet().getSeries(entry.getKey());
330 if (series == null) {
331 series = addSeries(entry.getKey());
332 }
333 series.setXSeries(fXValues);
334 /* Find the minimal and maximum values in this series */
335 for (double value : entry.getValue()) {
336 maxy = Math.max(maxy, value);
337 miny = Math.min(miny, value);
338 }
339 series.setYSeries(entry.getValue());
340 }
9392459a
GB
341 if (maxy == DEFAULT_MAXY) {
342 maxy = 1.0;
343 }
2e427755
GB
344
345 IAxisTick xTick = getSwtChart().getAxisSet().getXAxis(0).getTick();
346 xTick.setFormat(tmfChartTimeStampFormat);
347
348 final double start = fXValues[0];
349 int lastX = fXValues.length - 1;
350 double end = (start == fXValues[lastX]) ? start + 1 : fXValues[lastX];
351 getSwtChart().getAxisSet().getXAxis(0).setRange(new Range(start, end));
352 getSwtChart().getAxisSet().getXAxis(0).adjustRange();
353 if (maxy > miny) {
354 getSwtChart().getAxisSet().getYAxis(0).setRange(new Range(miny, maxy));
355 }
356 getSwtChart().redraw();
54404589
MAL
357
358 if (isSendTimeAlignSignals()) {
359 // The width of the chart might have changed and its time
360 // axis might be misaligned with the other views
361 Point viewPos = TmfCommonXLineChartViewer.this.getParent().getParent().toDisplay(0, 0);
362 int axisPos = getSwtChart().toDisplay(0, 0).x + getPointAreaOffset();
363 int timeAxisOffset = axisPos - viewPos.x;
364 TmfTimeViewAlignmentInfo timeAlignmentInfo = new TmfTimeViewAlignmentInfo(getControl().getShell(), viewPos, timeAxisOffset);
365 TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(TmfCommonXLineChartViewer.this, timeAlignmentInfo, true));
366 }
2e427755
GB
367 }
368 }
369 });
370 }
371
372 /**
373 * Create the series once the initialization of the viewer's data source is
374 * done. Series do not need to be created before setting their values, but
375 * if their appearance needs to be customized, this method is a good place
376 * to do so. It is called only once per trace.
377 */
378 protected void createSeries() {
379
380 }
381
382}
This page took 0.109892 seconds and 5 git commands to generate.