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