/********************************************************************** * Copyright (c) 2013, 2016 Ericsson, École Polytechnique de Montréal * * 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 * * Contributors: * Bernd Hufmann - Initial API and implementation * Geneviève Bastien - Moved some methods to TmfTimeViewer * Patrick Tasse - Fix setFocus **********************************************************************/ package org.eclipse.tracecompass.tmf.ui.viewers.xycharts; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal; import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal; import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal; import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext; import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; import org.eclipse.tracecompass.tmf.ui.viewers.TmfTimeViewer; import org.swtchart.Chart; import org.swtchart.IAxis; import org.swtchart.ISeries; import org.swtchart.ISeriesSet; import org.swtchart.Range; import com.google.common.annotations.VisibleForTesting; /** * Base class for a XY-Chart based on SWT chart. It provides a methods to define * zoom, selection and tool tip providers. It also provides call backs to be * notified by any changes caused by selection and zoom. * * @author Bernd Hufmann */ public abstract class TmfXYChartViewer extends TmfTimeViewer implements ITmfChartTimeProvider { // ------------------------------------------------------------------------ // Attributes // ------------------------------------------------------------------------ /** The SWT Chart reference */ private Chart fSwtChart; /** The mouse selection provider */ private TmfBaseProvider fMouseSelectionProvider; /** The mouse drag zoom provider */ private TmfBaseProvider fMouseDragZoomProvider; /** The mouse wheel zoom provider */ private TmfBaseProvider fMouseWheelZoomProvider; /** The tooltip provider */ private TmfBaseProvider fToolTipProvider; /** The middle mouse drag provider */ private TmfBaseProvider fMouseDragProvider; /** * Whether or not to send time alignment signals. This should be set to true * for viewers that are part of an aligned view. */ private boolean fSendTimeAlignSignals = false; // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ /** * Constructs a TmfXYChartViewer. * * @param parent * The parent composite * @param title * The title of the viewer * @param xLabel * The label of the xAxis * @param yLabel * The label of the yAXIS */ public TmfXYChartViewer(Composite parent, String title, String xLabel, String yLabel) { super(parent, title); fSwtChart = new Chart(parent, SWT.NONE) { @Override public boolean setFocus() { return fSwtChart.getPlotArea().setFocus(); } }; fSwtChart.getPlotArea().addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { fSwtChart.getPlotArea().setFocus(); } }); IAxis xAxis = fSwtChart.getAxisSet().getXAxis(0); IAxis yAxis = fSwtChart.getAxisSet().getYAxis(0); /* Set the title/labels, or hide them if they are not provided */ if (title == null) { fSwtChart.getTitle().setVisible(false); } else { fSwtChart.getTitle().setText(title); } if (xLabel == null) { xAxis.getTitle().setVisible(false); } else { xAxis.getTitle().setText(xLabel); } if (yLabel == null) { yAxis.getTitle().setVisible(false); } else { yAxis.getTitle().setText(yLabel); } fMouseSelectionProvider = new TmfMouseSelectionProvider(this); fMouseDragZoomProvider = new TmfMouseDragZoomProvider(this); fMouseWheelZoomProvider = new TmfMouseWheelZoomProvider(this); fToolTipProvider = new TmfSimpleTooltipProvider(this); fMouseDragProvider = new TmfMouseDragProvider(this); fSwtChart.addDisposeListener((e) -> { internalDispose(); }); } // ------------------------------------------------------------------------ // Getter/Setters // ------------------------------------------------------------------------ /** * Sets the SWT Chart reference * * @param chart * The SWT chart to set. */ protected void setSwtChart(Chart chart) { fSwtChart = chart; } /** * Gets the SWT Chart reference * * @return the SWT chart to set. */ protected Chart getSwtChart() { return fSwtChart; } /** * Sets a mouse selection provider. An existing provider will be * disposed. Use <code>null</code> to disable the mouse selection provider. * * @param provider * The selection provider to set */ public void setSelectionProvider(TmfBaseProvider provider) { if (fMouseSelectionProvider != null) { fMouseSelectionProvider.dispose(); } fMouseSelectionProvider = provider; } /** * Sets a mouse drag zoom provider. An existing provider will be * disposed. Use <code>null</code> to disable the mouse drag zoom provider. * * @param provider * The mouse drag zoom provider to set */ public void setMouseDragZoomProvider(TmfBaseProvider provider) { if (fMouseDragZoomProvider != null) { fMouseDragZoomProvider.dispose(); } fMouseDragZoomProvider = provider; } /** * Sets a mouse wheel zoom provider. An existing provider will be * disposed. Use <code>null</code> to disable the mouse wheel zoom * provider. * * @param provider * The mouse wheel zoom provider to set */ public void setMouseWheelZoomProvider(TmfBaseProvider provider) { if (fMouseWheelZoomProvider != null) { fMouseWheelZoomProvider.dispose(); } fMouseWheelZoomProvider = provider; } /** * Sets a tooltip provider. An existing provider will be * disposed. Use <code>null</code> to disable the tooltip provider. * * @param provider * The tooltip provider to set */ public void setTooltipProvider(TmfBaseProvider provider) { if (fToolTipProvider != null) { fToolTipProvider.dispose(); } fToolTipProvider = provider; } /** * Sets a mouse drag provider. An existing provider will be * disposed. Use <code>null</code> to disable the mouse drag provider. * * @param provider * The mouse drag provider to set */ public void setMouseDrageProvider(TmfBaseProvider provider) { if (fMouseDragProvider != null) { fMouseDragProvider.dispose(); } fMouseDragProvider = provider; } // ------------------------------------------------------------------------ // ITmfChartTimeProvider // ------------------------------------------------------------------------ @Override public long getTimeOffset() { return getWindowStartTime() - 1; } // ------------------------------------------------------------------------ // ITmfViewer interface // ------------------------------------------------------------------------ @Override public Control getControl() { return fSwtChart; } @Override public void refresh() { fSwtChart.redraw(); } // ------------------------------------------------------------------------ // TmfComponent // ------------------------------------------------------------------------ @Override public void dispose() { fSwtChart.dispose(); } private void internalDispose() { super.dispose(); if (fMouseSelectionProvider != null) { fMouseSelectionProvider.dispose(); } if (fMouseDragZoomProvider != null) { fMouseDragZoomProvider.dispose(); } if (fMouseWheelZoomProvider != null) { fMouseWheelZoomProvider.dispose(); } if (fToolTipProvider != null) { fToolTipProvider.dispose(); } if (fMouseDragProvider != null) { fMouseDragProvider.dispose(); } } // ------------------------------------------------------------------------ // Operations // ------------------------------------------------------------------------ /** * A Method to load a trace into the viewer. * * @param trace * A trace to apply in the viewer */ @Override public void loadTrace(ITmfTrace trace) { super.loadTrace(trace); clearContent(); updateContent(); } /** * Resets the content of the viewer */ @Override public void reset() { super.reset(); setStartTime(0); setEndTime(0); clearContent(); } /** * Method to implement to update the chart content. */ protected abstract void updateContent(); /** * Returns whether or not this chart viewer is dirty. The viewer is * considered dirty if it has yet to completely update its model. * * This method is meant to be used by tests in order to know when it is safe * to proceed. * * @return true if the time graph view has yet to completely update its * model, false otherwise * @since 2.2 */ @VisibleForTesting public boolean isDirty() { if (getTrace() == null) { return false; } TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext(); long startTime = ctx.getWindowRange().getStartTime().toNanos(); long endTime = ctx.getWindowRange().getEndTime().toNanos(); // If the chart viewer hasn't updated all the way to the end of // the window range then it's dirty. A refresh should happen later. return (getWindowStartTime() != startTime || getWindowEndTime() != endTime); } // ------------------------------------------------------------------------ // Signal Handler // ------------------------------------------------------------------------ /** * Signal handler for handling of the time synch signal. * * @param signal * The time synch signal {@link TmfSelectionRangeUpdatedSignal} */ @Override @TmfSignalHandler public void selectionRangeUpdated(TmfSelectionRangeUpdatedSignal signal) { super.selectionRangeUpdated(signal); if (signal != null && (signal.getSource() != this) && (getTrace() != null)) { if (fMouseSelectionProvider != null) { fMouseSelectionProvider.refresh(); } } } /** * Signal handler for handling of the window range signal. * * @param signal * The {@link TmfWindowRangeUpdatedSignal} */ @Override @TmfSignalHandler public void windowRangeUpdated(TmfWindowRangeUpdatedSignal signal) { super.windowRangeUpdated(signal); updateContent(); } /** * Signal handler for handling the signal that notifies about an updated * timestamp format. * * @param signal * The trace updated signal * {@link TmfTimestampFormatUpdateSignal} */ @TmfSignalHandler public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) { fSwtChart.getAxisSet().adjustRange(); fSwtChart.redraw(); } // ------------------------------------------------------------------------ // Helper Methods // ------------------------------------------------------------------------ /** * Clears the view content. */ protected void clearContent() { if (!fSwtChart.isDisposed()) { ISeriesSet set = fSwtChart.getSeriesSet(); ISeries[] series = set.getSeries(); for (int i = 0; i < series.length; i++) { set.deleteSeries(series[i].getId()); } for (IAxis axis: fSwtChart.getAxisSet().getAxes()){ axis.setRange(new Range(0,1)); } fSwtChart.redraw(); } } /** * Returns the current or default display. * * @return the current or default display */ protected static Display getDisplay() { Display display = Display.getCurrent(); // may be null if outside the UI thread if (display == null) { display = Display.getDefault(); } return display; } /** * Get the offset of the point area, relative to the XY chart viewer * control. We consider the point area to be from where the first point * could be drawn to where the last point could be drawn. * * @return the offset in pixels * * @since 1.0 */ public int getPointAreaOffset() { int pixelCoordinate = 0; IAxis[] xAxes = getSwtChart().getAxisSet().getXAxes(); if (xAxes.length > 0) { IAxis axis = xAxes[0]; pixelCoordinate = axis.getPixelCoordinate(axis.getRange().lower); } return getSwtChart().toControl(getSwtChart().getPlotArea().toDisplay(pixelCoordinate, 0)).x; } /** * Get the width of the point area. We consider the point area to be from * where the first point could be drawn to where the last point could be * drawn. The point area differs from the plot area because there might be a * gap between where the plot area start and where the fist point is drawn. * This also matches the width that the use can select. * * @return the width in pixels * * @since 1.0 */ public int getPointAreaWidth() { IAxis[] xAxes = getSwtChart().getAxisSet().getXAxes(); if (xAxes.length > 0) { IAxis axis = xAxes[0]; int x1 = getPointAreaOffset(); int x2 = axis.getPixelCoordinate(axis.getRange().upper); x2 = getSwtChart().toControl(getSwtChart().getPlotArea().toDisplay(x2, 0)).x; int width = x2 - x1; return width; } return getSwtChart().getPlotArea().getSize().x; } /** * Sets whether or not to send time alignment signals. This should be set to * true for viewers that are part of an aligned view. * * @param sendTimeAlignSignals * whether or not to send time alignment signals * @since 1.0 */ public void setSendTimeAlignSignals(boolean sendTimeAlignSignals) { fSendTimeAlignSignals = sendTimeAlignSignals; } /** * Returns whether or not to send time alignment signals. * * @return whether or not to send time alignment signals. * @since 1.0 */ public boolean isSendTimeAlignSignals() { return fSendTimeAlignSignals; } }