blob: c947ae66fdecc26642df574c881158be1ce87b8c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2015 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:
* Patrick Tasse - Initial API and implementation
* Bernd Hufmann - Updated signal handling
* Geneviève Bastien - Move code to provide base classes for time graph view
* Marc-Andre Laperle - Add time zone preference
* Geneviève Bastien - Add event links between entries
*******************************************************************************/
package org.eclipse.tracecompass.tmf.ui.views.timegraph;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TreeColumn;
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.TmfTraceClosedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfNanoTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
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.TmfUiRefreshHandler;
import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
import org.eclipse.tracecompass.tmf.ui.views.TmfView;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphContentProvider;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider2;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphRangeListener;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphSelectionListener;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphTimeListener;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphCombo;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphContentProvider;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphRangeUpdateEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
import org.eclipse.ui.IActionBars;
/**
* An abstract view all time graph views can inherit
*
* This view contains either a time graph viewer, or a time graph combo which is
* divided between a tree viewer on the left and a time graph viewer on the right.
*/
public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeAligned {
/** Constant indicating that all levels of the time graph should be expanded */
protected static final int ALL_LEVELS = AbstractTreeViewer.ALL_LEVELS;
/**
* Redraw state enum
*/
private enum State {
IDLE, BUSY, PENDING
}
// ------------------------------------------------------------------------
// Fields
// ------------------------------------------------------------------------
/** The timegraph wrapper */
private ITimeGraphWrapper fTimeGraphWrapper;
private int fDirty;
public boolean isDirty() {
return fDirty > 0;
}
/** The selected trace */
private ITmfTrace fTrace;
/** The timegraph entry list */
private List<TimeGraphEntry> fEntryList;
/** The trace to entry list hash map */
private final Map<ITmfTrace, List<TimeGraphEntry>> fEntryListMap = new HashMap<>();
/** The trace to build thread hash map */
private final Map<ITmfTrace, BuildThread> fBuildThreadMap = new HashMap<>();
/** The start time */
private long fStartTime;
/** The end time */
private long fEndTime;
/** The display width */
private final int fDisplayWidth;
/** The zoom thread */
private ZoomThread fZoomThread;
/** The next resource action */
private Action fNextResourceAction;
/** The previous resource action */
private Action fPreviousResourceAction;
/** A comparator class */
private Comparator<ITimeGraphEntry> fEntryComparator = null;
/** The redraw state used to prevent unnecessary queuing of display runnables */
private State fRedrawState = State.IDLE;
/** The redraw synchronization object */
private final Object fSyncObj = new Object();
/** The presentation provider for this view */
private final TimeGraphPresentationProvider fPresentation;
/** The tree column label array, or null if combo is not used */
private String[] fColumns;
/** The tree label provider, or null if combo is not used */
private TreeLabelProvider fLabelProvider = null;
/** The time graph content provider */
private @NonNull ITimeGraphContentProvider fTimeGraphContentProvider = new TimeGraphContentProvider();
/** The relative weight of the sash, ignored if combo is not used */
private int[] fWeight = { 1, 3 };
/** The filter column label array, or null if filter is not used */
private String[] fFilterColumns;
/** The pack done flag */
private boolean fPackDone = false;
/** The filter label provider, or null if filter is not used */
private TreeLabelProvider fFilterLabelProvider;
private int fAutoExpandLevel = ALL_LEVELS;
// ------------------------------------------------------------------------
// Classes
// ------------------------------------------------------------------------
private interface ITimeGraphWrapper {
void setTimeGraphProvider(TimeGraphPresentationProvider fPresentation);
TimeGraphViewer getTimeGraphViewer();
void addSelectionListener(ITimeGraphSelectionListener iTimeGraphSelectionListener);
ISelectionProvider getSelectionProvider();
void setFocus();
boolean isDisposed();
void refresh();
void setInput(Object input);
Object getInput();
void redraw();
void update();
void setAutoExpandLevel(int level);
void performAlign(int offset, int width);
TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo();
int getAvailableWidth(int requestedOffset);
}
private class TimeGraphViewerWrapper implements ITimeGraphWrapper {
private TimeGraphViewer viewer;
private TimeGraphViewerWrapper(Composite parent, int style) {
viewer = new TimeGraphViewer(parent, style);
}
@Override
public void setTimeGraphProvider(TimeGraphPresentationProvider timeGraphProvider) {
viewer.setTimeGraphProvider(timeGraphProvider);
}
@Override
public TimeGraphViewer getTimeGraphViewer() {
return viewer;
}
@Override
public void addSelectionListener(ITimeGraphSelectionListener listener) {
viewer.addSelectionListener(listener);
}
@Override
public ISelectionProvider getSelectionProvider() {
return viewer.getSelectionProvider();
}
@Override
public void setFocus() {
viewer.setFocus();
}
@Override
public boolean isDisposed() {
return viewer.getControl().isDisposed();
}
@Override
public void setInput(Object input) {
viewer.setInput(input);
}
@Override
public Object getInput() {
return viewer.getInput();
}
@Override
public void refresh() {
viewer.refresh();
}
@Override
public void redraw() {
viewer.getControl().redraw();
}
@Override
public void update() {
viewer.getControl().update();
}
@Override
public void setAutoExpandLevel(int level) {
viewer.setAutoExpandLevel(level);
}
@Override
public void performAlign(int offset, int width) {
viewer.performAlign(offset, width);
}
@Override
public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
return viewer.getTimeViewAlignmentInfo();
}
@Override
public int getAvailableWidth(int requestedOffset) {
return viewer.getAvailableWidth(requestedOffset);
}
}
private class TimeGraphComboWrapper implements ITimeGraphWrapper {
private TimeGraphCombo combo;
private TimeGraphComboWrapper(Composite parent, int style) {
combo = new TimeGraphCombo(parent, style, fWeight);
}
@Override
public void setTimeGraphProvider(TimeGraphPresentationProvider timeGraphProvider) {
combo.setTimeGraphProvider(timeGraphProvider);
}
@Override
public TimeGraphViewer getTimeGraphViewer() {
return combo.getTimeGraphViewer();
}
@Override
public void addSelectionListener(ITimeGraphSelectionListener listener) {
combo.addSelectionListener(listener);
}
@Override
public ISelectionProvider getSelectionProvider() {
return combo.getTreeViewer();
}
@Override
public void setFocus() {
combo.setFocus();
}
@Override
public boolean isDisposed() {
return combo.isDisposed();
}
@Override
public void setInput(Object input) {
combo.setInput(input);
}
@Override
public Object getInput() {
return combo.getInput();
}
@Override
public void refresh() {
combo.refresh();
}
@Override
public void redraw() {
combo.redraw();
}
@Override
public void update() {
combo.update();
}
@Override
public void setAutoExpandLevel(int level) {
combo.setAutoExpandLevel(level);
}
TimeGraphCombo getTimeGraphCombo() {
return combo;
}
TreeViewer getTreeViewer() {
return combo.getTreeViewer();
}
IAction getShowFilterAction() {
return combo.getShowFilterAction();
}
@Override
public void performAlign(int offset, int width) {
combo.performAlign(offset, width);
}
@Override
public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
return combo.getTimeViewAlignmentInfo();
}
@Override
public int getAvailableWidth(int requestedOffset) {
return combo.getAvailableWidth(requestedOffset);
}
}
/**
* Base class to provide the labels for the tree viewer. Views extending
* this class typically need to override the getColumnText method if they
* have more than one column to display
*/
protected static class TreeLabelProvider implements ITableLabelProvider, ILabelProvider {
@Override
public void addListener(ILabelProviderListener listener) {
}
@Override
public void dispose() {
}
@Override
public boolean isLabelProperty(Object element, String property) {
return false;
}
@Override
public void removeListener(ILabelProviderListener listener) {
}
@Override
public Image getColumnImage(Object element, int columnIndex) {
return null;
}
@Override
public String getColumnText(Object element, int columnIndex) {
TimeGraphEntry entry = (TimeGraphEntry) element;
if (columnIndex == 0) {
return entry.getName();
}
return new String();
}
@Override
public Image getImage(Object element) {
return null;
}
@Override
public String getText(Object element) {
TimeGraphEntry entry = (TimeGraphEntry) element;
return entry.getName();
}
}
private class BuildThread extends Thread {
private final @NonNull ITmfTrace fBuildTrace;
private final @NonNull ITmfTrace fParentTrace;
private final @NonNull IProgressMonitor fMonitor;
public BuildThread(final @NonNull ITmfTrace trace, final @NonNull ITmfTrace parentTrace, final String name) {
super(name + " build"); //$NON-NLS-1$
fBuildTrace = trace;
fParentTrace = parentTrace;
fMonitor = new NullProgressMonitor();
}
@Override
public void run() {
buildEventList(fBuildTrace, fParentTrace, fMonitor);
synchronized (fBuildThreadMap) {
fBuildThreadMap.remove(fBuildTrace);
}
}
public void cancel() {
fMonitor.setCanceled(true);
}
}
private static int fZoomThreadCount = 0;
private class ZoomThread extends Thread {
private final @NonNull List<TimeGraphEntry> fZoomEntryList;
private final long fZoomStartTime;
private final long fZoomEndTime;
private final long fResolution;
private final @NonNull IProgressMonitor fMonitor;
private int fZoomThreadId = 0;
private boolean fStarted = false;
public ZoomThread(@NonNull List<TimeGraphEntry> entryList, long startTime, long endTime, String name) {
super(name + " zoom"); //$NON-NLS-1$
synchronized (ZoomThread.class) {
fZoomThreadCount++;
fZoomThreadId = fZoomThreadCount;
}
fZoomEntryList = entryList;
fZoomStartTime = startTime;
fZoomEndTime = endTime;
fResolution = Math.max(1, (fZoomEndTime - fZoomStartTime) / fDisplayWidth);
fMonitor = new NullProgressMonitor();
}
@Override
public void run() {
if (!fStarted) {
fStarted = true;
synchronized (ZoomThread.class) {
fDirty++;
System.out.println("zoom thread start " + fZoomThreadId + "(" + fDirty + " dirty)");
}
} else {
System.out.println();
}
try {
for (TimeGraphEntry entry : fZoomEntryList) {
if (fMonitor.isCanceled()) {
return;
}
if (entry == null) {
break;
}
zoom(entry, fMonitor);
}
/* Refresh the arrows when zooming */
List<ILinkEvent> events = getLinkList(fZoomStartTime, fZoomEndTime, fResolution, fMonitor);
if (events != null) {
fTimeGraphWrapper.getTimeGraphViewer().setLinks(events);
redraw();
}
synchronized (ZoomThread.class) {
fDirty--;
System.out.println("zoom thread end " + fZoomThreadId + "(" + fDirty + " dirty)");
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void zoom(@NonNull TimeGraphEntry entry, @NonNull IProgressMonitor monitor) {
if (fZoomStartTime <= fStartTime && fZoomEndTime >= fEndTime) {
entry.setZoomedEventList(null);
} else {
List<ITimeEvent> zoomedEventList = getEventList(entry, fZoomStartTime, fZoomEndTime, fResolution, monitor);
if (zoomedEventList != null) {
entry.setZoomedEventList(zoomedEventList);
}
}
redraw();
for (ITimeGraphEntry child : entry.getChildren()) {
if (fMonitor.isCanceled()) {
return;
}
if (child instanceof TimeGraphEntry) {
zoom((TimeGraphEntry) child, monitor);
}
}
}
public void cancel() {
System.out.println("cancel " + fZoomThreadId);
fMonitor.setCanceled(true);
}
}
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Constructs a time graph view that contains either a time graph viewer or
* a time graph combo.
*
* By default, the view uses a time graph viewer. To use a time graph combo,
* the subclass constructor must call {@link #setTreeColumns(String[])} and
* {@link #setTreeLabelProvider(TreeLabelProvider)}.
*
* @param id
* The id of the view
* @param pres
* The presentation provider
*/
public AbstractTimeGraphView(String id, TimeGraphPresentationProvider pres) {
super(id);
fDirty = 0;
fPresentation = pres;
fDisplayWidth = Display.getDefault().getBounds().width;
}
// ------------------------------------------------------------------------
// Getters and setters
// ------------------------------------------------------------------------
/**
* Getter for the time graph combo
*
* @return The time graph combo, or null if combo is not used
*/
protected TimeGraphCombo getTimeGraphCombo() {
if (fTimeGraphWrapper instanceof TimeGraphComboWrapper) {
return ((TimeGraphComboWrapper) fTimeGraphWrapper).getTimeGraphCombo();
}
return null;
}
/**
* Getter for the time graph viewer
*
* @return The time graph viewer
*/
protected TimeGraphViewer getTimeGraphViewer() {
return fTimeGraphWrapper.getTimeGraphViewer();
}
/**
* Getter for the presentation provider
*
* @return The time graph presentation provider
*/
protected ITimeGraphPresentationProvider2 getPresentationProvider() {
return fPresentation;
}
/**
* Sets the tree column labels.
* This should be called from the constructor.
*
* @param columns
* The array of tree column labels
*/
protected void setTreeColumns(final String[] columns) {
fColumns = columns;
}
/**
* Sets the tree label provider.
* This should be called from the constructor.
*
* @param tlp
* The tree label provider
*/
protected void setTreeLabelProvider(final TreeLabelProvider tlp) {
fLabelProvider = tlp;
}
/**
* Sets the time graph content provider. This should be called from the
* constructor.
*
* @param tgcp
* The time graph content provider
* @since 1.0
*/
protected void setTimeGraphContentProvider(final @NonNull ITimeGraphContentProvider tgcp) {
fTimeGraphContentProvider = tgcp;
}
/**
* Sets the relative weight of each part of the time graph combo.
* This should be called from the constructor.
*
* @param weights
* The array (length 2) of relative weights of each part of the combo
*/
protected void setWeight(final int[] weights) {
fWeight = weights;
}
/**
* Sets the filter column labels.
* This should be called from the constructor.
*
* @param filterColumns
* The array of filter column labels
*/
protected void setFilterColumns(final String[] filterColumns) {
fFilterColumns = filterColumns;
}
/**
* Sets the filter label provider.
* This should be called from the constructor.
*
* @param labelProvider
* The filter label provider
*/
protected void setFilterLabelProvider(final TreeLabelProvider labelProvider) {
fFilterLabelProvider = labelProvider;
}
/**
* Gets the display width
*
* @return the display width
*/
protected int getDisplayWidth() {
return fDisplayWidth;
}
/**
* Gets the comparator for the entries
*
* @return The entry comparator
*/
protected Comparator<ITimeGraphEntry> getEntryComparator() {
return fEntryComparator;
}
/**
* Sets the comparator class for the entries
*
* @param comparator
* A comparator object
*/
protected void setEntryComparator(final Comparator<ITimeGraphEntry> comparator) {
fEntryComparator = comparator;
}
/**
* Gets the trace displayed in the view
*
* @return The trace
*/
protected ITmfTrace getTrace() {
return fTrace;
}
/**
* Gets the start time
*
* @return The start time
*/
protected long getStartTime() {
return fStartTime;
}
/**
* Sets the start time
*
* @param time
* The start time
*/
protected void setStartTime(long time) {
fStartTime = time;
}
/**
* Gets the end time
*
* @return The end time
*/
protected long getEndTime() {
return fEndTime;
}
/**
* Sets the end time
*
* @param time
* The end time
*/
protected void setEndTime(long time) {
fEndTime = time;
}
/**
* Sets the auto-expand level to be used for the input of the view. The
* value 0 means that there is no auto-expand; 1 means that top-level
* elements are expanded, but not their children; 2 means that top-level
* elements are expanded, and their children, but not grand-children; and so
* on.
* <p>
* The value {@link #ALL_LEVELS} means that all subtrees should be expanded.
* </p>
*
* @param level
* non-negative level, or <code>ALL_LEVELS</code> to expand all
* levels of the tree
*/
protected void setAutoExpandLevel(int level) {
fAutoExpandLevel = level;
ITimeGraphWrapper tgWrapper = fTimeGraphWrapper;
if (tgWrapper != null) {
tgWrapper.setAutoExpandLevel(level);
}
}
/**
* Gets the entry list for a trace
*
* @param trace
* the trace
*
* @return the entry list map
*/
protected List<TimeGraphEntry> getEntryList(ITmfTrace trace) {
synchronized (fEntryListMap) {
return fEntryListMap.get(trace);
}
}
/**
* Adds a trace entry list to the entry list map
*
* @param trace
* the trace to add
* @param list
* the list of time graph entries
*/
protected void putEntryList(ITmfTrace trace, List<TimeGraphEntry> list) {
synchronized (fEntryListMap) {
fEntryListMap.put(trace, new CopyOnWriteArrayList<>(list));
}
}
/**
* Adds a list of entries to a trace's entry list
*
* @param trace
* the trace
* @param list
* the list of time graph entries to add
*/
protected void addToEntryList(ITmfTrace trace, List<TimeGraphEntry> list) {
synchronized (fEntryListMap) {
List<TimeGraphEntry> entryList = fEntryListMap.get(trace);
if (entryList == null) {
fEntryListMap.put(trace, new CopyOnWriteArrayList<>(list));
} else {
entryList.addAll(list);
}
}
}
/**
* Removes a list of entries from a trace's entry list
*
* @param trace
* the trace
* @param list
* the list of time graph entries to remove
*/
protected void removeFromEntryList(ITmfTrace trace, List<TimeGraphEntry> list) {
synchronized (fEntryListMap) {
List<TimeGraphEntry> entryList = fEntryListMap.get(trace);
if (entryList != null) {
entryList.removeAll(list);
}
}
}
/**
* Text for the "next" button
*
* @return The "next" button text
*/
protected String getNextText() {
return Messages.AbstractTimeGraphtView_NextText;
}
/**
* Tooltip for the "next" button
*
* @return Tooltip for the "next" button
*/
protected String getNextTooltip() {
return Messages.AbstractTimeGraphView_NextTooltip;
}
/**
* Text for the "Previous" button
*
* @return The "Previous" button text
*/
protected String getPrevText() {
return Messages.AbstractTimeGraphView_PreviousText;
}
/**
* Tooltip for the "previous" button
*
* @return Tooltip for the "previous" button
*/
protected String getPrevTooltip() {
return Messages.AbstractTimeGraphView_PreviousTooltip;
}
// ------------------------------------------------------------------------
// ViewPart
// ------------------------------------------------------------------------
@Override
public void createPartControl(Composite parent) {
fDirty = 0;
super.createPartControl(parent);
if (fColumns == null || fLabelProvider == null) {
fTimeGraphWrapper = new TimeGraphViewerWrapper(parent, SWT.NONE);
TimeGraphViewer viewer = fTimeGraphWrapper.getTimeGraphViewer();
viewer.setTimeGraphContentProvider(fTimeGraphContentProvider);
} else {
TimeGraphComboWrapper wrapper = new TimeGraphComboWrapper(parent, SWT.NONE);
fTimeGraphWrapper = wrapper;
TimeGraphCombo combo = wrapper.getTimeGraphCombo();
combo.setTreeContentProvider(fTimeGraphContentProvider);
combo.setTreeLabelProvider(fLabelProvider);
combo.setTreeColumns(fColumns);
combo.setFilterContentProvider(fTimeGraphContentProvider);
combo.setFilterLabelProvider(fFilterLabelProvider);
combo.setFilterColumns(fFilterColumns);
combo.setTimeGraphContentProvider(fTimeGraphContentProvider);
}
fTimeGraphWrapper.setTimeGraphProvider(fPresentation);
fTimeGraphWrapper.setAutoExpandLevel(fAutoExpandLevel);
fTimeGraphWrapper.getTimeGraphViewer().addRangeListener(new ITimeGraphRangeListener() {
@Override
public void timeRangeUpdated(TimeGraphRangeUpdateEvent event) {
final long startTime = event.getStartTime();
final long endTime = event.getEndTime();
TmfTimeRange range = new TmfTimeRange(new TmfNanoTimestamp(startTime), new TmfNanoTimestamp(endTime));
broadcast(new TmfWindowRangeUpdatedSignal(AbstractTimeGraphView.this, range));
startZoomThread(startTime, endTime);
}
});
fTimeGraphWrapper.getTimeGraphViewer().addTimeListener(new ITimeGraphTimeListener() {
@Override
public void timeSelected(TimeGraphTimeEvent event) {
TmfNanoTimestamp startTime = new TmfNanoTimestamp(event.getBeginTime());
TmfNanoTimestamp endTime = new TmfNanoTimestamp(event.getEndTime());
broadcast(new TmfSelectionRangeUpdatedSignal(AbstractTimeGraphView.this, startTime, endTime));
}
});
fTimeGraphWrapper.getTimeGraphViewer().setTimeFormat(TimeFormat.CALENDAR);
IStatusLineManager statusLineManager = getViewSite().getActionBars().getStatusLineManager();
fTimeGraphWrapper.getTimeGraphViewer().getTimeGraphControl().setStatusLineManager(statusLineManager);
// View Action Handling
makeActions();
contributeToActionBars();
ITmfTrace trace = TmfTraceManager.getInstance().getActiveTrace();
if (trace != null) {
traceSelected(new TmfTraceSelectedSignal(this, trace));
}
// make selection available to other views
getSite().setSelectionProvider(fTimeGraphWrapper.getSelectionProvider());
}
@Override
public void setFocus() {
fTimeGraphWrapper.setFocus();
}
// ------------------------------------------------------------------------
// Signal handlers
// ------------------------------------------------------------------------
/**
* Handler for the trace opened signal.
*
* @param signal
* The incoming signal
*/
@TmfSignalHandler
public void traceOpened(TmfTraceOpenedSignal signal) {
fTrace = signal.getTrace();
loadTrace();
}
/**
* Handler for the trace selected signal
*
* @param signal
* The incoming signal
*/
@TmfSignalHandler
public void traceSelected(final TmfTraceSelectedSignal signal) {
if (signal.getTrace() == fTrace) {
return;
}
fTrace = signal.getTrace();
loadTrace();
}
/**
* Trace is closed: clear the data structures and the view
*
* @param signal
* the signal received
*/
@TmfSignalHandler
public void traceClosed(final TmfTraceClosedSignal signal) {
synchronized (fBuildThreadMap) {
for (ITmfTrace trace : getTracesToBuild(signal.getTrace())) {
BuildThread buildThread = fBuildThreadMap.remove(trace);
if (buildThread != null) {
buildThread.cancel();
}
}
}
synchronized (fEntryListMap) {
fEntryListMap.remove(signal.getTrace());
}
if (signal.getTrace() == fTrace) {
fTrace = null;
fStartTime = 0;
fEndTime = 0;
if (fZoomThread != null) {
fZoomThread.cancel();
}
refresh();
}
}
/**
* Handler for the selection range signal.
*
* @param signal
* The signal that's received
* @since 1.0
*/
@TmfSignalHandler
public void selectionRangeUpdated(final TmfSelectionRangeUpdatedSignal signal) {
if (signal.getSource() == this || fTrace == null) {
return;
}
final long beginTime = signal.getBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
final long endTime = signal.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (fTimeGraphWrapper.isDisposed()) {
return;
}
if (beginTime == endTime) {
fTimeGraphWrapper.getTimeGraphViewer().setSelectedTime(beginTime, true);
} else {
fTimeGraphWrapper.getTimeGraphViewer().setSelectionRange(beginTime, endTime);
}
startZoomThread(fTimeGraphWrapper.getTimeGraphViewer().getTime0(), fTimeGraphWrapper.getTimeGraphViewer().getTime1());
synchingToTime(beginTime);
}
});
}
/**
* Handler for the window range signal.
*
* @param signal
* The signal that's received
* @since 1.0
*/
@TmfSignalHandler
public void windowRangeUpdated(final TmfWindowRangeUpdatedSignal signal) {
if (signal.getSource() == this || fTrace == null) {
return;
}
if (signal.getCurrentRange().getIntersection(fTrace.getTimeRange()) == null) {
return;
}
final long startTime = signal.getCurrentRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
final long endTime = signal.getCurrentRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (fTimeGraphWrapper.isDisposed()) {
return;
}
fTimeGraphWrapper.getTimeGraphViewer().setStartFinishTime(startTime, endTime);
startZoomThread(startTime, endTime);
}
});
}
/**
* @param signal the format of the timestamps was updated.
*/
@TmfSignalHandler
public void updateTimeFormat( final TmfTimestampFormatUpdateSignal signal){
fTimeGraphWrapper.refresh();
}
// ------------------------------------------------------------------------
// Internal
// ------------------------------------------------------------------------
private void loadTrace() {
synchronized (fEntryListMap) {
fEntryList = fEntryListMap.get(fTrace);
if (fEntryList == null) {
rebuild();
} else {
fStartTime = fTrace.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
fEndTime = fTrace.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
refresh();
}
}
}
/**
* Forces a rebuild of the entries list, even if entries already exist for this trace
*/
protected void rebuild() {
setStartTime(Long.MAX_VALUE);
setEndTime(Long.MIN_VALUE);
refresh();
ITmfTrace viewTrace = fTrace;
if (viewTrace == null) {
return;
}
synchronized (fBuildThreadMap) {
for (ITmfTrace trace : getTracesToBuild(viewTrace)) {
if (trace == null) {
break;
}
BuildThread buildThread = new BuildThread(trace, viewTrace, getName());
fBuildThreadMap.put(trace, buildThread);
buildThread.start();
}
}
}
/**
* Method called when synching to a given timestamp. Inheriting classes can
* perform actions here to update the view at the given timestamp.
*
* @param time
* The currently selected time
*/
protected void synchingToTime(long time) {
}
/**
* Return the list of traces whose data or analysis results will be used to
* populate the view. By default, if the trace is an experiment, the traces
* under it will be returned, otherwise, the trace itself is returned.
*
* A build thread will be started for each trace returned by this method,
* some of which may receive events in live streaming mode.
*
* @param trace
* The trace associated with this view
* @return List of traces with data to display
*/
protected @NonNull Iterable<ITmfTrace> getTracesToBuild(@NonNull ITmfTrace trace) {
return TmfTraceManager.getTraceSet(trace);
}
/**
* Build the entries list to show in this time graph
*
* Called from the BuildThread
*
* @param trace
* The trace being built
* @param parentTrace
* The parent of the trace set, or the trace itself
* @param monitor
* The progress monitor object
*/
protected abstract void buildEventList(@NonNull ITmfTrace trace, @NonNull ITmfTrace parentTrace, @NonNull IProgressMonitor monitor);
/**
* Gets the list of event for an entry in a given timerange
*
* @param entry
* The entry to get events for
* @param startTime
* Start of the time range
* @param endTime
* End of the time range
* @param resolution
* The resolution
* @param monitor
* The progress monitor object
* @return The list of events for the entry
*/
protected abstract @Nullable List<ITimeEvent> getEventList(@NonNull TimeGraphEntry entry,
long startTime, long endTime, long resolution,
@NonNull IProgressMonitor monitor);
/**
* Gets the list of links (displayed as arrows) for a trace in a given
* timerange. Default implementation returns an empty list.
*
* @param startTime
* Start of the time range
* @param endTime
* End of the time range
* @param resolution
* The resolution
* @param monitor
* The progress monitor object
* @return The list of link events
*/
protected @Nullable List<ILinkEvent> getLinkList(long startTime, long endTime,
long resolution, @NonNull IProgressMonitor monitor) {
return new ArrayList<>();
}
/**
* Refresh the display
*/
protected void refresh() {
TmfUiRefreshHandler.getInstance().queueUpdate(this, new Runnable() {
@Override
public void run() {
if (fTimeGraphWrapper.isDisposed()) {
return;
}
boolean hasEntries = false;
synchronized (fEntryListMap) {
fEntryList = fEntryListMap.get(fTrace);
if (fEntryList == null) {
fEntryList = new CopyOnWriteArrayList<>();
} else if (fEntryComparator != null) {
List<TimeGraphEntry> list = new ArrayList<>(fEntryList);
Collections.sort(list, fEntryComparator);
fEntryList.clear();
fEntryList.addAll(list);
}
hasEntries = fEntryList.size() != 0;
}
if (fEntryList != fTimeGraphWrapper.getInput()) {
fTimeGraphWrapper.setInput(fEntryList);
// fTimeGraphWrapper.getTimeGraphViewer().setLinks(null);
} else {
fTimeGraphWrapper.refresh();
}
fTimeGraphWrapper.getTimeGraphViewer().setTimeBounds(fStartTime, fEndTime);
TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
long selectionBeginTime = fTrace == null ? 0 : ctx.getSelectionRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
long selectionEndTime = fTrace == null ? 0 : ctx.getSelectionRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
long startTime = fTrace == null ? 0 : ctx.getWindowRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
long endTime = fTrace == null ? 0 : ctx.getWindowRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
startTime = Math.max(startTime, fStartTime);
endTime = Math.min(endTime, fEndTime);
fTimeGraphWrapper.getTimeGraphViewer().setSelectionRange(selectionBeginTime, selectionEndTime);
fTimeGraphWrapper.getTimeGraphViewer().setStartFinishTime(startTime, endTime);
if (fTimeGraphWrapper instanceof TimeGraphComboWrapper && !fPackDone) {
for (TreeColumn column : ((TimeGraphComboWrapper) fTimeGraphWrapper).getTreeViewer().getTree().getColumns()) {
column.pack();
}
if (hasEntries) {
fPackDone = true;
}
}
startZoomThread(startTime, endTime);
}
});
}
/**
* Redraw the canvas
*/
protected void redraw() {
synchronized (fSyncObj) {
if (fRedrawState == State.IDLE) {
fRedrawState = State.BUSY;
} else {
fRedrawState = State.PENDING;
return;
}
}
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (fTimeGraphWrapper.isDisposed()) {
return;
}
fTimeGraphWrapper.redraw();
fTimeGraphWrapper.update();
synchronized (fSyncObj) {
if (fRedrawState == State.PENDING) {
fRedrawState = State.IDLE;
redraw();
} else {
fRedrawState = State.IDLE;
}
}
}
});
}
private void startZoomThread(long startTime, long endTime) {
if (fZoomThread != null) {
fZoomThread.cancel();
}
final List<TimeGraphEntry> entryList = fEntryList;
if (entryList == null) {
return;
}
fZoomThread = new ZoomThread(entryList, startTime, endTime, getName());
fZoomThread.start();
}
private void makeActions() {
fPreviousResourceAction = fTimeGraphWrapper.getTimeGraphViewer().getPreviousItemAction();
fPreviousResourceAction.setText(getPrevText());
fPreviousResourceAction.setToolTipText(getPrevTooltip());
fNextResourceAction = fTimeGraphWrapper.getTimeGraphViewer().getNextItemAction();
fNextResourceAction.setText(getNextText());
fNextResourceAction.setToolTipText(getNextTooltip());
}
private void contributeToActionBars() {
IActionBars bars = getViewSite().getActionBars();
fillLocalToolBar(bars.getToolBarManager());
}
/**
* Add actions to local tool bar manager
*
* @param manager the tool bar manager
*/
protected void fillLocalToolBar(IToolBarManager manager) {
if (fTimeGraphWrapper instanceof TimeGraphComboWrapper) {
if (fFilterColumns != null && fFilterLabelProvider != null && fFilterColumns.length > 0) {
manager.add(((TimeGraphComboWrapper) fTimeGraphWrapper).getShowFilterAction());
}
}
manager.add(fTimeGraphWrapper.getTimeGraphViewer().getShowLegendAction());
manager.add(new Separator());
manager.add(fTimeGraphWrapper.getTimeGraphViewer().getResetScaleAction());
manager.add(fTimeGraphWrapper.getTimeGraphViewer().getPreviousEventAction());
manager.add(fTimeGraphWrapper.getTimeGraphViewer().getNextEventAction());
manager.add(fPreviousResourceAction);
manager.add(fNextResourceAction);
manager.add(fTimeGraphWrapper.getTimeGraphViewer().getZoomInAction());
manager.add(fTimeGraphWrapper.getTimeGraphViewer().getZoomOutAction());
manager.add(new Separator());
}
/**
* @since 1.0
*/
@Override
public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
if (fTimeGraphWrapper == null) {
return null;
}
return fTimeGraphWrapper.getTimeViewAlignmentInfo();
}
/**
* @since 1.0
*/
@Override
public int getAvailableWidth(int requestedOffset) {
if (fTimeGraphWrapper == null) {
return 0;
}
return fTimeGraphWrapper.getAvailableWidth(requestedOffset);
}
/**
* @since 1.0
*/
@Override
public void performAlign(int offset, int width) {
if (fTimeGraphWrapper != null) {
fTimeGraphWrapper.performAlign(offset, width);
}
}
}