blob: 7b0d01bca9d0f80c073cbe66987e22098ef94798 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2019 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* William Bourque - Initial API and implementation
* Yuriy Vashchuk - GUI reorganisation, simplification and some related code improvements.
* Yuriy Vashchuk - Histograms optimisation.
* Yuriy Vashchuk - Histogram Canvas Heritage correction
* Francois Chouinard - Cleanup and refactoring
* Francois Chouinard - Moved from LTTng to TMF
* Patrick Tasse - Update for mouse wheel zoom
* Xavier Raynaud - Support multi-trace coloring
*******************************************************************************/
package org.eclipse.tracecompass.tmf.ui.views.histogram;
import java.util.Collection;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
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.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants;
import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType;
import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
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.signal.TmfTimeViewAlignmentInfo;
import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentSignal;
import org.eclipse.tracecompass.tmf.ui.signal.TmfUiSignalThrottler;
import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
import org.eclipse.tracecompass.tmf.ui.views.TmfView;
import org.eclipse.ui.IActionBars;
/**
* The purpose of this view is to provide graphical time distribution statistics about the trace events.
* <p>
* The view is composed of two histograms and two controls:
* <ul>
* <li>an event distribution histogram for the whole trace;
* <li>an event distribution histogram for current time window (window span);
* <li>the timestamp of the currently selected event;
* <li>the window span (size of the time window of the smaller histogram).
* </ul>
* The histograms x-axis show their respective time range.
*
* @version 2.0
* @author Francois Chouinard
*/
public class HistogramView extends TmfView implements ITmfTimeAligned {
// ------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------
/**
* The view ID as defined in plugin.xml
*/
public static final @NonNull String ID = "org.eclipse.linuxtools.tmf.ui.views.histogram"; //$NON-NLS-1$
private static final Image LINK_IMG = Activator.getDefault().getImageFromPath(ITmfImageConstants.IMG_UI_LINK);
private static final int[] DEFAULT_WEIGHTS = {1, 3};
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
// The current trace
private ITmfTrace fTrace;
// Current timestamp/time window - everything in the TIME_SCALE
private long fTraceStartTime;
private long fTraceEndTime;
private long fWindowStartTime;
private long fWindowEndTime;
private long fWindowSpan;
private long fSelectionBeginTime;
private long fSelectionEndTime;
// SashForm
private SashForm fSashForm;
private ScrolledComposite fScrollComposite;
private Composite fTimeControlsComposite;
private Composite fTimeRangeComposite;
private Listener fSashDragListener;
// Time controls
private HistogramTextControl fSelectionStartControl;
private HistogramTextControl fSelectionEndControl;
private HistogramTextControl fTimeSpanControl;
// Link
private Label fLinkButton;
private boolean fLinkState;
// Histogram/request for the full trace range
private FullTraceHistogram fFullTraceHistogram;
private HistogramRequest fFullTraceRequest;
// Histogram/request for the selected time range
private TimeRangeHistogram fTimeRangeHistogram;
private HistogramRequest fTimeRangeRequest;
// Legend area
private Composite fLegendArea;
private Image[] fLegendImages;
// Throttlers for the time sync and time-range sync signals
private final TmfUiSignalThrottler fTimeSyncThrottle;
private final TmfUiSignalThrottler fTimeRangeSyncThrottle;
// Action for toggle showing the lost events
private Action hideLostEventsAction;
// Action for toggle showing the traces
private Action showTraceAction;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Default constructor
*/
public HistogramView() {
super(ID);
fTimeSyncThrottle = new TmfUiSignalThrottler(this, 200);
fTimeRangeSyncThrottle = new TmfUiSignalThrottler(this, 200);
}
@Override
public void dispose() {
if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) {
fTimeRangeRequest.cancel();
}
if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) {
fFullTraceRequest.cancel();
}
fFullTraceHistogram.dispose();
fTimeRangeHistogram.dispose();
fSelectionStartControl.dispose();
fSelectionEndControl.dispose();
fTimeSpanControl.dispose();
disposeLegendImages();
super.dispose();
}
private void disposeLegendImages() {
if (fLegendImages != null) {
for (Image i: fLegendImages) {
i.dispose();
}
}
fLegendImages = null;
}
// ------------------------------------------------------------------------
// TmfView
// ------------------------------------------------------------------------
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
// Control labels
final String selectionStartLabel = Messages.HistogramView_selectionStartLabel;
final String selectionEndLabel = Messages.HistogramView_selectionEndLabel;
final String windowSpanLabel = Messages.HistogramView_windowSpanLabel;
// --------------------------------------------------------------------
// Set the HistogramView layout
// --------------------------------------------------------------------
Composite viewComposite = new Composite(getParentComposite(), SWT.FILL);
GridLayout gridLayout = new GridLayout(1, false);
gridLayout.verticalSpacing = 0;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
viewComposite.setLayout(gridLayout);
// --------------------------------------------------------------------
// Add a sash for time controls and time range histogram
// --------------------------------------------------------------------
/*
* The ScrolledComposite preferred size can be larger than its visible
* width. This affects the preferred width of the SashForm. Set the
* preferred width to 1 to prevent it from affecting the preferred width
* of the view composite.
*/
fSashForm = new SashForm(viewComposite, SWT.NONE) {
@Override
public Point computeSize(int wHint, int hHint) {
Point computedSize = super.computeSize(wHint, hHint);
if (wHint == SWT.DEFAULT) {
return new Point(1, computedSize.y);
}
return computedSize;
}
@Override
public Point computeSize(int wHint, int hHint, boolean changed) {
Point computedSize = super.computeSize(wHint, hHint, changed);
if (wHint == SWT.DEFAULT) {
return new Point(1, computedSize.y);
}
return computedSize;
}
};
GridData gridData = new GridData(GridData.FILL, GridData.FILL, false, true);
fSashForm.setLayoutData(gridData);
// --------------------------------------------------------------------
// Time controls
// --------------------------------------------------------------------
fScrollComposite = new PackedScrolledComposite(fSashForm, SWT.H_SCROLL | SWT.V_SCROLL);
fTimeControlsComposite = new Composite(fScrollComposite, SWT.NONE);
fScrollComposite.setContent(fTimeControlsComposite);
gridLayout = new GridLayout(1, false);
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
fScrollComposite.setLayout(gridLayout);
fScrollComposite.setExpandHorizontal(true);
fScrollComposite.setExpandVertical(true);
gridLayout = new GridLayout(1, false);
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
fTimeControlsComposite.setLayout(gridLayout);
gridData = new GridData(GridData.FILL, GridData.CENTER, false, true);
fTimeControlsComposite.setLayoutData(gridData);
Composite innerComp = new Composite(fTimeControlsComposite, SWT.NONE);
gridLayout = new GridLayout(2, false);
innerComp.setLayout(gridLayout);
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
gridLayout.horizontalSpacing = 5;
gridLayout.verticalSpacing = 1;
gridData = new GridData(GridData.FILL, GridData.CENTER, false, true);
innerComp.setLayoutData(gridData);
Composite selectionGroup = new Composite(innerComp, SWT.BORDER);
gridLayout = new GridLayout(1, false);
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
selectionGroup.setLayout(gridLayout);
gridData = new GridData(GridData.BEGINNING, GridData.CENTER, false, false);
selectionGroup.setLayoutData(gridData);
// Selection start control
gridData = new GridData(GridData.FILL, GridData.CENTER, false, false);
fSelectionStartControl = new HistogramSelectionStartControl(this, selectionGroup, selectionStartLabel, 0L);
fSelectionStartControl.setLayoutData(gridData);
fSelectionStartControl.setValue(Long.MIN_VALUE);
// Selection end control
gridData = new GridData(GridData.FILL, GridData.CENTER, false, false);
fSelectionEndControl = new HistogramSelectionEndControl(this, selectionGroup, selectionEndLabel, 0L);
fSelectionEndControl.setLayoutData(gridData);
fSelectionEndControl.setValue(Long.MIN_VALUE);
// Link button
gridData = new GridData(GridData.BEGINNING, GridData.CENTER, false, false);
fLinkButton = new Label(innerComp, SWT.NONE);
fLinkButton.setImage(LINK_IMG);
fLinkButton.setLayoutData(gridData);
addLinkButtonListeners();
// Window span time control
gridData = new GridData(GridData.FILL, GridData.CENTER, false, false);
fTimeSpanControl = new HistogramTimeRangeControl(this, innerComp, windowSpanLabel, 0L);
fTimeSpanControl.setLayoutData(gridData);
fTimeSpanControl.setValue(Long.MIN_VALUE);
IStatusLineManager statusLineManager = getViewSite().getActionBars().getStatusLineManager();
// --------------------------------------------------------------------
// Time range histogram
// --------------------------------------------------------------------
fTimeRangeComposite = new Composite(fSashForm, SWT.NONE);
gridLayout = new GridLayout(1, true);
gridLayout.marginTop = 0;
gridLayout.marginWidth = 0;
fTimeRangeComposite.setLayout(gridLayout);
// Use remaining horizontal space
gridData = new GridData(GridData.FILL, GridData.FILL, true, true);
fTimeRangeComposite.setLayoutData(gridData);
// Histogram
fTimeRangeHistogram = new TimeRangeHistogram(this, fTimeRangeComposite, true);
fTimeRangeHistogram.setStatusLineManager(statusLineManager);
// --------------------------------------------------------------------
// Full range histogram
// --------------------------------------------------------------------
final Composite fullRangeComposite = new Composite(viewComposite, SWT.FILL);
gridLayout = new GridLayout(1, true);
fullRangeComposite.setLayout(gridLayout);
// Use remaining horizontal space
gridData = new GridData(GridData.FILL, GridData.FILL, true, true, 2, 1);
fullRangeComposite.setLayoutData(gridData);
// Histogram
fFullTraceHistogram = new FullTraceHistogram(this, fullRangeComposite);
fFullTraceHistogram.setStatusLineManager(statusLineManager);
fLegendArea = new Composite(viewComposite, SWT.FILL);
fLegendArea.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, true, false, 2, 1));
fLegendArea.setLayout(new RowLayout());
// Add mouse wheel listener to time span control
MouseWheelListener listener = fFullTraceHistogram.getZoom();
fTimeSpanControl.addMouseWheelListener(listener);
// View Action Handling
contributeToActionBars();
ITmfTrace trace = TmfTraceManager.getInstance().getActiveTrace();
if (trace != null) {
traceSelected(new TmfTraceSelectedSignal(this, trace));
}
fSashForm.setVisible(true);
fSashForm.setWeights(DEFAULT_WEIGHTS);
fTimeControlsComposite.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
// Sashes in a SashForm are being created on layout so add the
// drag listener here
if (fSashDragListener == null) {
for (Control control : fSashForm.getChildren()) {
if (control instanceof Sash) {
fSashDragListener = event -> TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(fSashForm, getTimeViewAlignmentInfo()));
control.removePaintListener(this);
control.addListener(SWT.Selection, fSashDragListener);
// There should be only one sash
break;
}
}
}
}
});
}
@Override
public void setFocus() {
fFullTraceHistogram.fCanvas.setFocus();
}
void refresh() {
getParentComposite().layout(true);
}
/**
* @since 1.0
*/
@Override
public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
if (fSashForm == null) {
return null;
}
return new TmfTimeViewAlignmentInfo(fSashForm.getShell(), fSashForm.toDisplay(0, 0), getTimeAxisOffset());
}
private int getTimeAxisOffset() {
return fScrollComposite.getSize().x + fSashForm.getSashWidth() + fTimeRangeHistogram.getPointAreaOffset();
}
/**
* @since 1.0
*/
@Override
public int getAvailableWidth(int requestedOffset) {
int pointAreaWidth = fTimeRangeHistogram.getPointAreaWidth();
int curTimeAxisOffset = getTimeAxisOffset();
if (pointAreaWidth <= 0) {
pointAreaWidth = fSashForm.getBounds().width - curTimeAxisOffset;
}
int endOffset = curTimeAxisOffset + pointAreaWidth;
GridLayout layout = (GridLayout) fTimeRangeComposite.getLayout();
int endOffsetWithoutMargin = endOffset + layout.marginRight;
int availableWidth = endOffsetWithoutMargin - requestedOffset;
availableWidth = Math.min(fSashForm.getBounds().width, Math.max(0, availableWidth));
return availableWidth;
}
/**
* @since 1.0
*/
@Override
public void performAlign(int offset, int width) {
int total = fSashForm.getBounds().width;
int plotAreaOffset = fTimeRangeHistogram.getPointAreaOffset();
int width1 = Math.max(0, offset - plotAreaOffset - fSashForm.getSashWidth());
int width2 = Math.max(0, total - width1 - fSashForm.getSashWidth());
if (width1 >= 0 && width2 > 0 || width1 > 0 && width2 >= 0) {
fSashForm.setWeights(new int[] { width1, width2 });
fSashForm.layout();
}
// calculate right margin
GridLayout layout = (GridLayout) fTimeRangeComposite.getLayout();
int timeBasedControlsWidth = fTimeRangeComposite.getSize().x;
int marginSize = timeBasedControlsWidth - width - plotAreaOffset;
layout.marginRight = Math.max(0, marginSize);
fTimeRangeComposite.layout();
}
// ------------------------------------------------------------------------
// Accessors
// ------------------------------------------------------------------------
/**
* Returns the current trace handled by the view
*
* @return the current trace
*/
public ITmfTrace getTrace() {
return fTrace;
}
/**
* Returns the time range of the current selected window (base on default time scale).
*
* @return the time range of current selected window.
*/
public TmfTimeRange getTimeRange() {
return new TmfTimeRange(
TmfTimestamp.fromNanos(fWindowStartTime),
TmfTimestamp.fromNanos(fWindowEndTime));
}
/**
* get the show lost events action
*
* @return The action object
*/
public Action getShowLostEventsAction() {
if (hideLostEventsAction == null) {
/* show lost events */
hideLostEventsAction = new Action(Messages.HistogramView_hideLostEvents, IAction.AS_CHECK_BOX) {
@Override
public void run() {
HistogramScaledData.hideLostEvents = hideLostEventsAction.isChecked();
long maxNbEvents = HistogramScaledData.hideLostEvents ? fFullTraceHistogram.fScaledData.fMaxValue : fFullTraceHistogram.fScaledData.fMaxCombinedValue;
fFullTraceHistogram.setMaxNbEvents(maxNbEvents);
maxNbEvents = HistogramScaledData.hideLostEvents ? fTimeRangeHistogram.fScaledData.fMaxValue : fTimeRangeHistogram.fScaledData.fMaxCombinedValue;
fTimeRangeHistogram.setMaxNbEvents(maxNbEvents);
}
};
hideLostEventsAction.setText(Messages.HistogramView_hideLostEvents);
hideLostEventsAction.setToolTipText(Messages.HistogramView_hideLostEvents);
hideLostEventsAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SHOW_LOST_EVENTS));
}
return hideLostEventsAction;
}
/**
* get the show trace action
*
* @return The action object
*/
public Action getShowTraceAction() {
if (showTraceAction == null) {
/* show lost events */
showTraceAction = new Action(Messages.HistogramView_showTraces, IAction.AS_CHECK_BOX) {
@Override
public void run() {
Histogram.showTraces = showTraceAction.isChecked();
fFullTraceHistogram.fCanvas.redraw();
fTimeRangeHistogram.fCanvas.redraw();
updateLegendArea();
}
};
showTraceAction.setChecked(true);
showTraceAction.setText(Messages.HistogramView_showTraces);
showTraceAction.setToolTipText(Messages.HistogramView_showTraces);
showTraceAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SHOW_HIST_TRACES));
}
return showTraceAction;
}
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
/**
* Broadcast TmfSignal about new current selection time range.
* @param beginTime the begin time of current selection.
* @param endTime the end time of current selection.
*/
void updateSelectionTime(long beginTime, long endTime) {
updateDisplayedSelectionTime(beginTime, endTime);
ITmfTimestamp beginTs = TmfTimestamp.fromNanos(beginTime);
ITmfTimestamp endTs = TmfTimestamp.fromNanos(endTime);
TmfSelectionRangeUpdatedSignal signal = new TmfSelectionRangeUpdatedSignal(this, beginTs, endTs, fTrace);
fTimeSyncThrottle.queue(signal);
}
/**
* Get selection begin time
* @return the begin time of current selection
*/
long getSelectionBegin() {
return fSelectionBeginTime;
}
/**
* Get selection end time
* @return the end time of current selection
*/
long getSelectionEnd() {
return fSelectionEndTime;
}
/**
* Get the link state
* @return true if begin and end selection time should be linked
*/
boolean getLinkState() {
return fLinkState;
}
/**
* Broadcast TmfSignal about new selection time range.
* @param startTime the new start time
* @param endTime the new end time
*/
void updateTimeRange(long startTime, long endTime) {
if (fTrace != null) {
// Build the new time range; keep the current time
TmfTimeRange timeRange = new TmfTimeRange(
TmfTimestamp.fromNanos(startTime),
TmfTimestamp.fromNanos(endTime));
fTimeSpanControl.setValue(endTime - startTime);
updateDisplayedTimeRange(startTime, endTime);
// Send the FW signal
TmfWindowRangeUpdatedSignal signal = new TmfWindowRangeUpdatedSignal(this, timeRange, fTrace);
fTimeRangeSyncThrottle.queue(signal);
}
}
/**
* Broadcast TmfSignal about new selected time range.
* @param newDuration new duration (relative to current start time)
*/
public synchronized void updateTimeRange(long newDuration) {
if (fTrace != null) {
long delta = newDuration - fWindowSpan;
long newStartTime = fWindowStartTime - (delta / 2);
setNewRange(newStartTime, newDuration);
}
}
private void setNewRange(long startTime, long duration) {
long realStart = startTime;
if (realStart < fTraceStartTime) {
realStart = fTraceStartTime;
}
long endTime = realStart + duration;
if (endTime > fTraceEndTime) {
endTime = fTraceEndTime;
if ((endTime - duration) > fTraceStartTime) {
realStart = endTime - duration;
} else {
realStart = fTraceStartTime;
}
}
updateTimeRange(realStart, endTime);
}
// ------------------------------------------------------------------------
// Signal handlers
// ------------------------------------------------------------------------
/**
* Handles trace opened signal. Loads histogram if new trace time range is not
* equal <code>TmfTimeRange.NULL_RANGE</code>
* @param signal the trace opened signal
*/
@TmfSignalHandler
public void traceOpened(TmfTraceOpenedSignal signal) {
fTrace = Objects.requireNonNull(signal).getTrace();
loadTrace();
}
/**
* Handles trace selected signal. Loads histogram if new trace time range is not
* equal <code>TmfTimeRange.NULL_RANGE</code>
* @param signal the trace selected signal
*/
@TmfSignalHandler
public void traceSelected(TmfTraceSelectedSignal signal) {
if (fTrace != Objects.requireNonNull(signal).getTrace()) {
fTrace = signal.getTrace();
loadTrace();
}
}
private void loadTrace() {
initializeHistograms();
getParentComposite().redraw();
}
/**
* Handles trace closed signal. Clears the view and data model and cancels requests.
* @param signal the trace closed signal
*/
@TmfSignalHandler
public void traceClosed(TmfTraceClosedSignal signal) {
if (signal.getTrace() != fTrace) {
return;
}
// Kill any running request
if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) {
fTimeRangeRequest.cancel();
}
if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) {
fFullTraceRequest.cancel();
}
// Initialize the internal data
fTrace = null;
fTraceStartTime = 0L;
fTraceEndTime = 0L;
fWindowStartTime = 0L;
fWindowEndTime = 0L;
fWindowSpan = 0L;
fSelectionBeginTime = 0L;
fSelectionEndTime = 0L;
// Clear the UI widgets
fFullTraceHistogram.clear();
fFullTraceHistogram.fDataModel.setTrace(null);
fTimeRangeHistogram.clear();
fTimeRangeHistogram.fDataModel.setTrace(null);
fSelectionStartControl.setValue(Long.MIN_VALUE);
fSelectionEndControl.setValue(Long.MIN_VALUE);
fTimeSpanControl.setValue(Long.MIN_VALUE);
for (Control c: fLegendArea.getChildren()) {
c.dispose();
}
disposeLegendImages();
fLegendArea.layout();
fLegendArea.getParent().layout();
}
/**
* Handles trace range updated signal. Extends histogram according to the new time range. If a
* HistogramRequest is already ongoing, it will be cancelled and a new request with the new range
* will be issued.
*
* @param signal the trace range updated signal
*/
@TmfSignalHandler
public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal) {
if (signal.getTrace() != fTrace) {
return;
}
TmfTimeRange fullRange = signal.getRange();
fTraceStartTime = fullRange.getStartTime().toNanos();
fTraceEndTime = fullRange.getEndTime().toNanos();
fFullTraceHistogram.setFullRange(fTraceStartTime, fTraceEndTime);
fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime);
sendFullRangeRequest(fullRange);
}
/**
* Handles the trace updated signal. Used to update time limits (start and end time)
* @param signal the trace updated signal
*/
@TmfSignalHandler
public void traceUpdated(TmfTraceUpdatedSignal signal) {
if (signal.getTrace() != fTrace) {
return;
}
TmfTimeRange fullRange = signal.getTrace().getTimeRange();
fTraceStartTime = fullRange.getStartTime().toNanos();
fTraceEndTime = fullRange.getEndTime().toNanos();
fFullTraceHistogram.setFullRange(fTraceStartTime, fTraceEndTime);
fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime);
if ((fFullTraceRequest != null) && fFullTraceRequest.getRange().getEndTime().compareTo(signal.getRange().getEndTime()) < 0) {
sendFullRangeRequest(fullRange);
}
}
/**
* Handles the selection range updated signal. Sets the current time
* selection in the time range histogram as well as the full histogram.
*
* @param signal
* the signal to process
* @since 1.0
*/
@TmfSignalHandler
public void selectionRangeUpdated(final TmfSelectionRangeUpdatedSignal signal) {
if (Display.getCurrent() == null) {
// Make sure the signal is handled in the UI thread
Display.getDefault().asyncExec(() -> {
if (getParentComposite().isDisposed()) {
return;
}
selectionRangeUpdated(signal);
});
return;
}
// Update the selected time range
long beginTime = signal.getBeginTime().toNanos();
long endTime = signal.getEndTime().toNanos();
updateDisplayedSelectionTime(beginTime, endTime);
}
/**
* Updates the current window time range in the time range histogram and
* full range histogram.
*
* @param signal
* the signal to process
* @since 1.0
*/
@TmfSignalHandler
public void windowRangeUpdated(final TmfWindowRangeUpdatedSignal signal) {
if (Display.getCurrent() == null) {
// Make sure the signal is handled in the UI thread
Display.getDefault().asyncExec(() -> {
if (getParentComposite().isDisposed()) {
return;
}
windowRangeUpdated(signal);
});
return;
}
if (fTrace != null) {
// Validate the time range
TmfTimeRange range = signal.getCurrentRange().getIntersection(fTrace.getTimeRange());
if (range == null) {
return;
}
updateDisplayedTimeRange(
range.getStartTime().toNanos(),
range.getEndTime().toNanos());
// Send the event request to populate the small histogram
sendTimeRangeRequest(fWindowStartTime, fWindowEndTime);
fTimeSpanControl.setValue(fWindowSpan);
}
}
// ------------------------------------------------------------------------
// Helper functions
// ------------------------------------------------------------------------
private void initializeHistograms() {
TmfTimeRange fullRange = updateTraceTimeRange();
TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
long selectionBeginTime = ctx.getSelectionRange().getStartTime().toNanos();
long selectionEndTime = ctx.getSelectionRange().getEndTime().toNanos();
long startTime = ctx.getWindowRange().getStartTime().toNanos();
long duration = ctx.getWindowRange().getEndTime().toNanos() - startTime;
if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) {
fTimeRangeRequest.cancel();
}
fTimeRangeHistogram.clear();
fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime);
fTimeRangeHistogram.setTimeRange(startTime, duration);
fTimeRangeHistogram.setSelection(selectionBeginTime, selectionEndTime);
fTimeRangeHistogram.fDataModel.setTrace(fTrace);
if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) {
fFullTraceRequest.cancel();
}
fFullTraceHistogram.clear();
fFullTraceHistogram.setFullRange(fTraceStartTime, fTraceEndTime);
fFullTraceHistogram.setTimeRange(startTime, duration);
fFullTraceHistogram.setSelection(selectionBeginTime, selectionEndTime);
fFullTraceHistogram.fDataModel.setTrace(fTrace);
fWindowStartTime = startTime;
fWindowSpan = duration;
fWindowEndTime = startTime + duration;
fSelectionBeginTime = selectionBeginTime;
fSelectionEndTime = selectionEndTime;
fSelectionStartControl.setValue(fSelectionBeginTime);
fSelectionEndControl.setValue(fSelectionEndTime);
// make sure that the scrollbar is setup properly
fScrollComposite.setMinSize(fTimeControlsComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
fTimeSpanControl.setValue(duration);
Collection<ITmfTrace> traces = TmfTraceManager.getTraceSet(fTrace);
if (!traces.isEmpty()) {
this.showTraceAction.setEnabled(traces.size() < fFullTraceHistogram.getMaxNbTraces());
}
updateLegendArea();
if (!fullRange.equals(TmfTimeRange.NULL_RANGE)) {
sendTimeRangeRequest(startTime, startTime + duration);
sendFullRangeRequest(fullRange);
}
}
private void updateLegendArea() {
for (Control c: fLegendArea.getChildren()) {
c.dispose();
}
disposeLegendImages();
if (fFullTraceHistogram.showTraces()) {
Collection<ITmfTrace> traces = TmfTraceManager.getTraceSet(fTrace);
fLegendImages = new Image[traces.size()];
int traceIndex = 0;
for (ITmfTrace trace : traces) {
fLegendImages[traceIndex] = new Image(fLegendArea.getDisplay(), 16, 16);
GC gc = new GC(fLegendImages[traceIndex]);
gc.setBackground(fFullTraceHistogram.getTraceColor(traceIndex));
gc.fillRectangle(0, 0, 15, 15);
gc.setForeground(fLegendArea.getDisplay().getSystemColor(SWT.COLOR_BLACK));
gc.drawRectangle(0, 0, 15, 15);
gc.dispose();
CLabel label = new CLabel(fLegendArea, SWT.NONE);
label.setText(trace.getName());
label.setImage(fLegendImages[traceIndex]);
traceIndex++;
}
}
fLegendArea.layout();
fLegendArea.getParent().layout();
}
private void updateDisplayedSelectionTime(long beginTime, long endTime) {
fSelectionBeginTime = beginTime;
fSelectionEndTime = endTime;
fFullTraceHistogram.setSelection(fSelectionBeginTime, fSelectionEndTime);
fTimeRangeHistogram.setSelection(fSelectionBeginTime, fSelectionEndTime);
fSelectionStartControl.setValue(fSelectionBeginTime);
fSelectionEndControl.setValue(fSelectionEndTime);
}
private void updateDisplayedTimeRange(long start, long end) {
fWindowStartTime = start;
fWindowEndTime = end;
fWindowSpan = fWindowEndTime - fWindowStartTime;
fFullTraceHistogram.setTimeRange(fWindowStartTime, fWindowSpan);
}
private TmfTimeRange updateTraceTimeRange() {
fTraceStartTime = 0L;
fTraceEndTime = 0L;
TmfTimeRange timeRange = fTrace.getTimeRange();
if (!timeRange.equals(TmfTimeRange.NULL_RANGE)) {
fTraceStartTime = timeRange.getStartTime().toNanos();
fTraceEndTime = timeRange.getEndTime().toNanos();
}
return timeRange;
}
private void sendTimeRangeRequest(long startTime, long endTime) {
if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) {
fTimeRangeRequest.cancel();
}
ITmfTimestamp startTS = TmfTimestamp.fromNanos(startTime);
ITmfTimestamp endTS = TmfTimestamp.fromNanos(endTime);
TmfTimeRange timeRange = new TmfTimeRange(startTS, endTS);
fTimeRangeHistogram.clear();
fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime);
fTimeRangeHistogram.setTimeRange(startTime, endTime - startTime);
int cacheSize = fTrace.getCacheSize();
fTimeRangeRequest = new HistogramRequest(fTimeRangeHistogram.getDataModel(),
timeRange, 0, ITmfEventRequest.ALL_DATA, cacheSize, ExecutionType.FOREGROUND, false);
fTrace.sendRequest(fTimeRangeRequest);
}
private void sendFullRangeRequest(TmfTimeRange fullRange) {
if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) {
fFullTraceRequest.cancel();
}
int cacheSize = fTrace.getCacheSize();
fFullTraceRequest = new HistogramRequest(fFullTraceHistogram.getDataModel(),
fullRange,
(int) fFullTraceHistogram.fDataModel.getNbEvents(),
ITmfEventRequest.ALL_DATA,
cacheSize,
ExecutionType.BACKGROUND, true);
fTrace.sendRequest(fFullTraceRequest);
}
private void contributeToActionBars() {
IActionBars bars = getViewSite().getActionBars();
bars.getToolBarManager().add(getShowLostEventsAction());
bars.getToolBarManager().add(getShowTraceAction());
bars.getToolBarManager().add(new Separator());
}
private void addLinkButtonListeners() {
fLinkButton.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
fSelectionEndControl.setEnabled(fLinkState);
fLinkState = !fLinkState;
fLinkButton.redraw();
}
});
fLinkButton.addPaintListener(e -> {
if (fLinkState) {
Rectangle r = fLinkButton.getBounds();
r.x = -1;
r.y = -1;
e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
e.gc.drawRectangle(r);
r.x = 0;
r.y = 0;
e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_DARK_GRAY));
e.gc.drawRectangle(r);
}
});
}
private static class PackedScrolledComposite extends ScrolledComposite {
Point fScrollBarSize; // Size of OS-specific scrollbar
public PackedScrolledComposite(Composite parent, int style) {
super(parent, style);
Composite composite = new Composite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
composite.setSize(1, 1);
fScrollBarSize = composite.computeSize(0, 0);
composite.dispose();
}
@Override
public Point computeSize(int wHint, int hHint, boolean changed) {
Point point = super.computeSize(wHint, hHint, changed);
// Remove scrollbar size if applicable
point.x += ((getStyle() & SWT.V_SCROLL) != 0) ? -fScrollBarSize.x : 0;
point.y += ((getStyle() & SWT.H_SCROLL) != 0) ? -fScrollBarSize.y : 0;
return point;
}
}
}