org.eclipse.tracecompass.tmf.chart.core,
org.eclipse.tracecompass.tmf.chart.ui
Export-Package: org.eclipse.tracecompass.internal.analysis.lami.ui;x-internal:=true,
- org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format;x-internal:=true,
org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.handler;x-internal:=true,
- org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals;x-internal:=true,
org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;x-internal:=true,
org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;x-internal:=true
Import-Package: com.google.common.collect,
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format;
-
-import java.math.BigDecimal;
-import java.text.FieldPosition;
-
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.tracecompass.common.core.format.DecimalUnitFormat;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers.LamiGraphRange;
-
-/**
- * Decimal formatter for Lami graph
- *
- * Since the graph use normalized internal value the initial (external)
- * representation needs to be obtained. Subsequent formatting is done based on a
- * Double. Loss of precision could occurs based on the size. For now, loss of
- * precision for decimal values is not a big concern. If it ever become one the
- * use of Long while formatting might come in handy.
- *
- * @author Jonathan Rajotte-Julien
- */
-public class LamiDecimalUnitFormat extends DecimalUnitFormat {
-
- /** Maximum amount of digits that can be represented into a double */
- private static final int BIG_DECIMAL_DIVISION_SCALE = 22;
-
- private static final long serialVersionUID = 977671266270661188L;
-
- private final @Nullable LamiGraphRange fInternalRange;
- private final @Nullable LamiGraphRange fExternalRange;
-
- /**
- * Default constructor
- */
- public LamiDecimalUnitFormat() {
- super();
- fInternalRange = null;
- fExternalRange = null;
- }
-
- /**
- * Constructor with internal and external LamiRange for scale transformation
- *
- * @param internalRange
- * The internal range used for graph representation
- *
- * @param externalRange
- * The external (real value) range shown to the user
- */
- public LamiDecimalUnitFormat(@Nullable LamiGraphRange internalRange, @Nullable LamiGraphRange externalRange) {
- super();
- fInternalRange = internalRange;
- fExternalRange = externalRange;
- }
-
- /**
- * Constructor with multiplication factor and internal and external
- * LamiRange for scale transformation.
- *
- * @param factor
- * Multiplication factor to apply to the value
- * @param internalRange
- * The internal range used for graph representation
- * @param externalRange
- * The external (real value) range shown to the user
- */
- public LamiDecimalUnitFormat(double factor, @Nullable LamiGraphRange internalRange, @Nullable LamiGraphRange externalRange) {
- super(factor);
- fInternalRange = internalRange;
- fExternalRange = externalRange;
- }
-
- /**
- * @return the internal range definition
- */
- public @Nullable LamiGraphRange getInternalRange() {
- return fInternalRange;
- }
-
- /**
- * @return the external range definition
- */
- public @Nullable LamiGraphRange getExternalRange() {
- return fExternalRange;
- }
-
- @Override
- public StringBuffer format(@Nullable Object obj, @Nullable StringBuffer toAppendTo, @Nullable FieldPosition pos) {
- if (!(obj instanceof Number) || toAppendTo == null) {
- throw new IllegalArgumentException("Cannot format given Object as a Number: " + obj); //$NON-NLS-1$
- }
-
- @Nullable LamiGraphRange internalRange = fInternalRange;
- @Nullable LamiGraphRange externalRange = fExternalRange;
- if (internalRange == null || externalRange == null) {
- StringBuffer buffer = super.format(obj, toAppendTo, pos);
- return (buffer == null ? new StringBuffer() : buffer);
- }
-
- if (internalRange.getDelta().compareTo(BigDecimal.ZERO) == 0) {
- StringBuffer buffer = super.format(externalRange.getMinimum().doubleValue(), toAppendTo, pos);
- return (buffer == null ? new StringBuffer() : buffer);
- }
-
- if (externalRange.getDelta().compareTo(BigDecimal.ZERO) == 0) {
- StringBuffer buffer = super.format(externalRange.getMinimum().doubleValue(), toAppendTo, pos);
- return (buffer == null ? new StringBuffer() : buffer);
- }
-
- /* Find external value before formatting */
- BigDecimal externalValue = (new BigDecimal(obj.toString()))
- .subtract(internalRange.getMinimum())
- .multiply(externalRange.getDelta())
- .divide(internalRange.getDelta(), BIG_DECIMAL_DIVISION_SCALE, BigDecimal.ROUND_DOWN)
- .add(externalRange.getMinimum());
-
- Double value = externalValue.doubleValue();
- StringBuffer buffer = super.format(value, toAppendTo, pos);
- return (buffer == null ? new StringBuffer() : buffer);
- }
-}
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format;
-
-import java.text.FieldPosition;
-import java.text.Format;
-import java.text.ParsePosition;
-import java.util.Map.Entry;
-
-
-import org.eclipse.jdt.annotation.Nullable;
-
-import com.google.common.collect.BiMap;
-
-/**
- * Format label based on a given Map<String, Integer>
- *
- * @author Jonathan Rajotte-Julien
- */
-public class LamiLabelFormat extends Format {
-
- private static final long serialVersionUID = 4939553034329681316L;
-
- private static final String SWTCHART_EMPTY_LABEL = " "; //$NON-NLS-1$
- private static final String UNKNOWN_REPRESENTATION = "?"; //$NON-NLS-1$
- private final BiMap<@Nullable String, Integer> fMap;
-
- /**
- * Constructor
- *
- * @param map
- * Map of indices to labels
- */
- public LamiLabelFormat(BiMap<@Nullable String, Integer> map) {
- super();
- fMap = map;
- }
-
- @Override
- public @Nullable StringBuffer format(@Nullable Object obj, @Nullable StringBuffer toAppendTo, @Nullable FieldPosition pos) {
- if (obj == null || toAppendTo == null) {
- return new StringBuffer(SWTCHART_EMPTY_LABEL);
- }
-
- Double doubleObj = (Double) obj;
-
- /*
- * Return a string buffer with a space in it since SWT does not like to
- * draw empty strings.
- */
- if ((doubleObj % 1 != 0) || !fMap.containsValue((doubleObj.intValue()))) {
- return new StringBuffer(SWTCHART_EMPTY_LABEL);
- }
-
- for (Entry<@Nullable String, Integer> entry : fMap.entrySet()) {
- /*
- * FIXME: Find if the elements are the same, based on their double
- * value, because SWTChart uses double values so we do the same
- * check. The loss of precision could lead to false positives.
- */
- if (Double.compare(entry.getValue().doubleValue(), doubleObj.doubleValue()) == 0) {
- if (entry.getKey() == null) {
- return new StringBuffer(UNKNOWN_REPRESENTATION);
- }
- return toAppendTo.append(entry.getKey());
- }
- }
- return new StringBuffer(SWTCHART_EMPTY_LABEL);
- }
-
- @Override
- public @Nullable Object parseObject(@Nullable String source, @Nullable ParsePosition pos) {
- return fMap.get(source);
- }
-
-}
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2015, 2016 EfficiOS Inc. and others
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format;
-
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-
-import java.math.BigDecimal;
-import java.text.FieldPosition;
-import java.text.Format;
-import java.text.ParsePosition;
-
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers.LamiGraphRange;
-import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat;
-
-/**
- * Formatter for time stamps
- */
-public class LamiTimeStampFormat extends Format {
-
- private static final int BIG_DECIMAL_DIVISION_SCALE = 22;
-
- private static final long serialVersionUID = 4285447886537779762L;
-
- private final TmfTimestampFormat fFormat;
-
- private final @Nullable LamiGraphRange fInternalRange;
- private final @Nullable LamiGraphRange fExternalRange;
-
- // ------------------------------------------------------------------------
- // Constructors
- // ------------------------------------------------------------------------
-
- /**
- * The normal constructor
- *
- * @param pattern
- * the format pattern
- * @param internalRange
- * The internal range used for graph representation
- * @param externalRange
- * The external (real value) range shown to the user
- */
- public LamiTimeStampFormat(String pattern, @Nullable LamiGraphRange internalRange, @Nullable LamiGraphRange externalRange) {
- fFormat = new TmfTimestampFormat(pattern);
- fInternalRange = internalRange;
- fExternalRange = externalRange;
- }
-
- // ------------------------------------------------------------------------
- // Operations
- // ------------------------------------------------------------------------
-
- /**
- * @return the internal range definition
- */
- public @Nullable LamiGraphRange getInternalRange() {
- return fInternalRange;
- }
-
- /**
- * @return the external range definition
- */
- public @Nullable LamiGraphRange getExternalRange() {
- return fExternalRange;
- }
-
- @Override
- public StringBuffer format(@Nullable Object obj, @Nullable StringBuffer toAppendTo, @Nullable FieldPosition pos) {
- if (obj != null && obj instanceof Number && toAppendTo != null) {
- @Nullable LamiGraphRange internalRange = fInternalRange;
- @Nullable LamiGraphRange externalRange = fExternalRange;
- if (internalRange == null || externalRange == null) {
- long time = ((Number)obj).longValue();
- return checkNotNull(toAppendTo.append(fFormat.format(time)));
- }
-
- if (internalRange.getDelta().compareTo(BigDecimal.ZERO) == 0 ||
- externalRange.getDelta().compareTo(BigDecimal.ZERO) == 0) {
- return checkNotNull(toAppendTo.append(fFormat.format(externalRange.getMinimum().doubleValue())));
- }
-
- /* Find external value before formatting */
- BigDecimal externalValue = (new BigDecimal(obj.toString()))
- .subtract(internalRange.getMinimum())
- .multiply(externalRange.getDelta())
- .divide(internalRange.getDelta(), BIG_DECIMAL_DIVISION_SCALE, BigDecimal.ROUND_DOWN)
- .add(externalRange.getMinimum());
-
- return checkNotNull(toAppendTo.append(fFormat.format(externalValue.longValue())));
- }
- return new StringBuffer();
- }
-
- @Override
- public @Nullable Object parseObject(@Nullable String source, @Nullable ParsePosition pos) {
- return null;
- }
-
- /**
- * Get the pattern string of the format.
- *
- * @return the pattern string.
- */
- public String getPattern() {
- return fFormat.toPattern();
- }
-}
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-@org.eclipse.jdt.annotation.NonNullByDefault
-package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format;
\ No newline at end of file
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2015, 2016 EfficiOS Inc., Jonathan Rajotte-Julien
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportViewTabPage;
-import org.eclipse.tracecompass.tmf.core.signal.TmfSignal;
-
-/**
- * Enable signal sending on selection inside a LamiViewer implementation.
- *
- * @author Jonathan Rajotte-Julien
- */
-public class LamiSelectionUpdateSignal extends TmfSignal {
-
- private final Set<Integer> fEntryIndexes;
-
- /**
- * Use the {@link LamiReportViewTabPage LamiReportViewTabPage} object as the
- * unique key for LAMI internal signaling. The {@link LamiReportViewTabPage}
- * object is also the synchronization point for LAMI internal signaling.
- */
- private final LamiReportViewTabPage fSignalKey;
-
- /**
- * Constructor for a new signal.
- *
- * @param source
- * The object sending this signal
- * @param entryIndexList
- * The list of selected indices
- * @param signalKey
- * The {@link LamiReportViewTabPage LamiReportViewTabPage} acting
- * as a key for the signal.
- */
- public LamiSelectionUpdateSignal(Object source, Set<Integer> entryIndexList, LamiReportViewTabPage signalKey) {
- super(source);
- fEntryIndexes = new HashSet<>(entryIndexList);
- fSignalKey = signalKey;
- }
-
-
- @Override
- public String toString() {
- return "[" + this.getClass().getSimpleName() + " (" + fEntryIndexes + ")]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- }
-
- /**
- * Getter for the entryIndex
- *
- * @return
- * The new selected entry
- */
- public Set<Integer> getEntryIndex() {
- return fEntryIndexes;
- }
-
-
- /**
- * Getter for the exclusivity key
- *
- * @return
- * The exclusivity key
- */
- public LamiReportViewTabPage getSignalKey() {
- return fSignalKey;
- }
-}
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-@org.eclipse.jdt.annotation.NonNullByDefault
-package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportViewTabPage;
/**
return new LamiTableViewer(tableViewer, page);
}
- /**
- * Factory method to create a new chart viewer. The chart type is specified
- * by the 'chartModel' parameter.
- *
- * @param parent
- * The parent composite
- * @param page
- * The {@link LamiReportViewTabPage} parent page
- * @param chartModel
- * The information about the chart to display
- * @return The new viewer
- */
- static ILamiViewer createLamiChart(Composite parent, LamiReportViewTabPage page, LamiChartModel chartModel) {
- switch (chartModel.getChartType()) {
- case BAR_CHART:
- return new LamiBarChartViewer(parent, page, chartModel);
- case XY_SCATTER:
- return new LamiScatterViewer(parent, page, chartModel);
- case PIE_CHART:
- default:
- throw new UnsupportedOperationException("Unsupported chart type: " + chartModel.toString()); //$NON-NLS-1$
- }
- }
}
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
-
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-
-import java.math.BigDecimal;
-import java.text.Format;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Stream;
-
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.PaintEvent;
-import org.eclipse.swt.events.PaintListener;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.LamiChartType;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportViewTabPage;
-import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
-import org.swtchart.IAxis;
-import org.swtchart.IAxisTick;
-import org.swtchart.IBarSeries;
-import org.swtchart.ISeries;
-import org.swtchart.ISeries.SeriesType;
-import org.swtchart.Range;
-
-import com.google.common.collect.Iterators;
-
-/**
- * Bar chart Viewer for LAMI views.
- *
- * @author Alexandre Montplaisir
- * @author Jonathan Rajotte-Julien
- * @author Mathieu Desnoyers
- */
-public class LamiBarChartViewer extends LamiXYChartViewer {
-
- private static final double LOGSCALE_EPSILON_FACTOR = 100.0;
-
- private class Mapping {
- final private @Nullable Integer fInternalValue;
- final private @Nullable Integer fModelValue;
-
- public Mapping(@Nullable Integer internalValue, @Nullable Integer modelValue) {
- fInternalValue = internalValue;
- fModelValue = modelValue;
- }
-
- public @Nullable Integer getInternalValue() {
- return fInternalValue;
- }
-
- public @Nullable Integer getModelValue() {
- return fModelValue;
- }
- }
-
- private final String[] fCategories;
- private final Map<ISeries, List<Mapping>> fIndexPerSeriesMapping;
- private final Map<LamiTableEntry, Mapping> fEntryToCategoriesMap;
-
- private LamiGraphRange fYInternalRange = new LamiGraphRange(BigDecimal.ZERO, BigDecimal.ONE);
- private LamiGraphRange fYExternalRange;
-
-
- /**
- * Creates a bar chart Viewer instance based on SWTChart.
- *
- * @param parent
- * The parent composite to draw in.
- * @param page
- * The {@link LamiReportViewTabPage} parent page
- * @param chartModel
- * The information about the chart to build
- */
- public LamiBarChartViewer(Composite parent, LamiReportViewTabPage page, LamiChartModel chartModel) {
- super(parent, page, chartModel);
-
- List<LamiTableEntryAspect> xAxisAspects = getXAxisAspects();
- List<LamiTableEntryAspect> yAxisAspects = getYAxisAspects();
-
- /* bar chart cannot deal with multiple X series */
- if (getChartModel().getChartType() != LamiChartType.BAR_CHART && xAxisAspects.size() != 1) {
- throw new IllegalArgumentException("Invalid configuration passed to a bar chart."); //$NON-NLS-1$
- }
-
- /* Enable categories */
- getChart().getAxisSet().getXAxis(0).enableCategory(true);
-
- LamiTableEntryAspect xAxisAspect = xAxisAspects.get(0);
- List<LamiTableEntry> entries = getResultTable().getEntries();
- boolean logscale = chartModel.yAxisIsLog();
- fIndexPerSeriesMapping = new HashMap<>();
- fEntryToCategoriesMap = new HashMap<>();
-
- /* Categories index mapping */
- Format formatter = null;
- if (xAxisAspect.isContinuous()) {
- formatter = getContinuousAxisFormatter(xAxisAspects, entries, null, null);
- }
-
- List<@Nullable String> xCategories = new ArrayList<>();
- for (int i = 0; i < entries.size(); i++) {
- String string = xAxisAspect.resolveString(entries.get(i));
- if (string == null) {
- fEntryToCategoriesMap.put(entries.get(i), new Mapping(null, i));
- continue;
- }
-
- fEntryToCategoriesMap.put(entries.get(i), new Mapping(xCategories.size(), i));
- if (formatter != null) {
- string = formatter.format(xAxisAspect.resolveNumber(entries.get(i)));
- }
-
- xCategories.add(string);
-
- }
- fCategories = xCategories.toArray(new String[0]);
-
- /* The y values range */
- /* Clamp minimum to zero or negative value */
- fYExternalRange = getRange(yAxisAspects, true);
-
- /*
- * Log scale magic course 101:
- *
- * It uses the relative difference divided by a factor
- * (100) to get as close as it can to the actual minimum but still a
- * little bit smaller. This is used as a workaround of SWTCHART
- * limitations regarding custom scale drawing in log scale mode, bogus
- * representation of NaN double values and limited support of multiple
- * size series.
- *
- * This should be good enough for most users.
- */
- double min = Double.MAX_VALUE;
- double max = Double.MIN_VALUE;
- double logScaleEpsilon = ZERO_DOUBLE;
- if (logscale) {
-
- /* Find minimum and maximum values excluding <= 0 values */
- for (LamiTableEntryAspect aspect : yAxisAspects) {
- for (LamiTableEntry entry : entries) {
- Number externalValue = aspect.resolveNumber(entry);
- if (externalValue == null) {
- continue;
- }
- Double value = getInternalDoubleValue(externalValue, fYInternalRange, fYExternalRange);
- if (value <= 0) {
- continue;
- }
- min = Math.min(min, value);
- max = Math.max(max, value);
- }
- }
-
- if (min == Double.MAX_VALUE) {
- /* Series are empty in log scale*/
- return;
- }
-
- double delta = max - min;
- logScaleEpsilon = min - ((min * delta) / (LOGSCALE_EPSILON_FACTOR * max));
- }
-
- for (LamiTableEntryAspect yAxisAspect : yAxisAspects) {
- if (!yAxisAspect.isContinuous() || yAxisAspect.isTimeStamp()) {
- /* Only plot continuous aspects */
- continue;
- }
-
- List<Double> validXValues = new ArrayList<>();
- List<Double> validYValues = new ArrayList<>();
- List<Mapping> indexMapping = new ArrayList<>();
-
- for (int i = 0; i < entries.size(); i++) {
- Integer categoryIndex = checkNotNull(fEntryToCategoriesMap.get(checkNotNull(entries.get(i)))).fInternalValue;
-
- if (categoryIndex == null) {
- /* Invalid value do not show */
- continue;
- }
-
- Double yValue = ZERO_DOUBLE;
- @Nullable Number number = yAxisAspect.resolveNumber(entries.get(i));
-
- if (number == null) {
- /*
- * Null value for y is the same as zero since this is a bar
- * chart
- */
- yValue = ZERO_DOUBLE;
- } else {
- yValue = getInternalDoubleValue(number, fYInternalRange, fYExternalRange);
- }
-
- if (logscale && yValue <= ZERO_DOUBLE) {
- /*
- * Less or equal to 0 values can't be plotted on a log
- * scale. We map them to the mean of the >=0 minimal value
- * and the calculated log scale magic epsilon.
- */
- yValue = (min + logScaleEpsilon) / 2.0;
- }
-
- validXValues.add(checkNotNull(categoryIndex).doubleValue());
- validYValues.add(yValue.doubleValue());
- indexMapping.add(new Mapping(categoryIndex, checkNotNull(fEntryToCategoriesMap.get(checkNotNull(entries.get(i)))).fModelValue));
- }
-
- String name = yAxisAspect.getLabel();
-
- if (validXValues.isEmpty() || validYValues.isEmpty()) {
- /* No need to plot an empty series */
- continue;
- }
-
- IBarSeries barSeries = (IBarSeries) getChart().getSeriesSet().createSeries(SeriesType.BAR, name);
- barSeries.setXSeries(validXValues.stream().mapToDouble(Double::doubleValue).toArray());
- barSeries.setYSeries(validYValues.stream().mapToDouble(Double::doubleValue).toArray());
- fIndexPerSeriesMapping.put(barSeries, indexMapping);
- }
-
- setBarSeriesColors();
-
- /* Set all y axis logscale mode */
- Stream.of(getChart().getAxisSet().getYAxes()).forEach(axis -> axis.enableLogScale(logscale));
-
- /* Set the formatter on the Y axis */
- IAxisTick yTick = getChart().getAxisSet().getYAxis(0).getTick();
- yTick.setFormat(getContinuousAxisFormatter(yAxisAspects, entries, fYInternalRange, fYExternalRange));
-
- /*
- * SWTChart workaround: SWTChart fiddles with tick mark visibility based
- * on the fact that it can parse the label to double or not.
- *
- * If the label happens to be a double, it checks for the presence of
- * that value in its own tick labels to decide if it should add it or
- * not. If it happens that the parsed value is already present in its
- * map, the tick gets a visibility of false.
- *
- * The X axis does not have this problem since SWTCHART checks on label
- * angle, and if it is != 0 simply does no logic regarding visibility.
- * So simply set a label angle of 1 to the axis.
- */
- yTick.setTickLabelAngle(1);
-
- /* Adjust the chart range */
- getChart().getAxisSet().adjustRange();
-
- if (logscale && logScaleEpsilon != max) {
- getChart().getAxisSet().getYAxis(0).setRange(new Range(logScaleEpsilon, max));
- }
-
- /* Once the chart is filled, refresh the axis labels */
- refreshDisplayLabels();
-
- /* Add mouse listener */
- getChart().getPlotArea().addMouseListener(new LamiBarChartMouseDownListener());
-
- /* Custom Painter listener to highlight the current selection */
- getChart().getPlotArea().addPaintListener(new LamiBarChartPainterListener());
- }
-
- private final class LamiBarChartMouseDownListener extends MouseAdapter {
-
- @Override
- public void mouseDown(@Nullable MouseEvent event) {
- if (event == null || event.button != 1) {
- return;
- }
-
- boolean ctrlMode = false;
- int xMouseLocation = event.x;
- int yMouseLocation = event.y;
-
- Set<Integer> selections;
- if ((event.stateMask & SWT.CTRL) != 0) {
- ctrlMode = true;
- selections = getSelection();
- } else {
- /* Reset selection state */
- unsetSelection();
- selections = new HashSet<>();
- }
-
- ISeries[] series = getChart().getSeriesSet().getSeries();
-
- /*
- * Iterate over all series, get the rectangle bounds for each
- * category, and find the category index under the mouse.
- *
- * Since categories map directly to the index of the fResultTable
- * and that this table is immutable the index of the entry
- * corresponds to the categories index. Signal to all LamiViewer and
- * LamiView the update of selection.
- */
- for (ISeries oneSeries : series) {
- IBarSeries barSerie = ((IBarSeries) oneSeries);
- Rectangle[] recs = barSerie.getBounds();
-
- for (int j = 0; j < recs.length; j++) {
- Rectangle rectangle = recs[j];
- if (rectangle.contains(xMouseLocation, yMouseLocation)) {
- int index = getTableEntryIndexFromGraphIndex(checkNotNull(oneSeries), j);
- if (!ctrlMode || (index >= 0 && !selections.remove(index))) {
- selections.add(index);
- }
- }
- }
- }
-
- /* Save the current selection internally */
- setSelection(selections);
- /* Signal all Lami viewers & views of the selection */
- LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(this,
- selections, getPage());
- TmfSignalManager.dispatchSignal(signal);
- redraw();
- }
- }
-
- @Override
- protected void redraw() {
- setBarSeriesColors();
- super.redraw();
- }
-
- /**
- * Set the chart series colors according to the selection state. Use light
- * colors when a selection is present.
- */
- private void setBarSeriesColors() {
- Iterator<Color> colorsIt;
-
- if (isSelected()) {
- colorsIt = Iterators.cycle(LIGHT_COLORS);
- } else {
- colorsIt = Iterators.cycle(COLORS);
- }
-
- for (ISeries series : getChart().getSeriesSet().getSeries()) {
- ((IBarSeries) series).setBarColor(colorsIt.next());
- }
- }
-
- private final class LamiBarChartPainterListener implements PaintListener {
- @Override
- public void paintControl(@Nullable PaintEvent e) {
- if (e == null || !isSelected()) {
- return;
- }
-
- Iterator<Color> colorsIt = Iterators.cycle(COLORS);
- GC gc = e.gc;
-
- for (ISeries series : getChart().getSeriesSet().getSeries()) {
- Color color = colorsIt.next();
- for (int index : getSelection()) {
- int graphIndex = getGraphIndexFromTableEntryIndex(series, index);
- if (graphIndex < 0) {
- /* Invalid index */
- continue;
- }
-
- Rectangle[] bounds = ((IBarSeries) series).getBounds();
- if (bounds.length != fCategories.length) {
- /*
- * The plot is too cramped and SWTChart currently does
- * its best on rectangle drawing and returns the
- * rectangle that it is able to draw.
- *
- * For now we simply do not draw since it is really hard
- * to see anyway. A better way to visualize the value
- * would be a full cross for each selection based on
- * their coordinates.
- */
- continue;
- }
- Rectangle rectangle = bounds[graphIndex];
- gc.setBackground(color);
- gc.fillRectangle(rectangle);
- }
- }
- }
- }
-
- @Override
- protected void refreshDisplayLabels() {
- /* Only if we have at least 1 category */
- if (fCategories.length == 0) {
- return;
- }
-
- /* Only refresh if labels are visible */
- IAxis xAxis = getChart().getAxisSet().getXAxis(0);
- if (!xAxis.getTick().isVisible() || !xAxis.isCategoryEnabled()) {
- return;
- }
-
- /*
- * Shorten all the labels to 5 characters plus "…" when the longest
- * label length is more than 50% of the chart height.
- */
-
- Rectangle rect = getChart().getClientArea();
- int lengthLimit = (int) (rect.height * 0.40);
-
- GC gc = new GC(fParent);
- gc.setFont(xAxis.getTick().getFont());
-
- /* Find the longest category string */
- String longestString = Arrays.stream(fCategories).max(Comparator.comparingInt(String::length)).orElse(fCategories[0]);
-
- /* Get the length and height of the longest label in pixels */
- Point pixels = gc.stringExtent(longestString);
-
- // Completely arbitrary
- int cutLen = 5;
-
- String[] displayCategories = new String[fCategories.length];
- if (pixels.x > lengthLimit) {
- /* We have to cut down some strings */
- for (int i = 0; i < fCategories.length; i++) {
- if (fCategories[i].length() > cutLen) {
- displayCategories[i] = fCategories[i].substring(0, cutLen) + ELLIPSIS;
- } else {
- displayCategories[i] = fCategories[i];
- }
- }
- } else {
- /* All strings should fit */
- displayCategories = Arrays.copyOf(fCategories, fCategories.length);
- }
- xAxis.setCategorySeries(displayCategories);
-
- /* Cleanup */
- gc.dispose();
- }
-
- private int getTableEntryIndexFromGraphIndex(ISeries series, int index) {
- List<Mapping> indexes = fIndexPerSeriesMapping.get(series);
- if (indexes == null || index > indexes.size() || index < 0) {
- return -1;
- }
-
- Mapping mapping = indexes.get(index);
- Integer modelValue = mapping.getModelValue();
- if (modelValue != null) {
- return modelValue.intValue();
- }
- return -1;
- }
-
- private int getGraphIndexFromTableEntryIndex(ISeries series, int index) {
- List<Mapping> indexes = fIndexPerSeriesMapping.get(series);
- if (indexes == null || index < 0) {
- return -1;
- }
-
- int internalIndex = -1;
- for (Mapping mapping : indexes) {
- if (mapping.getModelValue() == index) {
- Integer internalValue = mapping.getInternalValue();
- if (internalValue != null) {
- internalIndex = internalValue.intValue();
- break;
- }
- }
- }
- return internalIndex;
- }
-}
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
-
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-
-import java.math.BigDecimal;
-import java.text.Format;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseMoveListener;
-import org.eclipse.swt.events.PaintEvent;
-import org.eclipse.swt.events.PaintListener;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.LamiChartType;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format.LamiLabelFormat;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format.LamiTimeStampFormat;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportViewTabPage;
-import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
-import org.swtchart.IAxisTick;
-import org.swtchart.ILineSeries;
-import org.swtchart.ISeries;
-import org.swtchart.ISeries.SeriesType;
-import org.swtchart.LineStyle;
-
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import com.google.common.collect.Iterators;
-
-/**
- * XY Scatter chart viewer for Lami views
- *
- * @author Jonathan Rajotte-Julien
- */
-public class LamiScatterViewer extends LamiXYChartViewer {
-
- private static final int SELECTION_SNAP_RANGE_MULTIPLIER = 20;
- private static final int SELECTION_CROSS_SIZE_MULTIPLIER = 3;
-
- private final Map<ISeries, List<Integer>> fIndexMapping;
-
- /* Use a scale from 0 to 1 internally for both axes */
- private LamiGraphRange fXInternalRange = new LamiGraphRange(BigDecimal.ZERO, BigDecimal.ONE);
- private LamiGraphRange fYInternalRange = new LamiGraphRange(BigDecimal.ZERO, BigDecimal.ONE);
-
- private @Nullable LamiGraphRange fXExternalRange = null;
- private @Nullable LamiGraphRange fYExternalRange = null;
-
- /* The current data point for the hovering cross */
- private Point fHoveringCrossDataPoint;
-
- /**
- * Constructor
- *
- * @param parent
- * parent
- * @param page
- * The {@link LamiReportViewTabPage} parent page
- * @param graphModel
- * Model of this chart
- */
- public LamiScatterViewer(Composite parent, LamiReportViewTabPage page, LamiChartModel graphModel) {
- super(parent, page, graphModel);
- if (getChartModel().getChartType() != LamiChartType.XY_SCATTER) {
- throw new IllegalStateException("Chart type not a Scatter Chart " + getChartModel().getChartType().toString()); //$NON-NLS-1$
- }
-
- /* Inspect X series */
- fIndexMapping = new HashMap<>();
-
- fHoveringCrossDataPoint = new Point(-1, -1);
-
- List<LamiTableEntryAspect> xAxisAspects = getXAxisAspects();
- if (xAxisAspects.stream().distinct().count() == 1) {
- LamiTableEntryAspect singleXAspect = xAxisAspects.get(0);
- xAxisAspects.clear();
- xAxisAspects.add(singleXAspect);
- }
-
- BiMap<@Nullable String, Integer> xMap = HashBiMap.create();
- boolean xIsLog = graphModel.xAxisIsLog();
-
- boolean areXAspectsContinuous = areAspectsContinuous(xAxisAspects);
- boolean areXAspectsTimeStamp = areAspectsTimeStamp(xAxisAspects);
-
- /* Check all aspect are the same type */
- for (LamiTableEntryAspect aspect : xAxisAspects) {
- if (aspect.isContinuous() != areXAspectsContinuous) {
- throw new IllegalStateException("Some X aspects are continuous and some are not"); //$NON-NLS-1$
- }
- if (aspect.isTimeStamp() != areXAspectsTimeStamp) {
- throw new IllegalStateException("Some X aspects are time based and some are not"); //$NON-NLS-1$
- }
- }
-
- /*
- * When xAxisAspects are discrete create a map for all values of all
- * series
- */
- if (!areXAspectsContinuous) {
- generateLabelMap(xAxisAspects, checkNotNull(xMap));
- } else {
- /*
- * Always clamp the range to min and max
- *
- * TODO: in the future this could be based on the result of the
- * delta between max and min multiplied by a ratio like it is done in
- * LibreOffice Calc
- */
- fXExternalRange = getRange(xAxisAspects, false);
- }
-
- /*
- * Create Y series
- */
- List<LamiTableEntryAspect> yAxisAspects = getYAxisAspects();
- BiMap<@Nullable String, Integer> yMap = HashBiMap.create();
- boolean yIsLog = graphModel.yAxisIsLog();
-
- boolean areYAspectsContinuous = areAspectsContinuous(yAxisAspects);
- boolean areYAspectsTimeStamp = areAspectsTimeStamp(yAxisAspects);
-
- /* Check all aspect are the same type */
- for (LamiTableEntryAspect aspect : yAxisAspects) {
- if (aspect.isContinuous() != areYAspectsContinuous) {
- throw new IllegalStateException("Some Y aspects are continuous and some are not"); //$NON-NLS-1$
- }
- if (aspect.isTimeStamp() != areYAspectsTimeStamp) {
- throw new IllegalStateException("Some Y aspects are time based and some are not"); //$NON-NLS-1$
- }
- }
-
- /*
- * When yAspects are discrete create a map for all values of all series
- */
- if (!areYAspectsContinuous) {
- generateLabelMap(yAxisAspects, yMap);
- } else {
- /*
- * Only clamp the range to the minimum value if it is a time stamp since
- * plotting from 1970 would make little sense.
- */
- fYExternalRange = getRange(yAxisAspects, areYAspectsTimeStamp);
- }
-
- /* Plot the series */
- int index = 0;
- for (LamiTableEntryAspect yAspect : getYAxisAspects()) {
- String name;
- LamiTableEntryAspect xAspect;
- if (xAxisAspects.size() == 1) {
- /* Always map to the same x series */
- xAspect = xAxisAspects.get(0);
- name = yAspect.getLabel();
- } else {
- xAspect = xAxisAspects.get(index);
- name = (yAspect.getName() + ' ' + Messages.LamiScatterViewer_by + ' ' + xAspect.getName());
- }
-
- List<@Nullable Double> xDoubleSeries;
- List<@Nullable Double> yDoubleSeries;
-
- if (xAspect.isContinuous()) {
- xDoubleSeries = getResultTable().getEntries().stream()
- .map(entry -> {
- Number number = xAspect.resolveNumber(entry);
- if (number != null && fXExternalRange != null) {
- return getInternalDoubleValue(number, fXInternalRange, fXExternalRange);
- }
- return null;
- })
- .collect(Collectors.toList());
- } else {
- xDoubleSeries = getResultTable().getEntries().stream()
- .map(entry -> {
- String string = xAspect.resolveString(entry);
- Integer value = xMap.get(string);
- if (value != null) {
- return Double.valueOf(value.doubleValue());
- }
- return null;
- })
- .collect(Collectors.toList());
- }
-
- if (yAspect.isContinuous()) {
- yDoubleSeries = getResultTable().getEntries().stream()
- .map(entry -> {
- Number number = yAspect.resolveNumber(entry);
- if (number != null && fYExternalRange != null) {
- return getInternalDoubleValue(number, fYInternalRange, fYExternalRange);
- }
- return null;
- })
- .collect(Collectors.toList());
- } else {
- yDoubleSeries = getResultTable().getEntries().stream()
- .map(entry -> {
- String string = yAspect.resolveString(entry);
- Integer value = yMap.get(string);
- if (value != null) {
- return Double.valueOf(value.doubleValue());
- }
- return null;
- })
- .collect(Collectors.toList());
- }
-
- List<@Nullable Double> validXDoubleSeries = new ArrayList<>();
- List<@Nullable Double> validYDoubleSeries = new ArrayList<>();
- List<Integer> indexSeriesCorrespondance = new ArrayList<>();
-
- if (xDoubleSeries.size() != yDoubleSeries.size()) {
- throw new IllegalStateException("Series sizes don't match!"); //$NON-NLS-1$
- }
-
- /* Check for invalid tuple value. Any null elements are invalid */
- for (int i = 0; i < xDoubleSeries.size(); i++) {
- Double xValue = xDoubleSeries.get(i);
- Double yValue = yDoubleSeries.get(i);
- if (xValue == null || yValue == null) {
- /* Reject this tuple */
- continue;
- }
- if ((xIsLog && xValue <= ZERO_DOUBLE) || (yIsLog && yValue <= ZERO_DOUBLE)) {
- /*
- * Equal or less than 0 values can't be plotted on log scale
- */
- continue;
- }
- validXDoubleSeries.add(xValue);
- validYDoubleSeries.add(yValue);
- indexSeriesCorrespondance.add(i);
- }
-
- if (validXDoubleSeries.isEmpty() || validXDoubleSeries.isEmpty()) {
- /* No need to plot an empty series */
- index++;
- continue;
- }
-
- ILineSeries scatterSeries = (ILineSeries) getChart().getSeriesSet().createSeries(SeriesType.LINE, name);
- scatterSeries.setLineStyle(LineStyle.NONE);
-
- double[] xserie = validXDoubleSeries.stream().mapToDouble(elem -> checkNotNull(elem).doubleValue()).toArray();
- double[] yserie = validYDoubleSeries.stream().mapToDouble(elem -> checkNotNull(elem).doubleValue()).toArray();
- scatterSeries.setXSeries(xserie);
- scatterSeries.setYSeries(yserie);
- fIndexMapping.put(scatterSeries, indexSeriesCorrespondance);
- index++;
- }
-
- /* Modify x axis related chart styling */
- IAxisTick xTick = getChart().getAxisSet().getXAxis(0).getTick();
- if (areXAspectsContinuous) {
- Format xAxisFormat = getContinuousAxisFormatter(xAxisAspects, getResultTable().getEntries(), fXInternalRange, fXExternalRange);
-
- xTick.setFormat(xAxisFormat);
-
- if (xAxisFormat instanceof LamiTimeStampFormat) {
- setXUnits(((LamiTimeStampFormat) xAxisFormat).getPattern());
- }
- } else {
- xTick.setFormat(new LamiLabelFormat(checkNotNull(xMap)));
- updateTickMark(checkNotNull(xMap), xTick, getChart().getPlotArea().getSize().x);
-
- /* Remove vertical grid line */
- getChart().getAxisSet().getXAxis(0).getGrid().setStyle(LineStyle.NONE);
- }
-
- /* Modify Y axis related chart styling */
- IAxisTick yTick = getChart().getAxisSet().getYAxis(0).getTick();
- if (areYAspectsContinuous) {
- Format yAxisFormat = getContinuousAxisFormatter(yAxisAspects, getResultTable().getEntries(), fYInternalRange, fYExternalRange);
-
- yTick.setFormat(yAxisFormat);
-
- if (yAxisFormat instanceof LamiTimeStampFormat) {
- setYUnits(((LamiTimeStampFormat) yAxisFormat).getPattern());
- }
- } else {
- yTick.setFormat(new LamiLabelFormat(checkNotNull(yMap)));
- updateTickMark(checkNotNull(yMap), yTick, getChart().getPlotArea().getSize().y);
-
- /* Remove horizontal grid line */
- getChart().getAxisSet().getYAxis(0).getGrid().setStyle(LineStyle.NONE);
- }
-
- /*
- * SWTChart workaround: SWTChart fiddles with tick mark visibility based
- * on the fact that it can parse the label to double or not.
- *
- * If the label happens to be a double, it checks for the presence of
- * that value in its own tick labels to decide if it should add it or
- * not. If it happens that the parsed value is already present in its
- * map, the tick gets a visibility of false.
- *
- * The X axis does not have this problem since SWTCHART checks on label
- * angle, and if it is != 0 simply does no logic regarding visibility.
- * So simply set a label angle of 1 to the axis.
- */
- yTick.setTickLabelAngle(1);
-
- setLineSeriesColor();
-
- /* Put log scale if necessary */
- if (xIsLog && areXAspectsContinuous && !areXAspectsTimeStamp) {
- Stream.of(getChart().getAxisSet().getXAxes()).forEach(axis -> axis.enableLogScale(xIsLog));
- }
-
- if (yIsLog && areYAspectsContinuous && !areYAspectsTimeStamp) {
- /* Set the axis as logscale */
- Stream.of(getChart().getAxisSet().getYAxes()).forEach(axis -> axis.enableLogScale(yIsLog));
- }
- getChart().getAxisSet().adjustRange();
-
- /*
- * Selection listener
- */
- getChart().getPlotArea().addMouseListener(new LamiScatterMouseDownListener());
-
- /*
- * Hovering cross listener
- */
- getChart().getPlotArea().addMouseMoveListener(new HoveringCrossListener());
-
- /*
- * Mouse exit listener: reset state of hovering cross on mouse exit.
- */
- getChart().getPlotArea().addListener(SWT.MouseExit, new Listener() {
-
- @Override
- public void handleEvent(@Nullable Event event) {
- if (event != null) {
- fHoveringCrossDataPoint.x = -1;
- fHoveringCrossDataPoint.y = -1;
- redraw();
- }
- }
- });
-
- /*
- * Selections and hovering cross painting
- */
- getChart().getPlotArea().addPaintListener(new LamiScatterPainterListener());
-
- /* On resize check for axis tick updating */
- getChart().addListener(SWT.Resize, new Listener() {
- @Override
- public void handleEvent(@Nullable Event event) {
- if (yTick.getFormat() instanceof LamiLabelFormat) {
- updateTickMark(checkNotNull(yMap), yTick, getChart().getPlotArea().getSize().y);
- }
- if (xTick.getFormat() instanceof LamiLabelFormat) {
- updateTickMark(checkNotNull(xMap), xTick, getChart().getPlotArea().getSize().x);
- }
- }
- });
- }
-
- private void generateLabelMap(List<LamiTableEntryAspect> aspects, BiMap<@Nullable String, Integer> map) {
- TreeSet<@Nullable String> set = new TreeSet<>();
- for (LamiTableEntryAspect aspect : aspects) {
- for (LamiTableEntry entry : getResultTable().getEntries()) {
- String string = aspect.resolveString(entry);
- if (string != null) {
- set.add(string);
- }
- }
- }
- /* Ordered label mapping to double */
- for (String string : set) {
- map.put(string, map.size());
- }
- }
-
- /**
- * Set the chart series colors.
- */
- private void setLineSeriesColor() {
- Iterator<Color> colorsIt;
-
- colorsIt = Iterators.cycle(COLORS);
-
- for (ISeries series : getChart().getSeriesSet().getSeries()) {
- ((ILineSeries) series).setSymbolColor((colorsIt.next()));
- /*
- * Generate initial array of Color to enable per point color change
- * on selection in the future
- */
- ArrayList<Color> colors = new ArrayList<>();
- for (int i = 0; i < series.getXSeries().length; i++) {
- Color color = ((ILineSeries) series).getSymbolColor();
- colors.add(checkNotNull(color));
- }
- ((ILineSeries) series).setSymbolColors(colors.toArray(new Color[colors.size()]));
- }
- }
-
- // ------------------------------------------------------------------------
- // Listeners
- // ------------------------------------------------------------------------
-
- private final class HoveringCrossListener implements MouseMoveListener {
-
- @Override
- public void mouseMove(@Nullable MouseEvent e) {
- if (e == null) {
- return;
- }
- ISeries[] series = getChart().getSeriesSet().getSeries();
- @Nullable Point closest = null;
- double closestDistance = -1.0;
-
- for (ISeries oneSeries : series) {
- ILineSeries lineSerie = (ILineSeries) oneSeries;
- for (int i = 0; i < lineSerie.getXSeries().length; i++) {
- Point dataPoint = lineSerie.getPixelCoordinates(i);
-
- /*
- * Find the distance between the data point and the mouse
- * location and compare it to the symbol size * the range
- * multiplier, so when a user hovers the mouse near the dot
- * the cursor cross snaps to it.
- */
- int snapRangeRadius = lineSerie.getSymbolSize() * SELECTION_SNAP_RANGE_MULTIPLIER;
-
- /*
- * FIXME if and only if performance of this code is an issue
- * for large sets, this can be accelerated by getting the
- * distance squared, and if it is smaller than
- * snapRangeRadius squared, then check hypot.
- */
- double distance = Math.hypot(dataPoint.x - e.x, dataPoint.y - e.y);
- if (distance < snapRangeRadius) {
- if (closestDistance == -1 || distance < closestDistance) {
- closest = dataPoint;
- closestDistance = distance;
- }
- }
- }
- }
- if (closest != null) {
- fHoveringCrossDataPoint.x = closest.x;
- fHoveringCrossDataPoint.y = closest.y;
- } else {
- fHoveringCrossDataPoint.x = -1;
- fHoveringCrossDataPoint.y = -1;
- }
- refresh();
- }
- }
-
- private final class LamiScatterMouseDownListener extends MouseAdapter {
-
- @Override
- public void mouseDown(@Nullable MouseEvent event) {
- if (event == null || event.button != 1) {
- return;
- }
-
- int xMouseLocation = event.x;
- int yMouseLocation = event.y;
-
- boolean ctrlMode = false;
-
- ISeries[] series = getChart().getSeriesSet().getSeries();
- Set<Integer> selections = getSelection();
-
- /* Check for ctrl on click */
- if ((event.stateMask & SWT.CTRL) != 0) {
- selections = getSelection();
- ctrlMode = true;
- } else {
- /* Reset selection */
- unsetSelection();
- selections = new HashSet<>();
- }
-
- for (ISeries oneSeries : series) {
- ILineSeries lineSerie = (ILineSeries) oneSeries;
-
- int closest = -1;
- double closestDistance = -1;
- for (int i = 0; i < lineSerie.getXSeries().length; i++) {
- Point dataPoint = lineSerie.getPixelCoordinates(i);
-
- /*
- * Find the distance between the data point and the mouse
- * location, and compare it to the symbol size so when a
- * user clicks on a symbol it selects it.
- */
- double distance = Math.hypot(dataPoint.x - xMouseLocation, dataPoint.y - yMouseLocation);
- int snapRangeRadius = lineSerie.getSymbolSize() * SELECTION_SNAP_RANGE_MULTIPLIER;
- if (distance < snapRangeRadius) {
- if (closestDistance == -1 || distance < closestDistance) {
- closest = i;
- closestDistance = distance;
- }
- }
- }
- if (closest != -1) {
- /* Translate to global index */
- int tableEntryIndex = getTableEntryIndexFromGraphIndex(checkNotNull(oneSeries), closest);
- if (tableEntryIndex < 0) {
- continue;
- }
- LamiTableEntry entry = getResultTable().getEntries().get(tableEntryIndex);
- int index = getResultTable().getEntries().indexOf(entry);
-
- if (!ctrlMode || !selections.remove(index)) {
- selections.add(index);
- }
- /* Do no iterate since we already found a match */
- break;
- }
- }
- setSelection(selections);
- /* Signal all Lami viewers & views of the selection */
- LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(this,
- selections, getPage());
- TmfSignalManager.dispatchSignal(signal);
- refresh();
- }
- }
-
- private final class LamiScatterPainterListener implements PaintListener {
-
- @Override
- public void paintControl(@Nullable PaintEvent e) {
- if (e == null) {
- return;
- }
- GC gc = e.gc;
-
- /* Draw the selection */
- drawSelectedDot(checkNotNull(gc));
-
- /* Draw the hovering cross */
- drawHoveringCross(checkNotNull(gc));
- }
-
- private void drawSelectedDot(GC gc) {
- if (isSelected()) {
- Iterator<Color> colorsIt;
- colorsIt = Iterators.cycle(COLORS);
- for (ISeries series : getChart().getSeriesSet().getSeries()) {
-
- /* Get series colors */
- Color color = colorsIt.next();
- int symbolSize = ((ILineSeries) series).getSymbolSize();
-
- for (int index : getInternalSelections()) {
- int graphIndex = getGraphIndexFromTableEntryIndex(series, index);
-
- if (graphIndex < 0) {
- continue;
- }
- Point point = series.getPixelCoordinates(graphIndex);
-
- /* Create a colored dot for selection */
- gc.setBackground(color);
- gc.fillOval(point.x - symbolSize, point.y - symbolSize, symbolSize * 2, symbolSize * 2);
-
- /* Draw cross */
- gc.setLineWidth(2);
- gc.setLineStyle(SWT.LINE_SOLID);
- /* Vertical line */
- int drawingDelta = SELECTION_CROSS_SIZE_MULTIPLIER * symbolSize;
- gc.drawLine(point.x, point.y - drawingDelta, point.x, point.y + drawingDelta);
- /* Horizontal line */
- gc.drawLine(point.x - drawingDelta, point.y, point.x + drawingDelta, point.y);
-
- }
- }
- }
- }
-
- private void drawHoveringCross(GC gc) {
- gc.setLineWidth(1);
- gc.setLineStyle(SWT.LINE_SOLID);
- gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
- gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
- /* Vertical line */
- gc.drawLine(fHoveringCrossDataPoint.x, 0, fHoveringCrossDataPoint.x, getChart().getPlotArea().getSize().y);
- /* Horizontal line */
- gc.drawLine(0, fHoveringCrossDataPoint.y, getChart().getPlotArea().getSize().x, fHoveringCrossDataPoint.y);
- }
- }
-
- // ------------------------------------------------------------------------
- // Utility functions
- // ------------------------------------------------------------------------
-
- private int getTableEntryIndexFromGraphIndex(ISeries series, int index) {
- List<Integer> indexes = fIndexMapping.get(series);
- if (indexes == null || index > indexes.size() || index < 0) {
- return -1;
- }
- return indexes.get(index);
- }
-
- private int getGraphIndexFromTableEntryIndex(ISeries series, int index) {
- List<Integer> indexes = fIndexMapping.get(series);
- if (indexes == null || !indexes.contains(index)) {
- return -1;
- }
- return indexes.indexOf(index);
- }
-
- @Override
- protected void refreshDisplayLabels() {
- }
-
- /**
- * Return the current selection in internal mapping
- *
- * @return the internal selections
- */
- protected Set<Integer> getInternalSelections() {
- /* Translate to internal table location */
- Set<Integer> indexes = super.getSelection();
- Set<Integer> internalIndexes = indexes.stream()
- .mapToInt(index -> getResultTable().getEntries().indexOf((getResultTable().getEntries().get(index))))
- .boxed()
- .collect(Collectors.toSet());
- return internalIndexes;
- }
-
- private static void updateTickMark(BiMap<@Nullable String, Integer> map, IAxisTick tick, int availableLenghtPixel) {
- int nbLabels = Math.max(1, map.size());
- int stepSizePixel = availableLenghtPixel / nbLabels;
- /*
- * This step is a limitation on swtchart side regarding minimal grid
- * step hint size. When the step size are smaller it get defined as the
- * "default" value for the axis instead of the smallest one.
- */
- if (IAxisTick.MIN_GRID_STEP_HINT > stepSizePixel) {
- stepSizePixel = (int) IAxisTick.MIN_GRID_STEP_HINT;
- }
- tick.setTickMarkStepHint(stepSizePixel);
- }
-
- @Override
- protected void setSelection(@NonNull Set<@NonNull Integer> selection) {
- super.setSelection(selection);
-
- /* Set color of selected symbol */
- Iterator<Color> colorsIt = Iterators.cycle(COLORS);
- Iterator<Color> lightColorsIt = Iterators.cycle(LIGHT_COLORS);
-
- Set<Integer> currentSelections = getInternalSelections();
-
- for (ISeries series : getChart().getSeriesSet().getSeries()) {
- /* Series color */
- Color lightColor = lightColorsIt.next();
- Color color = colorsIt.next();
- Color[] colors = ((ILineSeries) series).getSymbolColors();
- if (colors == null) {
- /* Should never happen */
- continue;
- }
-
- if (currentSelections.isEmpty()) {
- /* Put all symbols to the normal colors */
- Arrays.fill(colors, color);
- } else {
- /*
- * Fill with light colors to represent the deselected state. The
- * paint listener is then responsible for drawing the cross and
- * the dark colors for the selection.
- */
- Arrays.fill(colors, lightColor);
- }
- ((ILineSeries) series).setSymbolColors(colors);
- }
- }
-
-}
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportView;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportViewTabPage;
import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.signal.ChartSelectionUpdateSignal;
// ------------------------------------------------------------------------
private final LamiReportViewTabPage fPage;
- private Set<Integer> fSelections;
private Set<Object> fSelection;
// ------------------------------------------------------------------------
public void widgetSelected(@Nullable SelectionEvent event) {
IStructuredSelection selections = getTableViewer().getStructuredSelection();
- // Lami selections
- Set<Integer> selectionIndexes = new HashSet<>();
- for (Object selectedEntry : selections.toArray() ) {
- selectionIndexes.add(fPage.getResultTable().getEntries().indexOf(selectedEntry));
- }
-
- fSelections = selectionIndexes;
-
- /* Signal all Lami viewers & views of the selection */
- LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiTableViewer.this, selectionIndexes, fPage);
- TmfSignalManager.dispatchSignal(signal);
-
/* Find all selected entries */
Set<Object> selectionSet = new HashSet<>();
for (Object selectedEntry : selections.toArray()) {
tableViewer.getTable().moveAbove(null);
fPage = page;
- fSelections = new HashSet<>();
fSelection = new HashSet<>();
/* Default sort order of the content provider is by its first column */
fillData();
}
+ /**
+ * Factory method to create a new Table viewer.
+ *
+ * @param parent
+ * The parent composite
+ * @param page
+ * The {@link LamiReportViewTabPage} parent page
+ * @return The new viewer
+ */
+ public static LamiTableViewer createLamiTable(Composite parent, LamiReportViewTabPage page) {
+ TableViewer tableViewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.MULTI | SWT.VIRTUAL);
+ return new LamiTableViewer(tableViewer, page);
+ }
+
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
* asynchronous task we cannot use the normal signal handler since
* we have no guarantee of time of execution of the fill data.
*/
- if (!fSelections.isEmpty()) {
- int[] selectionsIndexes = fSelections.stream()
- .map(index -> fPage.getResultTable().getEntries().get(index))
- .mapToInt(entry -> ((LamiTableContentProvider) getTableViewer().getContentProvider()).getIndexOf(entry))
- .toArray();
-
- Display.getDefault().asyncExec(() -> {
- getTableViewer().getTable().setSelection(selectionsIndexes);
- getTableViewer().getTable().redraw();
- });
- }
if (!fSelection.isEmpty()) {
LamiTableContentProvider provider = (LamiTableContentProvider) getTableViewer().getContentProvider();
// Signals
// ------------------------------------------------------------------------
- /**
- * The signal handler for selection update.
- *
- * @param signal
- * The selection update signal
- */
- @TmfSignalHandler
- public void updateSelection(LamiSelectionUpdateSignal signal) {
-
- if (fPage != signal.getSignalKey() || equals(signal.getSource())) {
- /* The signal is not for us */
- return;
- }
- /* Fetch the position of the selected entry in the actual table since it could be sorted by another column */
- LamiTableContentProvider latencyContentProvider = (LamiTableContentProvider) getTableViewer().getContentProvider();
-
- Set<Integer> selections = signal.getEntryIndex();
-
- int[] selectionsIndexes = selections.stream()
- .map(index -> fPage.getResultTable().getEntries().get(index))
- .mapToInt(entry -> latencyContentProvider.getIndexOf(entry))
- .toArray();
-
- fSelections = new HashSet<>(selections);
-
- Display.getDefault().asyncExec(() -> {
- getTableViewer().getTable().setSelection(selectionsIndexes);
- getTableViewer().getTable().redraw();
- });
- }
-
/**
* The signal handler for selection update.
*
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2015, 2016 EfficiOS Inc., Michael Jeanson
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
-
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
-
-import java.math.BigDecimal;
-import java.text.Format;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.function.ToDoubleFunction;
-
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.Font;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.swt.widgets.ToolBar;
-import org.eclipse.swt.widgets.ToolItem;
-import org.eclipse.tracecompass.common.core.format.DecimalUnitFormat;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format.LamiDecimalUnitFormat;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format.LamiTimeStampFormat;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportViewTabPage;
-import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
-import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
-import org.eclipse.ui.ISharedImages;
-import org.eclipse.ui.PlatformUI;
-import org.swtchart.Chart;
-import org.swtchart.ITitle;
-
-import com.google.common.collect.ImmutableList;
-
-/**
- * Abstract XYChart Viewer for LAMI views.
- *
- * @author Michael Jeanson
- *
- */
-public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer {
-
- /** Ellipsis character */
- protected static final String ELLIPSIS = "…"; //$NON-NLS-1$
-
- /**
- * String representing unknown values. Can be present even in numerical
- * aspects!
- */
- protected static final String UNKNOWN = "?"; //$NON-NLS-1$
-
- /** Zero long value */
- protected static final long ZERO_LONG = 0L;
- /** Zero double value */
- protected static final double ZERO_DOUBLE = 0.0;
-
- /**
- * Function to use to map Strings read from the data table to doubles for
- * use in SWTChart series.
- */
- protected static final ToDoubleFunction<@Nullable String> DOUBLE_MAPPER = str -> {
- if (str == null || str.equals(UNKNOWN)) {
- return ZERO_LONG;
- }
- return Double.parseDouble(str);
- };
-
- /**
- * List of standard colors
- */
- protected static final List<@NonNull Color> COLORS = ImmutableList.of(
- new Color(Display.getDefault(), 72, 120, 207),
- new Color(Display.getDefault(), 106, 204, 101),
- new Color(Display.getDefault(), 214, 95, 95),
- new Color(Display.getDefault(), 180, 124, 199),
- new Color(Display.getDefault(), 196, 173, 102),
- new Color(Display.getDefault(), 119, 190, 219)
- );
-
- /**
- * List of "light" colors (when unselected)
- */
- protected static final List<@NonNull Color> LIGHT_COLORS = ImmutableList.of(
- new Color(Display.getDefault(), 173, 195, 233),
- new Color(Display.getDefault(), 199, 236, 197),
- new Color(Display.getDefault(), 240, 196, 196),
- new Color(Display.getDefault(), 231, 213, 237),
- new Color(Display.getDefault(), 231, 222, 194),
- new Color(Display.getDefault(), 220, 238, 246)
- );
-
- /**
- * Time stamp formatter pattern for intervals in the days range.
- */
- protected static final String DAYS_FORMATTER_PATTERN = "dd HH:mm"; //$NON-NLS-1$
-
- /**
- * Time stamp formatter pattern for intervals in the hours range.
- */
- protected static final String HOURS_FORMATTER_PATTERN = "HH:mm"; //$NON-NLS-1$
-
- /**
- * Time stamp formatter pattern for intervals in the minutes range.
- */
- protected static final String MINUTES_FORMATTER_PATTERN = "mm:ss"; //$NON-NLS-1$
-
- /**
- * Time stamp formatter pattern for intervals in the seconds range.
- */
- protected static final String SECONDS_FORMATTER_PATTERN = "ss"; //$NON-NLS-1$
-
- /**
- * Time stamp formatter pattern for intervals in the milliseconds range.
- */
- protected static final String MILLISECONDS_FORMATTER_PATTERN = "ss.SSS"; //$NON-NLS-1$
-
- /**
- * Decimal formatter factor to display nanoseconds as seconds.
- */
- protected static final double NANO_TO_SECS_FORMATTER_FACTOR = 0.000000001;
-
- /**
- * Default decimal formatter.
- */
- protected static final DecimalUnitFormat DECIMAL_FORMATTER = new LamiDecimalUnitFormat();
-
- /** Symbol for seconds (used in the custom ns -> s conversion) */
- private static final String SECONDS_SYMBOL = "s"; //$NON-NLS-1$
-
- /** Symbol for nanoseconds (used in the custom ns -> s conversion) */
- private static final String NANOSECONDS_SYMBOL = "ns"; //$NON-NLS-1$
-
- /** Maximum amount of digits that can be represented into a double */
- private static final int BIG_DECIMAL_DIVISION_SCALE = 22;
-
- private final Listener fResizeListener = event -> {
- /* Refresh the titles to fit the current chart size */
- refreshDisplayTitles();
-
- /* Refresh the Axis labels to fit the current chart size */
- refreshDisplayLabels();
- };
-
- private final LamiChartModel fChartModel;
- private final LamiReportViewTabPage fPage;
-
- private final Chart fChart;
-
- private final String fChartTitle;
-
- private String fXLabel;
- private @Nullable String fXUnits;
-
- private String fYLabel;
- private @Nullable String fYUnits;
-
- private boolean fSelected;
- private Set<Integer> fSelection;
-
- private final ToolBar fToolBar;
-
- /**
- * Creates a Viewer instance based on SWTChart.
- *
- * @param parent
- * The parent composite to draw in.
- * @param page
- * The {@link LamiReportViewTabPage} parent page
- * @param chartModel
- * The information about the chart to build
- */
- public LamiXYChartViewer(Composite parent, LamiReportViewTabPage page, LamiChartModel chartModel) {
- super(parent);
-
- fParent = parent;
- fPage = page;
- fChartModel = chartModel;
- fSelection = new HashSet<>();
-
- fXLabel = ""; //$NON-NLS-1$
- fYLabel = ""; //$NON-NLS-1$
-
- fChart = new Chart(parent, SWT.NONE);
- fChart.addListener(SWT.Resize, fResizeListener);
-
- /* Set Chart title */
- fChartTitle = fPage.getResultTable().getTableClass().getTableTitle();
-
- /* Set X axis title */
- if (fChartModel.getXSeriesColumns().size() == 1) {
- /*
- * There is only 1 series in the chart, we will use its name as the
- * X axis.
- */
- innerSetXTitle(getXAxisAspects().get(0).getName(), getXAxisAspects().get(0).getUnits());
- } else {
- /*
- * There are multiple series in the chart, if they all share the same
- * units, display that.
- */
- long nbDiffAspectsUnits = getXAxisAspects().stream()
- .map(aspect -> aspect.getUnits())
- .distinct()
- .count();
-
- long nbDiffAspectName = getXAxisAspects().stream()
- .map(aspect -> aspect.getName())
- .distinct()
- .count();
-
- String xBaseTitle = Messages.LamiViewer_DefaultValueName;
- if (nbDiffAspectName == 1) {
- xBaseTitle = getXAxisAspects().get(0).getName();
- }
-
- String units = null;
- if (nbDiffAspectsUnits == 1) {
- /* All aspects use the same unit type */
- units = getXAxisAspects().get(0).getUnits();
- }
-
- innerSetXTitle(xBaseTitle, units);
- }
-
- /* Set Y axis title */
- if (fChartModel.getYSeriesColumns().size() == 1) {
- /*
- * There is only 1 series in the chart, we will use its name as the
- * Y axis (and hide the legend).
- */
- innerSetYTitle(getYAxisAspects().get(0).getName(), getYAxisAspects().get(0).getUnits());
-
- /* Hide the legend */
- fChart.getLegend().setVisible(false);
- } else {
- /*
- * There are multiple series in the chart, if they all share the same
- * units, display that.
- */
- long nbDiffAspectsUnits = getYAxisAspects().stream()
- .map(aspect -> aspect.getUnits())
- .distinct()
- .count();
-
- long nbDiffAspectName = getYAxisAspects().stream()
- .map(aspect -> aspect.getName())
- .distinct()
- .count();
-
- String yBaseTitle = Messages.LamiViewer_DefaultValueName;
- if (nbDiffAspectName == 1) {
- yBaseTitle = getYAxisAspects().get(0).getName();
- }
-
- String units = null;
- if (nbDiffAspectsUnits == 1) {
- /* All aspects use the same unit type */
- units = getYAxisAspects().get(0).getUnits();
- }
-
- innerSetYTitle(yBaseTitle, units);
-
- /* Put legend at the bottom */
- fChart.getLegend().setPosition(SWT.BOTTOM);
- }
-
- /* Set all titles and labels font color to black */
- fChart.getTitle().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
- fChart.getAxisSet().getXAxis(0).getTitle().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
- fChart.getAxisSet().getYAxis(0).getTitle().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
- fChart.getAxisSet().getXAxis(0).getTick().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
- fChart.getAxisSet().getYAxis(0).getTick().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
-
- /* Set X label 90 degrees */
- fChart.getAxisSet().getXAxis(0).getTick().setTickLabelAngle(90);
-
- /* Refresh the titles to fit the current chart size */
- refreshDisplayTitles();
-
- fToolBar = createChartToolBar();
-
- fChart.addDisposeListener(e -> {
- /* Dispose resources of this class */
- LamiXYChartViewer.super.dispose();
- });
- }
-
- /**
- * Set the Y axis title and refresh the chart.
- *
- * @param label
- * the label string.
- * @param units
- * the units string.
- */
- protected void setYTitle(@Nullable String label, @Nullable String units) {
- innerSetYTitle(label, units);
- }
-
- private void innerSetYTitle(@Nullable String label, @Nullable String units) {
- fYLabel = nullToEmptyString(label);
- innerSetYUnits(units);
- refreshDisplayTitles();
- }
-
- /**
- * Set the units on the Y Axis title and refresh the chart.
- *
- * @param units
- * the units string.
- */
- protected void setYUnits(@Nullable String units) {
- innerSetYUnits(units);
- }
-
- private void innerSetYUnits(@Nullable String units) {
- /*
- * All time durations in the Lami protocol are nanoseconds, on the
- * charts we use an axis formater that converts back to seconds as a
- * base unit and then uses prefixes like nano and milli depending on the
- * range.
- *
- * So set the units to seconds in the title to match the base unit of
- * the formater.
- */
- if (NANOSECONDS_SYMBOL.equals(units)) {
- fYUnits = SECONDS_SYMBOL;
- } else {
- fYUnits = units;
- }
- refreshDisplayTitles();
- }
-
- /**
- * Get the Y axis title string.
- *
- * If the units is non-null, the title will be: "label (units)"
- *
- * If the units is null, the title will be: "label"
- *
- * @return the title of the Y axis.
- */
- protected String getYTitle() {
- if (fYUnits == null) {
- return fYLabel;
- }
- return fYLabel + " (" + fYUnits + ")"; //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- /**
- * Set the X axis title and refresh the chart.
- *
- * @param label
- * the label string.
- * @param units
- * the units string.
- */
- protected void setXTitle(@Nullable String label, @Nullable String units) {
- innerSetXTitle(label, units);
- }
-
- private void innerSetXTitle(@Nullable String label, @Nullable String units) {
- fXLabel = nullToEmptyString(label);
- innerSetXUnits(units);
- refreshDisplayTitles();
- }
-
- /**
- * Set the units on the X Axis title.
- *
- * @param units
- * the units string
- */
- protected void setXUnits(@Nullable String units) {
- innerSetXUnits(units);
- }
-
- private void innerSetXUnits(@Nullable String units) {
- /* The time duration formatter converts ns to s on the axis */
- if (NANOSECONDS_SYMBOL.equals(units)) {
- fXUnits = SECONDS_SYMBOL;
- } else {
- fXUnits = units;
- }
- refreshDisplayTitles();
- }
-
- /**
- * Get the X axis title string.
- *
- * If the units is non-null, the title will be: "label (units)"
- *
- * If the units is null, the title will be: "label"
- *
- * @return the title of the Y axis.
- */
- protected String getXTitle() {
- if (fXUnits == null) {
- return fXLabel;
- }
- return fXLabel + " (" + fXUnits + ")"; //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- /**
- * Util method to check if a list of aspects are all continuous.
- *
- * @param axisAspects
- * The list of aspects to check.
- * @return true is all aspects are continuous, otherwise false.
- */
- protected static boolean areAspectsContinuous(List<LamiTableEntryAspect> axisAspects) {
- return axisAspects.stream().allMatch(aspect -> aspect.isContinuous());
- }
-
- /**
- * Util method to check if a list of aspects are all time stamps.
- *
- * @param axisAspects
- * The list of aspects to check.
- * @return true is all aspects are time stamps, otherwise false.
- */
- protected static boolean areAspectsTimeStamp(List<LamiTableEntryAspect> axisAspects) {
- return axisAspects.stream().allMatch(aspect -> aspect.isTimeStamp());
- }
-
- /**
- * Util method to check if a list of aspects are all time durations.
- *
- * @param axisAspects
- * The list of aspects to check.
- * @return true is all aspects are time durations, otherwise false.
- */
- protected static boolean areAspectsTimeDuration(List<LamiTableEntryAspect> axisAspects) {
- return axisAspects.stream().allMatch(aspect -> aspect.isTimeDuration());
- }
-
- /**
- * Util method that will return a formatter based on the aspects linked to an axis
- *
- * If all aspects are time stamps, return a timestamp formatter tuned to the interval.
- * If all aspects are time durations, return the nanoseconds to seconds formatter.
- * Otherwise, return the generic decimal formatter.
- *
- * @param axisAspects
- * The list of aspects of the axis.
- * @param entries
- * The list of entries of the chart.
- * @param internalRange
- * The internal range for value transformation
- * @param externalRange
- * The external range for value transformation
- * @return a formatter for the axis.
- */
- protected static Format getContinuousAxisFormatter(List<LamiTableEntryAspect> axisAspects, List<LamiTableEntry> entries , @Nullable LamiGraphRange internalRange, @Nullable LamiGraphRange externalRange) {
-
- Format formatter = null;
-
- if (areAspectsTimeStamp(axisAspects)) {
- /* Set a TimeStamp formatter depending on the duration between the first and last value */
- BigDecimal max = new BigDecimal(Long.MIN_VALUE);
- BigDecimal min = new BigDecimal(Long.MAX_VALUE);
-
- for (LamiTableEntry entry : entries) {
- for (LamiTableEntryAspect aspect : axisAspects) {
- @Nullable Number number = aspect.resolveNumber(entry);
- if (number != null) {
- BigDecimal current = new BigDecimal(number.toString());
- max = current.max(max);
- min = current.min(min);
- }
- }
- }
-
- long duration = max.subtract(min).longValue();
- if (duration > TimeUnit.DAYS.toNanos(1)) {
- formatter = new LamiTimeStampFormat(DAYS_FORMATTER_PATTERN, internalRange, externalRange);
- } else if (duration > TimeUnit.HOURS.toNanos(1)) {
- formatter = new LamiTimeStampFormat(HOURS_FORMATTER_PATTERN, internalRange, externalRange);
- } else if (duration > TimeUnit.MINUTES.toNanos(1)) {
- formatter = new LamiTimeStampFormat(MINUTES_FORMATTER_PATTERN, internalRange, externalRange);
- } else if (duration > TimeUnit.SECONDS.toNanos(15)) {
- formatter = new LamiTimeStampFormat(SECONDS_FORMATTER_PATTERN, internalRange, externalRange);
- } else {
- formatter = new LamiTimeStampFormat(MILLISECONDS_FORMATTER_PATTERN, internalRange, externalRange);
- }
- } else if (areAspectsTimeDuration(axisAspects)) {
- /* Set the time duration formatter. */
- formatter = new LamiDecimalUnitFormat(NANO_TO_SECS_FORMATTER_FACTOR, internalRange, externalRange);
- } else {
- /*
- * For other numeric aspects, use the default lami decimal unit
- * formatter.
- */
- formatter = new LamiDecimalUnitFormat(internalRange, externalRange);
- }
-
- return formatter;
- }
-
- /**
- * Get the chart result table.
- *
- * @return The chart result table.
- */
- protected LamiResultTable getResultTable() {
- return fPage.getResultTable();
- }
-
- /**
- * Get the chart {@code LamiReportViewTabPage} parent.
- *
- * @return The {@code LamiReportViewTabPage} parent.
- */
- protected LamiReportViewTabPage getPage() {
- return fPage;
- }
-
- /**
- * Get the chart model.
- *
- * @return The chart model.
- */
- protected LamiChartModel getChartModel() {
- return fChartModel;
- }
-
- /**
- * Get the chart object.
- * @return The chart object.
- */
- protected Chart getChart() {
- return fChart;
- }
-
- /**
- * @return the toolBar
- */
- public ToolBar getToolBar() {
- return fToolBar;
- }
-
- /**
- * Is a selection made in the chart.
- *
- * @return true if there is a selection.
- */
- protected boolean isSelected() {
- return fSelected;
- }
-
- /**
- * Set the selection index.
- *
- * @param selection the index to select.
- */
- protected void setSelection(Set<Integer> selection) {
- fSelection = selection;
- fSelected = !selection.isEmpty();
- }
-
- /**
- * Unset the chart selection.
- */
- protected void unsetSelection() {
- fSelection.clear();
- fSelected = false;
- }
-
- /**
- * Get the current selection index.
- *
- * @return the current selection index.
- */
- protected Set<Integer> getSelection() {
- return fSelection;
- }
-
- @Override
- public @Nullable Control getControl() {
- return fChart.getParent();
- }
-
- @Override
- public void refresh() {
- Display.getDefault().asyncExec(() -> {
- if (!fChart.isDisposed()) {
- fChart.redraw();
- }
- });
- }
-
- @Override
- public void dispose() {
- fChart.dispose();
- /* The control's DisposeListener will call super.dispose() */
- }
-
- /**
- * Get a list of all the aspect of the Y axis.
- *
- * @return The aspects for the Y axis
- */
- protected List<LamiTableEntryAspect> getYAxisAspects() {
-
- List<LamiTableEntryAspect> yAxisAspects = new ArrayList<>();
-
- for (String colName : getChartModel().getYSeriesColumns()) {
- yAxisAspects.add(checkNotNull(getAspectFromName(getResultTable().getTableClass().getAspects(), colName)));
- }
-
- return yAxisAspects;
- }
-
- /**
- * Get a list of all the aspect of the X axis.
- *
- * @return The aspects for the X axis
- */
- protected List<LamiTableEntryAspect> getXAxisAspects() {
-
- List<LamiTableEntryAspect> xAxisAspects = new ArrayList<>();
-
- for (String colName : getChartModel().getXSeriesColumns()) {
- xAxisAspects.add(checkNotNull(getAspectFromName(getResultTable().getTableClass().getAspects(), colName)));
- }
-
- return xAxisAspects;
- }
-
- /**
- * Set the ITitle object text to a substring of canonicalTitle that when
- * rendered in the chart will fit maxPixelLength.
- */
- private void refreshDisplayTitle(ITitle title, String canonicalTitle, int maxPixelLength) {
- if (title.isVisible()) {
-
- String newTitle = canonicalTitle;
-
- /* Get the title font */
- Font font = title.getFont();
-
- GC gc = new GC(fParent);
- gc.setFont(font);
-
- /* Get the length and height of the canonical title in pixels */
- Point pixels = gc.stringExtent(canonicalTitle);
-
- /*
- * If the title is too long, generate a shortened version based on the
- * average character width of the current font.
- */
- if (pixels.x > maxPixelLength) {
- int charwidth = gc.getFontMetrics().getAverageCharWidth();
-
- int minimum = 3;
-
- int strLen = ((maxPixelLength / charwidth) - minimum);
-
- if (strLen > minimum) {
- newTitle = canonicalTitle.substring(0, strLen) + ELLIPSIS;
- } else {
- newTitle = ELLIPSIS;
- }
- }
-
- title.setText(newTitle);
-
- // Cleanup
- gc.dispose();
- }
- }
-
- /**
- * Refresh the Chart, XAxis and YAxis titles to fit the current
- * chart size.
- */
- private void refreshDisplayTitles() {
- Rectangle chartRect = fChart.getClientArea();
- Rectangle plotRect = fChart.getPlotArea().getClientArea();
-
- ITitle chartTitle = checkNotNull(fChart.getTitle());
- refreshDisplayTitle(chartTitle, fChartTitle, chartRect.width);
-
- ITitle xTitle = checkNotNull(fChart.getAxisSet().getXAxis(0).getTitle());
- refreshDisplayTitle(xTitle, getXTitle(), plotRect.width);
-
- ITitle yTitle = checkNotNull(fChart.getAxisSet().getYAxis(0).getTitle());
- refreshDisplayTitle(yTitle, getYTitle(), plotRect.height);
- }
-
- /**
- * Get the aspect with the given name
- *
- * @param aspects
- * The list of aspects to search into
- * @param aspectName
- * The name of the aspect we are looking for
- * @return The corresponding aspect
- */
- protected static @Nullable LamiTableEntryAspect getAspectFromName(List<LamiTableEntryAspect> aspects, String aspectName) {
- for (LamiTableEntryAspect lamiTableEntryAspect : aspects) {
-
- if (lamiTableEntryAspect.getLabel().equals(aspectName)) {
- return lamiTableEntryAspect;
- }
- }
-
- return null;
- }
-
- /**
- * Refresh the axis labels to fit the current chart size.
- */
- protected abstract void refreshDisplayLabels();
-
- /**
- * Redraw the chart.
- */
- protected void redraw() {
- refresh();
- }
-
- /**
- * Signal handler for selection update.
- *
- * @param signal
- * The selection update signal
- */
- @TmfSignalHandler
- public void updateSelection(LamiSelectionUpdateSignal signal) {
- if (getPage() != signal.getSignalKey() || equals(signal.getSource())) {
- /* The signal is not for us */
- return;
- }
- setSelection(signal.getEntryIndex());
-
- redraw();
- }
-
- /**
- * Create a tool bar on top right of the chart. Contained actions:
- * <ul>
- * <li>Dispose the current viewer, also known as "Close the chart"</li>
- * </ul>
- *
- * This tool bar should only appear when the mouse enters the composite.
- *
- * @return the tool bar
- */
- protected ToolBar createChartToolBar() {
- Image removeImage = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_ELCL_REMOVE);
- ToolBar toolBar = new ToolBar(getChart(), SWT.HORIZONTAL);
-
- /* Default state */
- toolBar.moveAbove(null);
- toolBar.setVisible(false);
-
- /*
- * Close chart button
- */
- ToolItem closeButton = new ToolItem(toolBar, SWT.PUSH);
- closeButton.setImage(removeImage);
- closeButton.setToolTipText(Messages.LamiXYChartViewer_CloseChartToolTip);
- closeButton.addSelectionListener(new SelectionListener() {
- @Override
- public void widgetSelected(@Nullable SelectionEvent e) {
- Composite parent = getParent();
- dispose();
- parent.layout();
- }
-
- @Override
- public void widgetDefaultSelected(@Nullable SelectionEvent e) {
- }
- });
-
- toolBar.pack();
- toolBar.setLocation(new Point(getChart().getSize().x - toolBar.getSize().x, 0));
-
- /* Visibility toggle filter */
- Listener toolBarVisibilityToggleListener = e -> {
- if (e.widget instanceof Control) {
- Control control = (Control) e.widget;
- Point display = control.toDisplay(e.x, e.y);
- Point location = getChart().getParent().toControl(display);
-
- /*
- * Only set to visible if we are at the right location, in the
- * right shell.
- */
- boolean visible = getChart().getBounds().contains(location) &&
- control.getShell().equals(getChart().getShell());
- getToolBar().setVisible(visible);
- }
- };
-
- /* Filter to make sure we hide the toolbar if we exit the window */
- Listener hideToolBarListener = (e -> getToolBar().setVisible(false));
-
- /*
- * Add the filters to the main Display, and remove them when we dispose
- * the chart.
- */
- Display display = getChart().getDisplay();
- display.addFilter(SWT.MouseEnter, toolBarVisibilityToggleListener);
- display.addFilter(SWT.MouseExit, hideToolBarListener);
-
- getChart().addDisposeListener(e -> {
- display.removeFilter(SWT.MouseEnter, toolBarVisibilityToggleListener);
- display.removeFilter(SWT.MouseExit, hideToolBarListener);
- });
-
- /* Reposition the tool bar on resize */
- getChart().addListener(SWT.Resize, new Listener() {
- @Override
- public void handleEvent(@Nullable Event event) {
- toolBar.setLocation(new Point(getChart().getSize().x - toolBar.getSize().x, 0));
- }
- });
-
- return toolBar;
- }
-
- /**
- * Get a {@link LamiGraphRange} that covers all data points in the result
- * table.
- * <p>
- * The returned range will be the minimum and maximum of the resolved values
- * of the passed aspects for all result entries. If <code>clampToZero</code>
- * is true, a positive minimum value will be clamped down to zero.
- *
- * @param aspects
- * The aspects that the range will represent
- * @param clampToZero
- * If true, a positive minimum value will be clamped down to zero
- * @return the range
- */
- protected LamiGraphRange getRange(List<LamiTableEntryAspect> aspects, boolean clampToZero) {
- /* Find the minimum and maximum values */
- BigDecimal min = new BigDecimal(Long.MAX_VALUE);
- BigDecimal max = new BigDecimal(Long.MIN_VALUE);
- for (LamiTableEntryAspect lamiTableEntryAspect : aspects) {
- for (LamiTableEntry entry : getResultTable().getEntries()) {
- @Nullable Number number = lamiTableEntryAspect.resolveNumber(entry);
- if (number != null) {
- BigDecimal current = new BigDecimal(number.toString());
- min = current.min(min);
- max = current.max(max);
- }
- }
- }
-
- if (clampToZero) {
- min = min.min(BigDecimal.ZERO);
- }
-
- /* Do not allow a range with a zero delta default to 1 */
- if (max.equals(min)) {
- max = min.add(BigDecimal.ONE);
- }
-
- return new LamiGraphRange(checkNotNull(min), checkNotNull(max));
- }
-
- /**
- * Transform an external value into an internal value. Since SWTChart only
- * support Double and Lami can pass Long values, loss of precision might
- * happen. To minimize this, transform the raw values to an internal
- * representation based on a linear transformation.
- *
- * The internal value =
- *
- * ((rawValue - rawMinimum) * (internalRangeDelta/rawRangeDelta)) +
- * internalMinimum
- *
- * @param number
- * The number to transform
- * @param internalRange
- * The internal range definition to be used
- * @param externalRange
- * The external range definition to be used
- * @return the transformed value in Double comprised inside the internal
- * range
- */
- protected static double getInternalDoubleValue(Number number, LamiGraphRange internalRange, LamiGraphRange externalRange) {
- BigDecimal value = new BigDecimal(number.toString());
-
- if (externalRange.getDelta().compareTo(BigDecimal.ZERO) == 0) {
- return internalRange.getMinimum().doubleValue();
- }
-
- BigDecimal internalValue = value
- .subtract(externalRange.getMinimum())
- .multiply(internalRange.getDelta())
- .divide(externalRange.getDelta(), BIG_DECIMAL_DIVISION_SCALE, BigDecimal.ROUND_DOWN)
- .add(internalRange.getMinimum());
-
- return internalValue.doubleValue();
- }
-}
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.osgi.util.NLS;
-
-/**
- * Message bundle for the package
- *
- * @noreference Messages class
- */
-@NonNullByDefault({})
-@SuppressWarnings("javadoc")
-public class Messages extends NLS {
-
- private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
-
- public static String LamiScatterViewer_by;
-
- public static String LamiXYChartViewer_CloseChartToolTip;
-
- public static String LamiViewer_DefaultValueName;
-
- static {
- NLS.initializeMessages(BUNDLE_NAME, Messages.class);
- }
-
- private Messages() {
- }
-}
+++ /dev/null
-###############################################################################
-# Copyright (c) 2015, 2016 EfficiOS Inc. and others
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Eclipse Public License v1.0
-# which accompanies this distribution, and is available at
-# http://www.eclipse.org/legal/epl-v10.html
-###############################################################################
-
-
-LamiScatterViewer_by = by
-LamiXYChartViewer_CloseChartToolTip = Close chart
-LamiViewer_DefaultValueName = Value
import org.eclipse.swt.widgets.Composite;
import org.eclipse.tracecompass.internal.analysis.lami.ui.Activator;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiAnalysisReport;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.LamiChartType;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
import org.eclipse.tracecompass.tmf.ui.views.TmfView;
}
}
- private class NewChartAction extends Action {
-
- private final LamiChartType fChartType;
-
- public NewChartAction(LamiChartType chartType) {
- fChartType = chartType;
- }
-
- @Override
- public void run() {
- LamiReportViewTabPage page = getCurrentSelectedPage();
- if (page == null) {
- return;
- }
- page.createNewCustomChart(fChartType);
- }
- }
-
private class NewCustomChartAction extends Action {
@Override
toolbarMgr.add(toggleTableAction);
IMenuManager menuMgr = getViewSite().getActionBars().getMenuManager();
- IAction newBarChartAction = new NewChartAction(LamiChartType.BAR_CHART);
- IAction newXYScatterAction = new NewChartAction(LamiChartType.XY_SCATTER);
-
- newBarChartAction.setText(Messages.LamiReportView_NewCustomBarChart);
- newXYScatterAction.setText(Messages.LamiReportView_NewCustomScatterChart);
IAction newChartAction = new NewCustomChartAction();
newChartAction.setText(Messages.LamiReportView_NewCustomChart);
};
clearCustomViewsAction.setText(Messages.LamiReportView_ClearAllCustomViews);
- menuMgr.add(newBarChartAction);
- menuMgr.add(newXYScatterAction);
menuMgr.add(newChartAction);
menuMgr.add(new Separator());
menuMgr.add(clearCustomViewsAction);
package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
-import java.util.List;
import java.util.Set;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jface.viewers.ArrayContentProvider;
-import org.eclipse.jface.viewers.IStructuredContentProvider;
-import org.eclipse.jface.viewers.LabelProvider;
+
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiEmptyAspect;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.LamiChartType;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiXYSeriesDescription;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartData;
import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartModel;
import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.signal.ChartSelectionUpdateSignal;
-import org.eclipse.tracecompass.internal.provisional.tmf.chart.ui.chart.IChartViewer;
import org.eclipse.tracecompass.internal.provisional.tmf.chart.ui.dialog.ChartMakerDialog;
import org.eclipse.tracecompass.tmf.core.component.TmfComponent;
import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
private final Set<LamiViewerControl> fCustomGraphViewerControls = new LinkedHashSet<>();
private final Composite fControl;
- private Set<Integer> fSelectionIndexes;
-
- private @Nullable IChartViewer fChart;
private Set<Object> fSelection;
// ------------------------------------------------------------------------
super(table.getTableClass().getTableTitle());
fResultTable = table;
- fSelectionIndexes = new HashSet<>();
- fSelectionIndexes = getIndexOfEntriesIntersectingTimerange(checkNotNull(fResultTable), TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange());
fControl = parent;
fTableViewerControl.getToggleAction().run();
/* Simulate a new external signal to the default viewer */
- LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, fSelectionIndexes, this);
- TmfSignalManager.dispatchSignal(signal);
ChartSelectionUpdateSignal chartSignal = new ChartSelectionUpdateSignal(this, fResultTable, fSelection);
TmfSignalManager.dispatchSignal(chartSignal);
}
/* Make a chart with the factory constructor */
- fChart = IChartViewer.createChart(fControl, data, model);
+ LamiViewerControl viewerControl = new LamiViewerControl(fControl, data, model);
+ fCustomGraphViewerControls.add(viewerControl);
+ viewerControl.getToggleAction().run();
/* Signal the current selection to the newly created graph */
ChartSelectionUpdateSignal signal = new ChartSelectionUpdateSignal(LamiReportViewTabPage.this,
fResultTable, fSelection);
TmfSignalManager.dispatchSignal(signal);
+
+
}
/**
return fResultTable;
}
- /**
- * Add a new chart viewer to this tab.
- *
- * The method only needs a chart type (currently selected via separate
- * actions), all other information will be found in the result table or in
- * dialogs shown to the user as part of the execution of this method.
- *
- * @param chartType
- * The type of chart to create
- */
- public void createNewCustomChart(LamiChartType chartType) {
- int xLogScaleOptionIndex = -1;
- int yLogScaleOptionIndex = -1;
-
- List<LamiTableEntryAspect> xStringColumn = fResultTable.getTableClass().getAspects().stream()
- .filter(aspect -> !(aspect instanceof LamiEmptyAspect))
- .collect(Collectors.toList());
-
- /* Get the flattened aspects for Y since mapping an aggregate aspect to y series make no sense so far */
- List<LamiTableEntryAspect> yStringColumn = fResultTable.getTableClass().getAspects().stream()
- .filter(aspect -> !(aspect instanceof LamiEmptyAspect))
- .collect(Collectors.toList());
-
- switch (chartType) {
- case BAR_CHART:
- /* Y value must strictly continous and non timestamp */
- yStringColumn = yStringColumn.stream()
- .filter(aspect -> !aspect.isTimeStamp() && aspect.isContinuous())
- .collect(Collectors.toList());
- break;
- case PIE_CHART:
- break;
- case XY_SCATTER:
- break;
- default:
- break;
- }
-
- IStructuredContentProvider contentProvider = checkNotNull(ArrayContentProvider.getInstance());
-
- LamiSeriesDialog dialog = new LamiSeriesDialog(getControl().getShell(),
- chartType,
- xStringColumn,
- yStringColumn,
- contentProvider,
- new LabelProvider() {
- @Override
- public String getText(@Nullable Object element) {
- return ((LamiTableEntryAspect) checkNotNull(element)).getLabel();
- }
- },
- contentProvider,
- new LabelProvider() {
- @Override
- public String getText(@Nullable Object element) {
- return ((LamiTableEntryAspect) checkNotNull(element)).getLabel();
- }
- });
- dialog.setTitle(chartType.toString() + ' ' + Messages.LamiSeriesDialog_creation);
-
- /* X options per chart type */
- switch (chartType) {
- case XY_SCATTER:
- xLogScaleOptionIndex = dialog.addXCheckBoxOption(
- Messages.LamiSeriesDialog_x_axis + ' ' + Messages.LamiReportView_LogScale,
- false, new Predicate<LamiTableEntryAspect>() {
- @Override
- public boolean test(@NonNull LamiTableEntryAspect t) {
- return t.isContinuous() && !t.isTimeStamp();
- }
- });
- break;
- case BAR_CHART:
- case PIE_CHART:
- default:
- break;
- }
-
- /* Y options per chart type */
- switch (chartType) {
- case BAR_CHART:
- case XY_SCATTER:
- yLogScaleOptionIndex = dialog.addYCheckBoxOption(
- Messages.LamiSeriesDialog_y_axis + ' ' + Messages.LamiReportView_LogScale,
- false, new Predicate<LamiTableEntryAspect>() {
- @Override
- public boolean test(@NonNull LamiTableEntryAspect t) {
- return t.isContinuous() && !t.isTimeStamp();
- }
- });
- break;
-
- case PIE_CHART:
- default:
- break;
- }
-
- if (dialog.open() != Window.OK) {
- return;
- }
-
- List<LamiXYSeriesDescription> results = Arrays.stream(dialog.getResult())
- .map(serie -> (LamiXYSeriesDescription) serie)
- .collect(Collectors.toList());
-
- boolean[] xCheckBoxOptionsResults = dialog.getXCheckBoxOptionValues();
- boolean[] yCheckBoxOptionsResults = dialog.getYCheckBoxOptionValues();
-
- boolean isXLogScale = false;
- boolean isYLogScale = false;
-
- /* Get X log scale option */
- if (xLogScaleOptionIndex > -1 && xLogScaleOptionIndex < xCheckBoxOptionsResults.length) {
- isXLogScale = xCheckBoxOptionsResults[xLogScaleOptionIndex];
- }
- /* Get Y log scale option */
- if (yLogScaleOptionIndex > -1 && yLogScaleOptionIndex < yCheckBoxOptionsResults.length) {
- isYLogScale = yCheckBoxOptionsResults[yLogScaleOptionIndex];
- }
-
- List<String> xAxisColString = new ArrayList<>();
- List<String> yAxisColString = new ArrayList<>();
-
- /* Specific chart type result fetching */
- switch (chartType) {
- case PIE_CHART:
- case BAR_CHART:
- /* Validate that we only have 1 X aspect */
- if (results.stream()
- .map(element -> element.getXAspect().getLabel())
- .distinct()
- .count() != 1) {
- throw new IllegalStateException();
- }
- xAxisColString = results.stream()
- .map(element -> element.getXAspect().getLabel())
- .distinct()
- .collect(Collectors.toList());
- break;
- case XY_SCATTER:
- xAxisColString = results.stream()
- .map(element -> element.getXAspect().getLabel())
- .collect(Collectors.toList());
- break;
- default:
- break;
- }
-
- yAxisColString = results.stream()
- .map(element -> element.getYAspect().getLabel())
- .collect(Collectors.toList());
-
- LamiChartModel model = new LamiChartModel(chartType,
- nullToEmptyString(Messages.LamiReportView_Custom),
- xAxisColString,
- yAxisColString,
- isXLogScale,
- isYLogScale);
-
- LamiViewerControl viewerControl = new LamiViewerControl(fControl, this, model);
- fCustomGraphViewerControls.add(viewerControl);
- viewerControl.getToggleAction().run();
-
- /* Signal the current selection to the newly created graph */
- LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this,
- fSelectionIndexes, this);
- TmfSignalManager.dispatchSignal(signal);
- }
-
// ------------------------------------------------------------------------
// Signals
// ------------------------------------------------------------------------
- // Lami signals
- /**
- * Signal handler for selection update.
- * Propagate a TmfSelectionRangeUpdatedSignal if possible.
- *
- * @param signal
- * The selection update signal
- */
- @TmfSignalHandler
- public void updateSelection(LamiSelectionUpdateSignal signal) {
- LamiResultTable table = fResultTable;
- Object source = signal.getSource();
-
- /*
- * Don't forward signals from other tab pages, especially those
- * from other views/tab page.
- */
- if (this != signal.getSignalKey() ||
- this == source ||
- source instanceof LamiReportViewTabPage) {
- /* The signal is not for us */
- return;
- }
-
- Set<Integer> entryIndex = signal.getEntryIndex();
-
- /*
- * Since most of the external viewer deal only with continuous timerange and do not allow multi time range
- * selection simply signal only when only one selection is present.
- */
-
- if (entryIndex.isEmpty()) {
- /*
- * In an ideal world we would send a null signal to reset all view
- * and simply show no selection. But since this is Tracecompass
- * there is no notion of "unselected state" in most of the viewers so
- * we do not update/clear the last timerange and show false information to the user.
- */
- ChartSelectionUpdateSignal customSignal = new ChartSelectionUpdateSignal(LamiReportViewTabPage.this, fResultTable, Collections.EMPTY_SET);
- TmfSignalManager.dispatchSignal(customSignal);
- return;
- }
-
- if (entryIndex.size() == 1) {
- int index = Iterables.getOnlyElement(entryIndex).intValue();
- LamiTimeRange timeRange = table.getEntries().get(index).getCorrespondingTimeRange();
- if (timeRange != null) {
- /* Send Range update to other views */
- // TODO: Consider low and high limits of timestamps here.
- Number tsBeginValueNumber = timeRange.getBegin().getValue();
- Number tsEndValueNumber = timeRange.getEnd().getValue();
-
- if (tsBeginValueNumber != null && tsEndValueNumber != null) {
- ITmfTimestamp start = TmfTimestamp.fromNanos(tsBeginValueNumber.longValue());
- ITmfTimestamp end = TmfTimestamp.fromNanos(tsEndValueNumber.longValue());
- TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(LamiReportViewTabPage.this, start, end));
- }
- }
- }
-
- fSelectionIndexes = entryIndex;
-
- // Create the signal for the custom chart
- List<LamiTableEntry> entries = fResultTable.getEntries();
- Set<Object> selectionSet = new HashSet<>();
- for (Integer selectionIndex : entryIndex) {
- selectionSet.add(entries.get(selectionIndex));
- }
- fSelection = selectionSet;
- ChartSelectionUpdateSignal customSignal = new ChartSelectionUpdateSignal(LamiReportViewTabPage.this, fResultTable, selectionSet);
- TmfSignalManager.dispatchSignal(customSignal);
-
-// /* Update all LamiViewer */
-// LamiSelectionUpdateSignal signal1 = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, selections, this);
-// TmfSignalManager.dispatchSignal(signal1);
- }
-
- private static Set<Integer> getIndexOfEntriesIntersectingTimerange(LamiResultTable table, TmfTimeRange range) {
- Set<Integer> selections = new HashSet<>();
- for (LamiTableEntry entry : table.getEntries()) {
- LamiTimeRange timerange = entry.getCorrespondingTimeRange();
- if (timerange == null) {
- /* Return since the table have no timerange */
- return selections;
- }
-
- // TODO: Consider low and high limits of timestamps here.
- Number tsBeginValueNumber = timerange.getBegin().getValue();
- Number tsEndValueNumber = timerange.getEnd().getValue();
-
- if (tsBeginValueNumber != null && tsEndValueNumber != null) {
- ITmfTimestamp start = TmfTimestamp.fromNanos(tsBeginValueNumber.longValue());
- ITmfTimestamp end = TmfTimestamp.fromNanos(tsEndValueNumber.longValue());
-
- TmfTimeRange tempTimeRange = new TmfTimeRange(start, end);
- if (tempTimeRange.getIntersection(range) != null) {
- selections.add(table.getEntries().indexOf(entry));
- }
- }
- }
- return selections;
- }
-
// Custom chart signals
/**
* Signal handler for a chart selection update. It will try to propagate a
*/
@TmfSignalHandler
public void updateSelection(ChartSelectionUpdateSignal signal) {
- IChartViewer chart = fChart;
- if (chart == null) {
- return;
- }
/* Make sure we are not sending a signal to ourself */
if (signal.getSource() == this) {
/* Find which index row has been selected */
Set<Object> entries = signal.getSelectedObject();
- /*
- * Since most of the external viewer deal only with continuous timerange
- * and do not allow multi time range selection simply signal only when
- * only one selection is present.
- */
- if (entries.isEmpty()) {
- /*
- * In an ideal world we would send a null signal to reset all view
- * and simply show no selection. But since this is Tracecompass
- * there is no notion of "unselected state" in most of the viewers
- * so we do not update/clear the last timerange and show false
- * information to the user.
- */
- /* Signal all Lami viewers & views of the selection */
- LamiSelectionUpdateSignal lamiSignal = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, Collections.EMPTY_SET, this);
- TmfSignalManager.dispatchSignal(lamiSignal);
- return;
- }
-
/* Update the selection */
fSelection = entries;
- // Create the signal for the LAMI selection
- Set<Integer> selectionIndexes = new HashSet<>();
- for (Object entry : entries ) {
- selectionIndexes.add(fResultTable.getEntries().indexOf(entry));
- }
-
- fSelectionIndexes = selectionIndexes;
-
- /* Signal all Lami viewers & views of the selection */
- LamiSelectionUpdateSignal lamiSignal = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, selectionIndexes, this);
- TmfSignalManager.dispatchSignal(lamiSignal);
-
/* Only propagate to all TraceCompass if there is a single selection */
if (entries.size() == 1) {
LamiTableEntry entry = (LamiTableEntry) Iterables.getOnlyElement(entries);
TmfTimeRange range = new TmfTimeRange(signal.getBeginTime(), signal.getEndTime());
- // Lami signal
- Set<Integer> selections = getIndexOfEntriesIntersectingTimerange(fResultTable, range);
-
- /* Update all LamiViewer */
- LamiSelectionUpdateSignal signal1 = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, selections, this);
- TmfSignalManager.dispatchSignal(signal1);
-
// Custom chart signal
/* Find which lami table entry intersects the signal */
Set<Object> selection = getEntriesIntersectingTimerange(fResultTable, range);
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
-
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.jface.dialogs.IDialogConstants;
-import org.eclipse.jface.layout.TableColumnLayout;
-import org.eclipse.jface.viewers.ArrayContentProvider;
-import org.eclipse.jface.viewers.CheckboxTableViewer;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.jface.viewers.ColumnWeightData;
-import org.eclipse.jface.viewers.ILabelProvider;
-import org.eclipse.jface.viewers.IStructuredContentProvider;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.TableViewerColumn;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Group;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.TableItem;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.LamiChartType;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiXYSeriesDescription;
-import org.eclipse.ui.dialogs.SelectionDialog;
-
-/**
- * Series creation dialog
- *
- * @author Jonathan Rajotte-Julien
- */
-public class LamiSeriesDialog extends SelectionDialog {
-
- private static final int MINIMUM_COLUMN_WIDTH = 30;
- private static final int MININAL_SERIES_TABLE_HEIGHT = 150;
-
- /* The root element to populate the viewer with */
- private final Object fXInputElement;
- private final Object fYInputElement;
- private final List<LamiXYSeriesDescription> series;
-
- /* Providers for populating dialog */
- private final ILabelProvider fXLabelProvider;
- private final IStructuredContentProvider fXContentProvider;
- private final ILabelProvider fYLabelProvider;
- private final IStructuredContentProvider fYContentProvider;
- private final IStructuredContentProvider fSeriesContentProvider;
-
- private final boolean fRestrictXSeriesNumbers;
-
- private final List<LamiAxisCheckBoxOption> fXCheckBoxOptions;
- private final List<LamiAxisCheckBoxOption> fYCheckBoxOptions;
-
- // the visual selection widget group
- private TableViewer fXTableViewer;
- private CheckboxTableViewer fYCheckBoxViewer;
- private TableViewer fSeriesListViewer;
-
- private Label fWarning;
-
- /**
- * @param parentShell
- * The parent shell of the dialog
- * @param chartType
- * The chart type for which the dialog construct series
- * @param xInput
- * The possible X axis set of values
- * @param yInput
- * The possible Y axis set of values
- * @param xContentProvider
- * A content provider for the X axis set
- * @param xLabelProvider
- * The label provider for the X axis set
- * @param yContentProvider
- * The content provider for the Y axis set
- * @param yLabelProvider
- * The label provider for the Y axis set
- */
- public LamiSeriesDialog(Shell parentShell, LamiChartType chartType, Object xInput,
- Object yInput,
- IStructuredContentProvider xContentProvider,
- ILabelProvider xLabelProvider,
- IStructuredContentProvider yContentProvider,
- ILabelProvider yLabelProvider) {
- super(parentShell);
- fXInputElement = xInput;
- fYInputElement = yInput;
- fXContentProvider = xContentProvider;
- fXLabelProvider = xLabelProvider;
- fYContentProvider = yContentProvider;
- fYLabelProvider = yLabelProvider;
- series = new ArrayList<>();
- fSeriesContentProvider = checkNotNull(ArrayContentProvider.getInstance());
-
- fXCheckBoxOptions = new ArrayList<>();
- fYCheckBoxOptions = new ArrayList<>();
- fSeriesListViewer = new TableViewer(parentShell);
- fXTableViewer = new TableViewer(parentShell);
- fYCheckBoxViewer = checkNotNull(CheckboxTableViewer.newCheckList(parentShell, SWT.NONE));
-
- /* Dynamic restriction per chart type */
- switch (chartType) {
- case XY_SCATTER:
- fRestrictXSeriesNumbers = false;
- break;
- case BAR_CHART:
- case PIE_CHART:
- default:
- fRestrictXSeriesNumbers = true;
- break;
- }
-
- this.fWarning = new Label(parentShell, SWT.NONE);
- }
-
- @Override
- protected Control createDialogArea(@Nullable Composite parent) {
-
- Composite composite = (Composite) super.createDialogArea(parent);
- initializeDialogUnits(composite);
-
- /* Base 3 column grid layout */
- GridLayout gridLayout = new GridLayout(3, false);
- composite.setLayout(gridLayout);
-
- GridData gridData = new GridData(GridData.FILL_BOTH);
- gridData.horizontalSpan = 3;
- Group seriesGroup = new Group(composite, SWT.NONE);
- seriesGroup.setLayoutData(gridData);
- seriesGroup.setLayout(new GridLayout(3, false));
- seriesGroup.setText(Messages.LamiSeriesDialog_series);
-
- /*
- * New sub group for the series table.
- */
- gridData = new GridData(GridData.FILL_BOTH);
- gridData.horizontalSpan = 2;
- gridData.heightHint = MININAL_SERIES_TABLE_HEIGHT;
- Group seriesTableGroup = new Group(seriesGroup, SWT.NONE);
- seriesTableGroup.setLayoutData(gridData);
- TableColumnLayout layout = new TableColumnLayout();
- seriesTableGroup.setLayout(layout);
-
- /* Current series */
- fSeriesListViewer = new TableViewer(seriesTableGroup, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
- fSeriesListViewer.setContentProvider(fSeriesContentProvider);
- fSeriesListViewer.setInput(series);
- fSeriesListViewer.getTable().setHeaderVisible(true);
- fSeriesListViewer.getTable().setLinesVisible(true);
- TableViewerColumn column1 = createTableViewerColumn(fSeriesListViewer, Messages.LamiSeriesDialog_x_values, element -> element.getXAspect().getLabel());
- TableViewerColumn column2 = createTableViewerColumn(fSeriesListViewer, Messages.LamiSeriesDialog_y_values, element -> element.getYAspect().getLabel());
- layout.setColumnData(column1.getColumn(), new ColumnWeightData(1, MINIMUM_COLUMN_WIDTH, true));
- layout.setColumnData(column2.getColumn(), new ColumnWeightData(1, MINIMUM_COLUMN_WIDTH, true));
-
- /* Delete series button */
- gridData = new GridData(GridData.CENTER);
- gridData.horizontalSpan = 1;
- Button deleteSeries = new Button(seriesGroup, SWT.PUSH);
- deleteSeries.setText(Messages.LamiSeriesDialog_delete);
- deleteSeries.setLayoutData(gridData);
- deleteSeries.addSelectionListener(new SelectionListener() {
- @Override
- public void widgetSelected(@Nullable SelectionEvent e) {
- /* Remove the selectecd series */
- IStructuredSelection selections = (IStructuredSelection) fSeriesListViewer.getSelection();
- for (Object selection : selections.toList()) {
- series.remove(selection);
- }
- /* When table is empty reset to initial state */
- if (series.isEmpty()) {
- /* Make sure the OK button is disabled */
- getButton(IDialogConstants.OK_ID).setEnabled(false);
- /* Hide the selection warning */
- fWarning.setVisible(false);
-
- /*
- * Reset the initial selection of the X axis selection table
- */
- fXTableViewer.refresh();
- /* Reset check boxes options */
- fXCheckBoxOptions.forEach(checkBox -> {
- checkBox.setButtonEnabled(true);
- });
- fYCheckBoxOptions.forEach(checkBox -> {
- checkBox.setButtonEnabled(true);
- });
- }
- /* Refresh the series table to show the added series */
- fSeriesListViewer.refresh();
- }
-
- @Override
- public void widgetDefaultSelected(@Nullable SelectionEvent e) {
- }
- });
-
- /*
- * Series creator subgroup
- */
- gridData = new GridData(GridData.FILL_BOTH);
- gridData.horizontalSpan = 3;
- Group seriesCreatorGroup = new Group(composite, getShellStyle());
- seriesCreatorGroup.setLayoutData(gridData);
- seriesCreatorGroup.setLayout(new GridLayout(3, false));
- seriesCreatorGroup.setText(Messages.LamiSeriesDialog_serie_creator);
-
- /* X axis sash label */
- gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END);
- gridData.horizontalSpan = 1;
- Label xSeriesCreatorLabel = new Label(seriesCreatorGroup, SWT.CENTER);
- xSeriesCreatorLabel.setLayoutData(gridData);
- xSeriesCreatorLabel.setText(Messages.LamiSeriesDialog_x_axis);
-
- gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END);
- gridData.horizontalSpan = 1;
- Label ySeriesCreatorLabel = new Label(seriesCreatorGroup, SWT.CENTER);
- ySeriesCreatorLabel.setLayoutData(gridData);
- ySeriesCreatorLabel.setText(Messages.LamiSeriesDialog_y_axis);
-
- /* Empty label for grid layout */
- gridData = new GridData(GridData.FILL_BOTH);
- gridData.horizontalSpan = 1;
- Label emptyLabel = new Label(seriesCreatorGroup, SWT.CENTER);
- emptyLabel.setLayoutData(gridData);
-
- SashForm sash1 = new SashForm(seriesCreatorGroup, SWT.BORDER | SWT.HORIZONTAL);
- gridData = new GridData(GridData.FILL_BOTH);
- gridData.horizontalSpan = 2;
- sash1.setLayoutData(gridData);
- sash1.setVisible(true);
-
- fXTableViewer = new TableViewer(sash1, getTableStyle());
- fXTableViewer.setContentProvider(fXContentProvider);
- fXTableViewer.setLabelProvider(fXLabelProvider);
- fXTableViewer.setInput(fXInputElement);
-
- fYCheckBoxViewer = checkNotNull(CheckboxTableViewer.newCheckList(sash1, SWT.BORDER));
- fYCheckBoxViewer.setLabelProvider(fYLabelProvider);
- fYCheckBoxViewer.setContentProvider(fYContentProvider);
- fYCheckBoxViewer.setInput(fYInputElement);
-
- gridData = new GridData(SWT.FILL, SWT.NONE, true, true);
- gridData.horizontalSpan = 1;
- Button button1 = new Button(seriesCreatorGroup, SWT.PUSH);
- button1.setText(Messages.LamiSeriesDialog_add);
- button1.setLayoutData(gridData);
- button1.addSelectionListener(new SelectionListener() {
-
- @Override
- public void widgetSelected(@Nullable SelectionEvent e) {
- Object[] ySelections = fYCheckBoxViewer.getCheckedElements();
- IStructuredSelection xSelections = (IStructuredSelection) fXTableViewer.getSelection();
- @Nullable Object x = xSelections.getFirstElement();
- if (!(x instanceof LamiTableEntryAspect) || ySelections.length == 0) {
- return;
- }
-
- /* Add selection to series if it doesn not already exist in the list */
- for (Object y : ySelections) {
- if(!(y instanceof LamiTableEntryAspect)) {
- continue;
- }
- LamiXYSeriesDescription serie = new LamiXYSeriesDescription((LamiTableEntryAspect) x, ((LamiTableEntryAspect) y));
- if (!series.contains(serie)) {
- series.add(serie);
- fSeriesListViewer.refresh();
- }
- }
-
- /* Set label warning visible and enable OK button */
- fWarning.setVisible(true);
- getButton(IDialogConstants.OK_ID).setEnabled(true);
-
- /* Update possible X selection based on current series */
- TableItem[] items = fXTableViewer.getTable().getItems();
- Arrays.stream(items).forEach(item -> {
- LamiTableEntryAspect aspect = (LamiTableEntryAspect) item.getData();
- if (!aspect.arePropertiesEqual(series.get(0).getXAspect())) {
- fXTableViewer.remove(aspect);
- }
- if (fRestrictXSeriesNumbers && aspect != (series.get(0).getXAspect())) {
- fXTableViewer.remove(aspect);
- }
- });
-
- /*
- * Disable all checkBox that do not apply to aspects series.
- * Simply take the first one since all series should comply to
- * the same restriction
- */
- fXCheckBoxOptions.forEach(checkBox -> {
- checkBox.setButtonEnabled(checkBox.getPredicate().test(series.get(0).getXAspect()));
- });
- fYCheckBoxOptions.forEach(checkBox -> {
- checkBox.setButtonEnabled(checkBox.getPredicate().test(series.get(0).getYAspect()));
- });
- }
-
- @Override
- public void widgetDefaultSelected(@Nullable SelectionEvent e) {
- }
- });
-
-
- gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END);
- gridData.horizontalSpan = 3;
- fWarning = new Label(seriesCreatorGroup, SWT.LEFT);
- fWarning.setLayoutData(gridData);
- fWarning.setText(Messages.LamiSeriesDialog_selectionRestrictionWarning);
- fWarning.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
- fWarning.setVisible(false);
-
- gridData = new GridData(GridData.FILL_BOTH);
- gridData.horizontalSpan = 3;
- Group optionGroups = new Group(composite, getShellStyle());
- optionGroups.setLayoutData(gridData);
- optionGroups.setLayout(new GridLayout(3, false));
- optionGroups.setText(Messages.LamiSeriesDialog_chart_options);
-
- for (LamiAxisCheckBoxOption checkBox : fXCheckBoxOptions) {
- Button button = new Button(optionGroups, SWT.CHECK);
- button.setSelection(checkBox.getDefaultValue());
- button.setText(checkBox.getName());
- checkBox.setButton(button);
- }
-
- for (LamiAxisCheckBoxOption checkBox : fYCheckBoxOptions) {
- Button button = new Button(optionGroups, SWT.CHECK);
- button.setSelection(checkBox.getDefaultValue());
- button.setText(checkBox.getName());
- checkBox.setButton(button);
- }
-
- fYCheckBoxViewer.getTable().addSelectionListener(new SelectionListener() {
-
- @Override
- public void widgetSelected(@Nullable SelectionEvent e) {
- /* On check */
- if (e != null && e.detail == SWT.CHECK) {
- /* Change possible selection */
- IStructuredSelection selections = (IStructuredSelection) fYCheckBoxViewer.getSelection();
- if (selections.getFirstElement() != null) {
-
- boolean checked = fYCheckBoxViewer.getChecked(selections.getFirstElement());
- /*
- * If just selected look for stuff to disable. If not no
- * need to look for stuff to disable since it was
- * already done before.
- */
- if (checked) {
- TableItem[] items = fYCheckBoxViewer.getTable().getItems();
- Arrays.stream(items).forEach(item -> {
- LamiTableEntryAspect aspect = (LamiTableEntryAspect) item.getData();
- if (!aspect.arePropertiesEqual((LamiTableEntryAspect) checkNotNull(selections.getFirstElement()))) {
- fYCheckBoxViewer.remove(aspect);
- }
- });
- } else if (!checked && fYCheckBoxViewer.getCheckedElements().length == 0 && fSeriesListViewer.getTable().getItemCount() == 0) {
- fYCheckBoxViewer.refresh();
- }
- }
- }
- }
-
- @Override
- public void widgetDefaultSelected(@Nullable SelectionEvent e) {
- }
- });
-
- Dialog.applyDialogFont(composite);
- return composite;
- }
-
- /*
- * Disable OK button on dialog creation.
- */
- @Override
- protected void createButtonsForButtonBar(@Nullable Composite parent) {
- super.createButtonsForButtonBar(parent);
- getButton(IDialogConstants.OK_ID).setEnabled(false);
- }
-
- /**
- * Return the style flags for the table viewer.
- *
- * @return int
- */
- protected int getTableStyle() {
- return SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER;
- }
-
- /**
- * Add check box option for X series.
- *
- * @param name
- * The name of the option. The actual text shown to the user.
- * @param defaultValue
- * The default state of the check box option.
- * @param predicate
- * The predicate to check if the option applies to the given
- * aspect
- * @return The index of the option value in the result table.
- */
- public int addXCheckBoxOption(String name, boolean defaultValue, Predicate<LamiTableEntryAspect> predicate) {
- LamiAxisCheckBoxOption checkBox = new LamiAxisCheckBoxOption(name, defaultValue, predicate);
- fXCheckBoxOptions.add(checkBox);
- return fXCheckBoxOptions.size() - 1;
- }
-
- /**
- * Add check box option for Y series.
- *
- * @param name
- * The name of the option. The actual text shown to the user.
- * @param defaultValue
- * The default state of the check box option.
- * @param predicate
- * The predicate to check if the option applies to the given
- * aspect
- * @return The index of the option value in the result table.
- */
- public int addYCheckBoxOption(String name, boolean defaultValue, Predicate<LamiTableEntryAspect> predicate) {
- LamiAxisCheckBoxOption checkbox = new LamiAxisCheckBoxOption(name, defaultValue, predicate);
- fYCheckBoxOptions.add(checkbox);
- return fYCheckBoxOptions.size() - 1;
- }
-
- /**
- * @return The final values of X series check boxes.
- */
- public boolean[] getXCheckBoxOptionValues() {
- boolean[] selections = new boolean[fXCheckBoxOptions.size()];
- for (int i = 0; i < selections.length; i++) {
- selections[i] = fXCheckBoxOptions.get(i).getValue();
- }
- return selections;
- }
-
- /**
- * @return The final values of Y series check boxes.
- */
- public boolean[] getYCheckBoxOptionValues() {
- boolean[] selections = new boolean[fYCheckBoxOptions.size()];
- for (int i = 0; i < selections.length; i++) {
- selections[i] = fYCheckBoxOptions.get(i).getValue();
- }
- return selections;
- }
-
- @Override
- protected void okPressed() {
- for (LamiAxisCheckBoxOption checkBox : fXCheckBoxOptions) {
- checkBox.updateValue();
- }
- for (LamiAxisCheckBoxOption checkBox : fYCheckBoxOptions) {
- checkBox.updateValue();
- }
- super.okPressed();
- }
-
- @Override
- public Object[] getResult() {
- return series.toArray();
- }
-
- private static <T extends Comparable<T>> TableViewerColumn createTableViewerColumn(TableViewer viewer, String name,
- Function<LamiXYSeriesDescription, T> propertyFunction) {
- TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.CENTER);
- viewerColumn.setLabelProvider(new ColumnLabelProvider() {
- @Override
- public @Nullable String getText(@Nullable Object element) {
- if (element != null) {
- return propertyFunction.apply((LamiXYSeriesDescription) element).toString();
- }
- return null;
- }
- });
-
- TableColumn column = viewerColumn.getColumn();
- column.setText(name);
- return viewerColumn;
- }
-
-}
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.tracecompass.internal.analysis.lami.ui.Activator;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers.ILamiViewer;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers.LamiTableViewer;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartData;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartModel;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartType;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.ui.chart.IChartViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
/**
* Control for Lami viewers.
private final Action fToggleAction;
- private @Nullable ILamiViewer fViewer;
+ private @Nullable TmfViewer fViewer;
/**
* Build a new control for a Lami table viewer.
fToggleAction = new Action() {
@Override
public void run() {
- ILamiViewer viewer = fViewer;
+ TmfViewer viewer = fViewer;
if (viewer == null) {
- fViewer = ILamiViewer.createLamiTable(parent, page);
+ fViewer = LamiTableViewer.createLamiTable(parent, page);
} else {
viewer.dispose();
fViewer = null;
*
* @param parent
* The parent composite
- * @param page
+ * @param data
* The {@link LamiReportViewTabPage} parent page
- * @param graphModel
+ * @param model
* The graph model
*/
- public LamiViewerControl(Composite parent, LamiReportViewTabPage page, LamiChartModel graphModel) {
+ public LamiViewerControl(Composite parent, ChartData data, ChartModel model) {
fToggleAction = new Action() {
@Override
public void run() {
- ILamiViewer viewer = fViewer;
+ TmfViewer viewer = fViewer;
if (viewer == null) {
- fViewer = ILamiViewer.createLamiChart(parent, page, graphModel);
+ fViewer = (TmfViewer) IChartViewer.createChart(parent, data, model);
} else {
viewer.dispose();
fViewer = null;
parent.layout();
}
};
- fToggleAction.setText(Messages.LamiReportView_ToggleAction_ButtonNamePrefix + ' ' + graphModel.getName());
+ fToggleAction.setText(Messages.LamiReportView_ToggleAction_ButtonNamePrefix + ' ' + model.getTitle());
fToggleAction.setToolTipText(Messages.LamiReportView_ToggleAction_ButtonTooltip);
- fToggleAction.setImageDescriptor(getIconForGraphType(graphModel.getChartType()));
+ fToggleAction.setImageDescriptor(getIconForGraphType(model.getChartType()));
}
/**
*
* @return The viewer
*/
- public @Nullable ILamiViewer getViewer() {
+ public @Nullable TmfViewer getViewer() {
return fViewer;
}
}
}
- private static @Nullable ImageDescriptor getIconForGraphType(LamiChartModel.LamiChartType graphType) {
- switch (graphType) {
+ private static @Nullable ImageDescriptor getIconForGraphType(ChartType chartType) {
+ switch (chartType) {
case BAR_CHART:
return Activator.getDefault().getImageDescripterFromPath("icons/histogram.gif"); //$NON-NLS-1$
case PIE_CHART:
- case XY_SCATTER:
+ case SCATTER_CHART:
default:
// FIXME Use other icons
return Activator.getDefault().getImageDescripterFromPath("icons/histogram.gif"); //$NON-NLS-1$
public static String LamiReportView_ToggleAction_ButtonTooltip;
public static String LamiReportView_NewCustomChart;
- public static String LamiReportView_NewCustomBarChart;
- public static String LamiReportView_NewCustomScatterChart;
public static String LamiReportView_ClearAllCustomViews;
- public static String LamiReportView_LogScale;
- public static String LamiReportView_SelectColumnForX;
- public static String LamiReportView_SelectColumnsForCategories;
- public static String LamiReportView_SelectColumnsForSeries;
- public static String LamiReportView_Custom;
-
- public static String LamiSeriesDialog_creation;
- public static String LamiSeriesDialog_add;
- public static String LamiSeriesDialog_chart_options;
- public static String LamiSeriesDialog_delete;
- public static String LamiSeriesDialog_selectionRestrictionWarning;
- public static String LamiSeriesDialog_serie_creator;
- public static String LamiSeriesDialog_series;
- public static String LamiSeriesDialog_x_axis;
- public static String LamiSeriesDialog_x_values;
- public static String LamiSeriesDialog_y_axis;
- public static String LamiSeriesDialog_y_values;
static {
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
LamiReportView_ToggleAction_ButtonTooltip = Toggle showing this graph in the view
LamiReportView_NewCustomChart = New custom chart
-LamiReportView_NewCustomBarChart = New custom bar chart
-LamiReportView_NewCustomScatterChart = New custom scatter chart
LamiReportView_ClearAllCustomViews = Clear all custom views
-LamiReportView_LogScale = Log scale
-LamiReportView_SelectColumnForX = Select the column used for the X axis
-LamiReportView_SelectColumnsForCategories = Select the columns used for the categories
-LamiReportView_SelectColumnsForSeries = Select the columns used for the series
-LamiReportView_Custom = Custom
-
-LamiSeriesDialog_creation = chart series creation
-LamiSeriesDialog_add = Add
-LamiSeriesDialog_chart_options = Chart options
-LamiSeriesDialog_delete = Delete
-LamiSeriesDialog_selectionRestrictionWarning = Note: Selection might be restricted based on type checking of previously selected series
-LamiSeriesDialog_serie_creator = Series creator
-LamiSeriesDialog_series = Series
-LamiSeriesDialog_x_axis = X axis
-LamiSeriesDialog_x_values = X-Values
-LamiSeriesDialog_y_axis = Y axis
-LamiSeriesDialog_y_values = Y-Values