| /******************************************************************************* |
| * 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; |
| } |
| } |
| } |