Commit | Line | Data |
---|---|---|
4208b510 AM |
1 | /******************************************************************************* |
2 | * Copyright (c) 2015, 2016 EfficiOS Inc., Michael Jeanson | |
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 | ||
10 | package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers; | |
11 | ||
12 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; | |
13 | import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString; | |
14 | ||
15 | import java.text.Format; | |
16 | import java.util.ArrayList; | |
17 | import java.util.HashSet; | |
18 | import java.util.List; | |
19 | import java.util.Set; | |
20 | import java.util.concurrent.TimeUnit; | |
21 | import java.util.function.ToDoubleFunction; | |
22 | ||
23 | import org.eclipse.jdt.annotation.NonNull; | |
24 | import org.eclipse.jdt.annotation.Nullable; | |
25 | import org.eclipse.swt.SWT; | |
26 | import org.eclipse.swt.graphics.Color; | |
27 | import org.eclipse.swt.graphics.Font; | |
28 | import org.eclipse.swt.graphics.GC; | |
29 | import org.eclipse.swt.graphics.Point; | |
30 | import org.eclipse.swt.graphics.Rectangle; | |
31 | import org.eclipse.swt.widgets.Composite; | |
32 | import org.eclipse.swt.widgets.Control; | |
33 | import org.eclipse.swt.widgets.Display; | |
34 | import org.eclipse.swt.widgets.Listener; | |
35 | import org.eclipse.tracecompass.common.core.format.DecimalUnitFormat; | |
36 | import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect; | |
37 | import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel; | |
38 | import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable; | |
39 | import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry; | |
40 | import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTimeStampFormat; | |
41 | import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal; | |
42 | import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; | |
43 | import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer; | |
44 | import org.swtchart.Chart; | |
45 | import org.swtchart.ITitle; | |
46 | ||
47 | import com.google.common.collect.ImmutableList; | |
48 | ||
49 | /** | |
50 | * Abstract XYChart Viewer for LAMI views. | |
51 | * | |
52 | * @author Michael Jeanson | |
53 | * | |
54 | */ | |
55 | public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer { | |
56 | ||
57 | /** Ellipsis character */ | |
58 | protected static final String ELLIPSIS = "…"; //$NON-NLS-1$ | |
59 | ||
60 | /** | |
61 | * String representing unknown values. Can be present even in numerical | |
62 | * aspects! | |
63 | */ | |
64 | protected static final String UNKNOWN = "?"; //$NON-NLS-1$ | |
65 | ||
66 | /** Zero value */ | |
67 | protected static final double ZERO = 0.0; | |
68 | ||
69 | /** Symbol for seconds (used in the custom ns -> s conversion) */ | |
70 | private static final String SECONDS_SYMBOL = "s"; //$NON-NLS-1$ | |
71 | ||
72 | /** Symbol for nanoseconds (used in the custom ns -> s conversion) */ | |
73 | private static final String NANOSECONDS_SYMBOL = "ns"; //$NON-NLS-1$ | |
74 | ||
75 | /** | |
76 | * Function to use to map Strings read from the data table to doubles for | |
77 | * use in SWTChart series. | |
78 | */ | |
79 | protected static final ToDoubleFunction<@Nullable String> DOUBLE_MAPPER = str -> { | |
80 | if (str == null || str.equals(UNKNOWN)) { | |
81 | return ZERO; | |
82 | } | |
83 | return Double.parseDouble(str); | |
84 | }; | |
85 | ||
86 | /** | |
87 | * List of standard colors | |
88 | */ | |
89 | protected static final List<@NonNull Color> COLORS = ImmutableList.of( | |
90 | new Color(Display.getDefault(), 72, 120, 207), | |
91 | new Color(Display.getDefault(), 106, 204, 101), | |
92 | new Color(Display.getDefault(), 214, 95, 95), | |
93 | new Color(Display.getDefault(), 180, 124, 199), | |
94 | new Color(Display.getDefault(), 196, 173, 102), | |
95 | new Color(Display.getDefault(), 119, 190, 219) | |
96 | ); | |
97 | ||
98 | /** | |
99 | * List of "light" colors (when unselected) | |
100 | */ | |
101 | protected static final List<@NonNull Color> LIGHT_COLORS = ImmutableList.of( | |
102 | new Color(Display.getDefault(), 173, 195, 233), | |
103 | new Color(Display.getDefault(), 199, 236, 197), | |
104 | new Color(Display.getDefault(), 240, 196, 196), | |
105 | new Color(Display.getDefault(), 231, 213, 237), | |
106 | new Color(Display.getDefault(), 231, 222, 194), | |
107 | new Color(Display.getDefault(), 220, 238, 246) | |
108 | ); | |
109 | ||
110 | /** | |
111 | * Time stamp formatter for intervals in the days range. | |
112 | */ | |
113 | protected static final LamiTimeStampFormat DAYS_FORMATTER = new LamiTimeStampFormat("dd HH:mm"); //$NON-NLS-1$ | |
114 | ||
115 | /** | |
116 | * Time stamp formatter for intervals in the hours range. | |
117 | */ | |
118 | protected static final LamiTimeStampFormat HOURS_FORMATTER = new LamiTimeStampFormat("HH:mm"); //$NON-NLS-1$ | |
119 | ||
120 | /** | |
121 | * Time stamp formatter for intervals in the minutes range. | |
122 | */ | |
123 | protected static final LamiTimeStampFormat MINUTES_FORMATTER = new LamiTimeStampFormat("mm:ss"); //$NON-NLS-1$ | |
124 | ||
125 | /** | |
126 | * Time stamp formatter for intervals in the seconds range. | |
127 | */ | |
128 | protected static final LamiTimeStampFormat SECONDS_FORMATTER = new LamiTimeStampFormat("ss"); //$NON-NLS-1$ | |
129 | ||
130 | /** | |
131 | * Time stamp formatter for intervals in the milliseconds range. | |
132 | */ | |
133 | protected static final LamiTimeStampFormat MILLISECONDS_FORMATTER = new LamiTimeStampFormat("ss.SSS"); //$NON-NLS-1$ | |
134 | ||
135 | /** | |
136 | * Decimal formatter to display nanoseconds as seconds. | |
137 | */ | |
138 | protected static final DecimalUnitFormat NANO_TO_SECS_FORMATTER = new DecimalUnitFormat(0.000000001); | |
139 | ||
140 | /** | |
141 | * Default decimal formatter. | |
142 | */ | |
143 | protected static final DecimalUnitFormat DECIMAL_FORMATTER = new DecimalUnitFormat(); | |
144 | ||
145 | private final Listener fResizeListener = event -> { | |
146 | /* Refresh the titles to fit the current chart size */ | |
147 | refreshDisplayTitles(); | |
148 | ||
149 | /* Refresh the Axis labels to fit the current chart size */ | |
150 | refreshDisplayLabels(); | |
151 | }; | |
152 | ||
153 | private final LamiResultTable fResultTable; | |
154 | private final LamiChartModel fChartModel; | |
155 | ||
156 | private final Chart fChart; | |
157 | ||
158 | private final String fChartTitle; | |
159 | private final String fXTitle; | |
160 | private final String fYTitle; | |
161 | ||
162 | private boolean fSelected; | |
163 | private Set<Integer> fSelection; | |
164 | ||
165 | /** | |
166 | * Creates a Viewer instance based on SWTChart. | |
167 | * | |
168 | * @param parent | |
169 | * The parent composite to draw in. | |
170 | * @param resultTable | |
171 | * The result table containing the data from which to build the | |
172 | * chart | |
173 | * @param chartModel | |
174 | * The information about the chart to build | |
175 | */ | |
176 | public LamiXYChartViewer(Composite parent, LamiResultTable resultTable, LamiChartModel chartModel) { | |
177 | super(parent); | |
178 | ||
179 | fParent = parent; | |
180 | fResultTable = resultTable; | |
181 | fChartModel = chartModel; | |
182 | fSelection = new HashSet<>(); | |
183 | ||
184 | fChart = new Chart(parent, SWT.NONE); | |
185 | fChart.addListener(SWT.Resize, fResizeListener); | |
186 | ||
187 | /* Set Chart title */ | |
188 | fChartTitle = fResultTable.getTableClass().getTableTitle(); | |
189 | ||
190 | /* Set X axis title */ | |
191 | if (fChartModel.getXSeriesColumns().size() == 1) { | |
192 | /* | |
193 | * There is only 1 series in the chart, we will use its name as the | |
194 | * Y axis (and hide the legend). | |
195 | */ | |
196 | String seriesName = getChartModel().getXSeriesColumns().get(0); | |
197 | // The time duration formatter converts ns to s on the axis | |
198 | if (NANOSECONDS_SYMBOL.equals(getXAxisAspects().get(0).getUnits())) { | |
199 | seriesName = getXAxisAspects().get(0).getName() + " (" + SECONDS_SYMBOL + ')'; //$NON-NLS-1$ | |
200 | } | |
201 | fXTitle = seriesName; | |
202 | } else { | |
203 | /* | |
204 | * There are multiple series in the chart, if they all share the same | |
205 | * units, display that. | |
206 | */ | |
207 | long nbDiffAspects = getXAxisAspects().stream() | |
208 | .map(aspect -> aspect.getUnits()) | |
209 | .distinct() | |
210 | .count(); | |
211 | ||
212 | String units = getXAxisAspects().get(0).getUnits(); | |
213 | if (nbDiffAspects == 1 && units != null) { | |
214 | /* All aspects use the same unit type */ | |
215 | ||
216 | // The time duration formatter converts ns to s on the axis | |
217 | if (NANOSECONDS_SYMBOL.equals(units)) { | |
218 | units = SECONDS_SYMBOL; | |
219 | } | |
220 | fXTitle = Messages.LamiViewer_DefaultValueName + " (" + units + ')'; //$NON-NLS-1$ | |
221 | } else { | |
222 | /* Various unit types, just say "Value" */ | |
223 | fXTitle = nullToEmptyString(Messages.LamiViewer_DefaultValueName); | |
224 | } | |
225 | } | |
226 | ||
227 | /* Set Y axis title */ | |
228 | if (fChartModel.getYSeriesColumns().size() == 1) { | |
229 | /* | |
230 | * There is only 1 series in the chart, we will use its name as the | |
231 | * Y axis (and hide the legend). | |
232 | */ | |
233 | String seriesName = getChartModel().getYSeriesColumns().get(0); | |
234 | // The time duration formatter converts ns to s on the axis | |
235 | if (NANOSECONDS_SYMBOL.equals(getYAxisAspects().get(0).getUnits())) { | |
236 | seriesName = getYAxisAspects().get(0).getName() + " (" + SECONDS_SYMBOL + ')'; //$NON-NLS-1$ | |
237 | } | |
238 | fYTitle = seriesName; | |
239 | fChart.getLegend().setVisible(false); | |
240 | } else { | |
241 | /* | |
242 | * There are multiple series in the chart, if they all share the same | |
243 | * units, display that. | |
244 | */ | |
245 | long nbDiffAspects = getYAxisAspects().stream() | |
246 | .map(aspect -> aspect.getUnits()) | |
247 | .distinct() | |
248 | .count(); | |
249 | ||
250 | String units = getYAxisAspects().get(0).getUnits(); | |
251 | if (nbDiffAspects == 1 && units != null) { | |
252 | /* All aspects use the same unit type */ | |
253 | ||
254 | // The time duration formatter converts ns to s on the axis | |
255 | if (NANOSECONDS_SYMBOL.equals(units)) { | |
256 | units = SECONDS_SYMBOL; | |
257 | } | |
258 | fYTitle = Messages.LamiViewer_DefaultValueName + " (" + units + ')'; //$NON-NLS-1$ | |
259 | } else { | |
260 | /* Various unit types, just say "Value" */ | |
261 | fYTitle = nullToEmptyString(Messages.LamiViewer_DefaultValueName); | |
262 | } | |
263 | ||
264 | /* Put legend at the bottom */ | |
265 | fChart.getLegend().setPosition(SWT.BOTTOM); | |
266 | } | |
267 | ||
268 | /* Set all titles and labels font color to black */ | |
269 | fChart.getTitle().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); | |
270 | fChart.getAxisSet().getXAxis(0).getTitle().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); | |
271 | fChart.getAxisSet().getYAxis(0).getTitle().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); | |
272 | fChart.getAxisSet().getXAxis(0).getTick().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); | |
273 | fChart.getAxisSet().getYAxis(0).getTick().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); | |
274 | ||
275 | /* Set X label 90 degrees */ | |
276 | fChart.getAxisSet().getXAxis(0).getTick().setTickLabelAngle(90); | |
277 | ||
278 | /* Refresh the titles to fit the current chart size */ | |
279 | refreshDisplayTitles(); | |
280 | ||
281 | fChart.addDisposeListener(e -> { | |
282 | /* Dispose resources of this class */ | |
283 | LamiXYChartViewer.super.dispose(); | |
284 | }); | |
285 | } | |
286 | ||
287 | /** | |
288 | * Util method to check if a list of aspects are all continuous. | |
289 | * | |
290 | * @param axisAspects | |
291 | * The list of aspects to check. | |
292 | * @return true is all aspects are continuous, otherwise false. | |
293 | */ | |
294 | protected static boolean areAspectsContinuous(List<LamiTableEntryAspect> axisAspects) { | |
295 | return axisAspects.stream().allMatch(aspect -> aspect.isContinuous()); | |
296 | } | |
297 | ||
298 | /** | |
299 | * Util method to check if a list of aspects are all time stamps. | |
300 | * | |
301 | * @param axisAspects | |
302 | * The list of aspects to check. | |
303 | * @return true is all aspects are time stamps, otherwise false. | |
304 | */ | |
305 | protected static boolean areAspectsTimeStamp(List<LamiTableEntryAspect> axisAspects) { | |
306 | return axisAspects.stream().allMatch(aspect -> aspect.isTimeStamp()); | |
307 | } | |
308 | ||
309 | /** | |
310 | * Util method to check if a list of aspects are all time durations. | |
311 | * | |
312 | * @param axisAspects | |
313 | * The list of aspects to check. | |
314 | * @return true is all aspects are time durations, otherwise false. | |
315 | */ | |
316 | protected static boolean areAspectsTimeDuration(List<LamiTableEntryAspect> axisAspects) { | |
317 | return axisAspects.stream().allMatch(aspect -> aspect.isTimeDuration()); | |
318 | } | |
319 | ||
320 | /** | |
321 | * Util method that will return a formatter based on the aspects linked to an axis | |
322 | * | |
323 | * If all aspects are time stamps, return a timestamp formatter tuned to the interval. | |
324 | * If all aspects are time durations, return the nanoseconds to seconds formatter. | |
325 | * Otherwise, return the generic decimal formatter. | |
326 | * | |
327 | * @param axisAspects | |
328 | * The list of aspects of the axis. | |
329 | * @param entries | |
330 | * The list of entries of the chart. | |
331 | * @return a formatter for the axis. | |
332 | */ | |
333 | protected static Format getContinuousAxisFormatter(List<LamiTableEntryAspect> axisAspects, List<LamiTableEntry> entries) { | |
334 | ||
335 | if (areAspectsTimeStamp(axisAspects)) { | |
336 | /* Set a TimeStamp formatter depending on the duration between the first and last value */ | |
337 | double max = Double.MIN_VALUE; | |
338 | double min = Double.MAX_VALUE; | |
339 | ||
340 | for (LamiTableEntry entry : entries) { | |
341 | for (LamiTableEntryAspect aspect : axisAspects) { | |
342 | Double current = aspect.resolveDouble(entry); | |
343 | if (current != null) { | |
344 | max = Math.max(max, current); | |
345 | min = Math.min(min, current); | |
346 | } | |
347 | } | |
348 | } | |
349 | long duration = (long) max - (long) min; | |
350 | ||
351 | if (duration > TimeUnit.DAYS.toNanos(1)) { | |
352 | return DAYS_FORMATTER; | |
353 | } else if (duration > TimeUnit.HOURS.toNanos(1)) { | |
354 | return HOURS_FORMATTER; | |
355 | } else if (duration > TimeUnit.MINUTES.toNanos(1)) { | |
356 | return MINUTES_FORMATTER; | |
357 | } else if (duration > TimeUnit.SECONDS.toNanos(15)) { | |
358 | return SECONDS_FORMATTER; | |
359 | } else { | |
360 | return MILLISECONDS_FORMATTER; | |
361 | } | |
362 | } else if (areAspectsTimeDuration(axisAspects)) { | |
363 | /* Set the time duration formatter */ | |
364 | return NANO_TO_SECS_FORMATTER; | |
365 | ||
366 | } else { | |
367 | /* For other numeric aspects, use the default decimal unit formatter */ | |
368 | return DECIMAL_FORMATTER; | |
369 | } | |
370 | } | |
371 | ||
372 | /** | |
373 | * Get the chart result table. | |
374 | * | |
375 | * @return The chart result table. | |
376 | */ | |
377 | protected LamiResultTable getResultTable() { | |
378 | return fResultTable; | |
379 | } | |
380 | ||
381 | /** | |
382 | * Get the chart model. | |
383 | * | |
384 | * @return The chart model. | |
385 | */ | |
386 | protected LamiChartModel getChartModel() { | |
387 | return fChartModel; | |
388 | } | |
389 | ||
390 | /** | |
391 | * Get the chart object. | |
392 | * @return The chart object. | |
393 | */ | |
394 | protected Chart getChart() { | |
395 | return fChart; | |
396 | } | |
397 | ||
398 | /** | |
399 | * Is a selection made in the chart. | |
400 | * | |
401 | * @return true if there is a selection. | |
402 | */ | |
403 | protected boolean isSelected() { | |
404 | return fSelected; | |
405 | } | |
406 | ||
407 | /** | |
408 | * Set the selection index. | |
409 | * | |
410 | * @param selection the index to select. | |
411 | */ | |
412 | protected void setSelection(Set<Integer> selection) { | |
413 | fSelection = selection; | |
414 | fSelected = !selection.isEmpty(); | |
415 | } | |
416 | ||
417 | /** | |
418 | * Unset the chart selection. | |
419 | */ | |
420 | protected void unsetSelection() { | |
421 | fSelection.clear(); | |
422 | fSelected = false; | |
423 | } | |
424 | ||
425 | /** | |
426 | * Get the current selection index. | |
427 | * | |
428 | * @return the current selection index. | |
429 | */ | |
430 | protected Set<Integer> getSelection() { | |
431 | return fSelection; | |
432 | } | |
433 | ||
434 | @Override | |
435 | public @Nullable Control getControl() { | |
436 | return fChart.getParent(); | |
437 | } | |
438 | ||
439 | @Override | |
440 | public void refresh() { | |
441 | Display.getDefault().asyncExec(() -> { | |
442 | if (!fChart.isDisposed()) { | |
443 | fChart.redraw(); | |
444 | } | |
445 | }); | |
446 | } | |
447 | ||
448 | @Override | |
449 | public void dispose() { | |
450 | fChart.dispose(); | |
451 | /* The control's DisposeListener will call super.dispose() */ | |
452 | } | |
453 | ||
454 | /** | |
455 | * Get a list of all the aspect of the Y axis. | |
456 | * | |
457 | * @return The aspects for the Y axis | |
458 | */ | |
459 | protected List<LamiTableEntryAspect> getYAxisAspects() { | |
460 | ||
461 | List<LamiTableEntryAspect> yAxisAspects = new ArrayList<>(); | |
462 | ||
463 | for (String colName : getChartModel().getYSeriesColumns()) { | |
464 | yAxisAspects.add(checkNotNull(getAspectFromName(getResultTable().getTableClass().getAspects(), colName))); | |
465 | } | |
466 | ||
467 | return yAxisAspects; | |
468 | } | |
469 | ||
470 | /** | |
471 | * Get a list of all the aspect of the X axis. | |
472 | * | |
473 | * @return The aspects for the X axis | |
474 | */ | |
475 | protected List<LamiTableEntryAspect> getXAxisAspects() { | |
476 | ||
477 | List<LamiTableEntryAspect> xAxisAspects = new ArrayList<>(); | |
478 | ||
479 | for (String colName : getChartModel().getXSeriesColumns()) { | |
480 | xAxisAspects.add(checkNotNull(getAspectFromName(getResultTable().getTableClass().getAspects(), colName))); | |
481 | } | |
482 | ||
483 | return xAxisAspects; | |
484 | } | |
485 | ||
486 | /** | |
487 | * Set the ITitle object text to a substring of canonicalTitle that when | |
488 | * rendered in the chart will fit maxPixelLength. | |
489 | */ | |
490 | private void refreshDisplayTitle(ITitle title, String canonicalTitle, int maxPixelLength) { | |
491 | if (title.isVisible()) { | |
492 | ||
493 | String newTitle = canonicalTitle; | |
494 | ||
495 | /* Get the title font */ | |
496 | Font font = title.getFont(); | |
497 | ||
498 | GC gc = new GC(fParent); | |
499 | gc.setFont(font); | |
500 | ||
501 | /* Get the length and height of the canonical title in pixels */ | |
502 | Point pixels = gc.stringExtent(canonicalTitle); | |
503 | ||
504 | /* | |
505 | * If the title is too long, generate a shortened version based on the | |
506 | * average character width of the current font. | |
507 | */ | |
508 | if (pixels.x > maxPixelLength) { | |
509 | int charwidth = gc.getFontMetrics().getAverageCharWidth(); | |
510 | ||
511 | int minimum = 3; | |
512 | ||
513 | int strLen = ((maxPixelLength / charwidth) - minimum); | |
514 | ||
515 | if (strLen > minimum) { | |
516 | newTitle = canonicalTitle.substring(0, strLen) + ELLIPSIS; | |
517 | } else { | |
518 | newTitle = ELLIPSIS; | |
519 | } | |
520 | } | |
521 | ||
522 | title.setText(newTitle); | |
523 | ||
524 | // Cleanup | |
525 | gc.dispose(); | |
526 | } | |
527 | } | |
528 | ||
529 | /** | |
530 | * Refresh the Chart, XAxis and YAxis titles to fit the current | |
531 | * chart size. | |
532 | */ | |
533 | private void refreshDisplayTitles() { | |
534 | Rectangle chartRect = fChart.getClientArea(); | |
535 | Rectangle plotRect = fChart.getPlotArea().getClientArea(); | |
536 | ||
537 | ITitle chartTitle = checkNotNull(fChart.getTitle()); | |
538 | refreshDisplayTitle(chartTitle, fChartTitle, chartRect.width); | |
539 | ||
540 | ITitle xTitle = checkNotNull(fChart.getAxisSet().getXAxis(0).getTitle()); | |
541 | refreshDisplayTitle(xTitle, fXTitle, plotRect.width); | |
542 | ||
543 | ITitle yTitle = checkNotNull(fChart.getAxisSet().getYAxis(0).getTitle()); | |
544 | refreshDisplayTitle(yTitle, fYTitle, plotRect.height); | |
545 | } | |
546 | ||
547 | /** | |
548 | * Get the aspect with the given name | |
549 | * | |
550 | * @param aspects | |
551 | * The list of aspects to search into | |
552 | * @param aspectName | |
553 | * The name of the aspect we are looking for | |
554 | * @return The corresponding aspect | |
555 | */ | |
556 | protected static @Nullable LamiTableEntryAspect getAspectFromName(List<LamiTableEntryAspect> aspects, String aspectName) { | |
557 | for (LamiTableEntryAspect lamiTableEntryAspect : aspects) { | |
558 | ||
559 | if (lamiTableEntryAspect.getLabel().equals(aspectName)) { | |
560 | return lamiTableEntryAspect; | |
561 | } | |
562 | } | |
563 | ||
564 | return null; | |
565 | } | |
566 | ||
567 | /** | |
568 | * Refresh the axis labels to fit the current chart size. | |
569 | */ | |
570 | protected abstract void refreshDisplayLabels(); | |
571 | ||
572 | /** | |
573 | * Redraw the chart. | |
574 | */ | |
575 | protected void redraw() { | |
576 | refresh(); | |
577 | } | |
578 | ||
579 | /** | |
580 | * Signal handler for selection update. | |
581 | * | |
582 | * @param signal | |
583 | * The selection update signal | |
584 | */ | |
585 | @TmfSignalHandler | |
586 | public void updateSelection(LamiSelectionUpdateSignal signal) { | |
587 | if (getResultTable().hashCode() != signal.getSignalHash() || equals(signal.getSource())) { | |
588 | /* The signal is not for us */ | |
589 | return; | |
590 | } | |
591 | setSelection(signal.getEntryIndex()); | |
592 | ||
593 | redraw(); | |
594 | } | |
595 | } |