import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.commands.ActionHandler;
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.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
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.TmfNanoTimestamp;
+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.TmfTraceAdapterManager;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.handlers.IHandlerActivation;
+import org.eclipse.ui.handlers.IHandlerService;
/**
* An abstract view all time graph views can inherit
private AtomicInteger fDirty = new AtomicInteger();
+ private final Object fZoomThreadResultLock = new Object();
+
/** The selected trace */
private ITmfTrace fTrace;
*/
private final @NonNull MenuManager fEntryMenuManager = new MenuManager();
+ /** Time Graph View part listener */
+ private TimeGraphPartListener fPartListener;
+
+ /** Action for the find command. There is only one for all Time Graph views */
+ private static final ShowFindDialogAction FIND_ACTION = new ShowFindDialogAction();
+
+ /** The find action handler */
+ private ActionHandler fFindActionHandler;
+
+ /** The find handler activation */
+ private IHandlerActivation fFindHandlerActivation;
+
+ /** The find target to use */
+ private final FindTarget fFindTarget;
+
// ------------------------------------------------------------------------
// Classes
// ------------------------------------------------------------------------
void setAutoExpandLevel(int level);
+ boolean getExpandedState(ITimeGraphEntry entry);
+
+ void setExpandedState(ITimeGraphEntry entry, boolean expanded);
+
void setFilterColumns(String[] columnNames);
void setFilterContentProvider(ITreeContentProvider contentProvider);
ITimeGraphEntry getSelection();
void setSelection(ITimeGraphEntry selection);
+
+ void selectAndReveal(@NonNull ITimeGraphEntry selection);
+
}
private class TimeGraphViewerWrapper implements ITimeGraphWrapper {
viewer.setAutoExpandLevel(level);
}
+ @Override
+ public boolean getExpandedState(ITimeGraphEntry entry) {
+ return viewer.getExpandedState(entry);
+ }
+
+ @Override
+ public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {
+ viewer.setExpandedState(entry, expanded);
+ }
+
@Override
public void performAlign(int offset, int width) {
viewer.performAlign(offset, width);
public void setSelection(ITimeGraphEntry selection) {
viewer.setSelection(selection);
}
+
+ @Override
+ public void selectAndReveal(@NonNull ITimeGraphEntry selection) {
+ viewer.selectAndReveal(selection);
+ }
}
private class TimeGraphComboWrapper implements ITimeGraphWrapper {
combo.setAutoExpandLevel(level);
}
+ @Override
+ public boolean getExpandedState(ITimeGraphEntry entry) {
+ return combo.getExpandedState(entry);
+ }
+
+ @Override
+ public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {
+ combo.setExpandedState(entry, expanded);
+ }
+
TimeGraphCombo getTimeGraphCombo() {
return combo;
}
public void setSelection(ITimeGraphEntry selection) {
combo.setSelection(selection);
}
+
+ @Override
+ public void selectAndReveal(@NonNull ITimeGraphEntry selection) {
+ combo.selectAndReveal(selection);
+ }
}
/**
@Override
public void run() {
- buildEventList(fBuildTrace, fParentTrace, fMonitor);
+ buildEntryList(fBuildTrace, fParentTrace, fMonitor);
synchronized (fBuildThreadMap) {
fBuildThreadMap.remove(fBuildTrace);
}
fDirty.decrementAndGet();
}
+ /**
+ * Applies the results of the ZoomThread calculations.
+ *
+ * Note: This method makes sure that only the results of the last
+ * created ZoomThread are applied.
+ *
+ * @param runnable
+ * the code to run in order to apply the results
+ * @since 2.0
+ */
+ protected void applyResults(Runnable runnable) {
+ synchronized (fZoomThreadResultLock) {
+ if (this == fZoomThread) {
+ runnable.run();
+ }
+ }
+ }
+
/**
* Run the zoom operation.
* @since 2.0
}
/* Refresh the arrows when zooming */
List<ILinkEvent> events = getLinkList(getZoomStartTime(), getZoomEndTime(), getResolution(), getMonitor());
- if (events != null) {
- fTimeGraphWrapper.getTimeGraphViewer().setLinks(events);
- redraw();
- }
/* Refresh the view-specific markers when zooming */
List<IMarkerEvent> markers = new ArrayList<>(getViewMarkerList(getZoomStartTime(), getZoomEndTime(), getResolution(), getMonitor()));
/* Refresh the trace-specific markers when zooming */
markers.addAll(getTraceMarkerList(getZoomStartTime(), getZoomEndTime(), getResolution(), getMonitor()));
- fTimeGraphWrapper.getTimeGraphViewer().setMarkers(markers);
- redraw();
+ applyResults(() -> {
+ if (events != null) {
+ fTimeGraphWrapper.getTimeGraphViewer().setLinks(events);
+ }
+ fTimeGraphWrapper.getTimeGraphViewer().setMarkers(markers);
+ redraw();
+ });
}
private void zoom(@NonNull TimeGraphEntry entry, @NonNull IProgressMonitor monitor) {
if (getZoomStartTime() <= fStartTime && getZoomEndTime() >= fEndTime) {
- entry.setZoomedEventList(null);
+ applyResults(() -> {
+ entry.setZoomedEventList(null);
+ });
} else {
List<ITimeEvent> zoomedEventList = getEventList(entry, getZoomStartTime(), getZoomEndTime(), getResolution(), monitor);
if (zoomedEventList != null) {
- entry.setZoomedEventList(zoomedEventList);
+ applyResults(() -> {
+ entry.setZoomedEventList(zoomedEventList);
+ });
}
}
redraw();
- for (ITimeGraphEntry child : entry.getChildren()) {
+ for (TimeGraphEntry child : entry.getChildren()) {
if (monitor.isCanceled()) {
return;
}
- if (child instanceof TimeGraphEntry) {
- zoom((TimeGraphEntry) child, monitor);
- }
+ zoom(child, monitor);
}
}
super(id);
fPresentation = pres;
fDisplayWidth = Display.getDefault().getBounds().width;
+ fFindTarget = new FindTarget();
}
// ------------------------------------------------------------------------
*
* @param contentProvider
* The filter content provider
- * @since 2.0
+ * @since 1.2
*/
protected void setFilterContentProvider(final ITreeContentProvider contentProvider) {
checkPartNotCreated();
return Messages.AbstractTimeGraphView_PreviousTooltip;
}
+
+ FindTarget getFindTarget() {
+ return fFindTarget;
+ }
+
// ------------------------------------------------------------------------
// ViewPart
// ------------------------------------------------------------------------
if (fColumnComparators != null) {
createColumnSelectionListener(combo.getTreeViewer());
}
+ // Add double click listener to tree viewer
+ createDoubleClickListener(combo.getTreeViewer());
}
fTimeGraphWrapper.setTimeGraphContentProvider(fTimeGraphContentProvider);
fTimeGraphWrapper.setFilterContentProvider(fFilterContentProvider != null ? fFilterContentProvider : fTimeGraphContentProvider);
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));
+ TmfTimeRange range = new TmfTimeRange(TmfTimestamp.fromNanos(startTime), TmfTimestamp.fromNanos(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());
+ ITmfTimestamp startTime = TmfTimestamp.fromNanos(event.getBeginTime());
+ ITmfTimestamp endTime = TmfTimestamp.fromNanos(event.getEndTime());
broadcast(new TmfSelectionRangeUpdatedSignal(AbstractTimeGraphView.this, startTime, endTime));
}
});
marker.setAttribute(ITmfMarker.MARKER_DURATION, Long.toString(bookmark.getDuration()));
marker.setAttribute(IMarker.LOCATION,
NLS.bind(org.eclipse.tracecompass.internal.tmf.ui.Messages.TmfMarker_LocationTimeRange,
- new TmfNanoTimestamp(bookmark.getTime()),
- new TmfNanoTimestamp(bookmark.getTime() + bookmark.getDuration())));
+ TmfTimestamp.fromNanos(bookmark.getTime()),
+ TmfTimestamp.fromNanos(bookmark.getTime() + bookmark.getDuration())));
} else {
marker.setAttribute(IMarker.LOCATION,
NLS.bind(org.eclipse.tracecompass.internal.tmf.ui.Messages.TmfMarker_LocationTime,
- new TmfNanoTimestamp(bookmark.getTime())));
+ TmfTimestamp.fromNanos(bookmark.getTime())));
}
marker.setAttribute(ITmfMarker.MARKER_COLOR, bookmark.getColor().toString());
}
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
createContextMenu();
+ fPartListener = new TimeGraphPartListener();
+ getSite().getPage().addPartListener(fPartListener);
}
@Override
@Override
public void dispose() {
super.dispose();
+ synchronized (fBuildThreadMap) {
+ fBuildThreadMap.values().forEach(buildThread -> {
+ buildThread.cancel();
+ });
+ }
+ if (fZoomThread != null) {
+ fZoomThread.cancel();
+ }
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+ getSite().getPage().removePartListener(fPartListener);
}
/**
*/
@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();
- }
- }
- }
- fMarkerEventSourcesMap.remove(signal.getTrace());
- synchronized (fEntryListMap) {
- fEntryListMap.remove(signal.getTrace());
- }
- fFiltersMap.remove(signal.getTrace());
- fViewContext.remove(signal.getTrace());
+ resetView(signal.getTrace());
if (signal.getTrace() == fTrace) {
fTrace = null;
fEditorFile = null;
fStartTime = SWT.DEFAULT;
fEndTime = SWT.DEFAULT;
- if (fZoomThread != null) {
- fZoomThread.cancel();
- fZoomThread = null;
- }
refresh();
}
}
if (viewTrace == null) {
return;
}
+ resetView(viewTrace);
+
List<IMarkerEventSource> markerEventSources = new ArrayList<>();
synchronized (fBuildThreadMap) {
for (ITmfTrace trace : getTracesToBuild(viewTrace)) {
}
/**
- * Build the entries list to show in this time graph
- *
- * Called from the BuildThread
+ * Build the entry list to show in this time graph view.
+ * <p>
+ * Called from the BuildThread for each trace returned by
+ * {@link #getTracesToBuild(ITmfTrace)}. The full event list is also
+ * normally computed for every entry that is created.
*
* @param trace
* The trace being built
* The parent of the trace set, or the trace itself
* @param monitor
* The progress monitor object
+ * @since 2.0
*/
- protected abstract void buildEventList(@NonNull ITmfTrace trace, @NonNull ITmfTrace parentTrace, @NonNull IProgressMonitor monitor);
+ protected abstract void buildEntryList(@NonNull ITmfTrace trace, @NonNull ITmfTrace parentTrace, @NonNull IProgressMonitor monitor);
/**
- * Gets the list of event for an entry in a given timerange
+ * Gets the list of event for an entry in a given time range.
+ * <p>
+ * Called from the ZoomThread for every entry to update the zoomed event
+ * list. Can be an empty implementation if the view does not support zoomed
+ * event lists. Can also be used to compute the full event list.
*
* @param entry
* The entry to get events for
long resolution = Math.max(1, (clampedEndTime - clampedStartTime) / fDisplayWidth);
fZoomThread = createZoomThread(clampedStartTime, clampedEndTime, resolution, restart);
if (fZoomThread != null) {
- fZoomThread.start();
+ // Don't start a new thread right away if results are being applied
+ // from an old ZoomThread. Otherwise, the old results might
+ // overwrite the new results if it finishes after.
+ synchronized (fZoomThreadResultLock) {
+ fZoomThread.start();
+ }
} else {
fDirty.decrementAndGet();
}
}
}
+ private void createDoubleClickListener(TreeViewer treeViewer) {
+ treeViewer.addDoubleClickListener(event -> {
+ if (event.getSelection() instanceof TreeSelection) {
+ TreeSelection selection = (TreeSelection) event.getSelection();
+ if (selection.getFirstElement() instanceof ITimeGraphEntry) {
+ ITimeGraphEntry entry = (ITimeGraphEntry) selection.getFirstElement();
+ if (entry.hasChildren()) {
+ fTimeGraphWrapper.setExpandedState(entry, !fTimeGraphWrapper.getExpandedState(entry));
+ }
+ }
+ }
+ });
+ }
+
+
private void restoreViewContext() {
TimeGraphCombo combo = getTimeGraphCombo();
ViewContext viewContext = fViewContext.get(fTrace);
}
}
+ /**
+ * Method to reset the view internal data for a given trace.
+ *
+ * When overriding this method make sure to call the super
+ * implementation.
+ *
+ * @param viewTrace
+ * trace to reset the view for.
+ * @since 2.0
+ */
+ protected void resetView(ITmfTrace viewTrace) {
+ if (viewTrace == null) {
+ return;
+ }
+ synchronized (fBuildThreadMap) {
+ for (ITmfTrace trace : getTracesToBuild(viewTrace)) {
+ BuildThread buildThread = fBuildThreadMap.remove(trace);
+ if (buildThread != null) {
+ buildThread.cancel();
+ }
+ }
+ }
+ synchronized (fEntryListMap) {
+ fEntryListMap.remove(viewTrace);
+ }
+ fViewContext.remove(viewTrace);
+ fFiltersMap.remove(viewTrace);
+ fMarkerEventSourcesMap.remove(viewTrace);
+ if (viewTrace == fTrace) {
+ if (fZoomThread != null) {
+ fZoomThread.cancel();
+ fZoomThread = null;
+ }
+ }
+ }
+
private void createContextMenu() {
TimeGraphCombo combo = getTimeGraphCombo();
fEntryMenuManager.setRemoveAllWhenShown(true);
fEntryMenuManager.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(IMenuManager manager) {
- fillTimeGraphEntryContextMenu(fEntryMenuManager);
+ fillTimeGraphEntryContextMenu(fEntryMenuManager);
fEntryMenuManager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
}
});
* @since 2.0
*/
protected void fillTimeGraphEntryContextMenu (@NonNull IMenuManager menuManager) {
+ }
+ /*
+ * Inner classes used for searching
+ */
+ class FindTarget {
+ public ITimeGraphEntry getSelection() {
+ return fTimeGraphWrapper.getSelection();
+ }
+
+ public void selectAndReveal(@NonNull ITimeGraphEntry entry) {
+ fTimeGraphWrapper.selectAndReveal(entry);
+ }
+
+ public ITimeGraphEntry[] getEntries() {
+ TimeGraphViewer viewer = getTimeGraphViewer();
+ return viewer.getTimeGraphContentProvider().getElements(viewer.getInput());
+ }
+
+ public Shell getShell() {
+ return getSite().getShell();
+ }
+ }
+
+ class TimeGraphPartListener implements IPartListener {
+ @Override
+ public void partActivated(IWorkbenchPart part) {
+ if (part == AbstractTimeGraphView.this) {
+ synchronized (FIND_ACTION) {
+ if (fFindActionHandler == null) {
+ fFindActionHandler = new ActionHandler(FIND_ACTION);
+ }
+ if (fFindHandlerActivation == null) {
+ final Object service = PlatformUI.getWorkbench().getService(IHandlerService.class);
+ fFindHandlerActivation = ((IHandlerService) service).activateHandler(ActionFactory.FIND.getCommandId(), fFindActionHandler);
+ }
+ }
+ }
+ // Notify action for all parts
+ FIND_ACTION.partActivated(part);
+ }
+ @Override
+ public void partDeactivated(IWorkbenchPart part) {
+ if ((part == AbstractTimeGraphView.this) && (fFindHandlerActivation != null)) {
+ final Object service = PlatformUI.getWorkbench().getService(IHandlerService.class);
+ ((IHandlerService) service).deactivateHandler(fFindHandlerActivation);
+ fFindHandlerActivation = null;
+ }
+ }
+ @Override
+ public void partBroughtToTop(IWorkbenchPart part) {
+ }
+ @Override
+ public void partClosed(IWorkbenchPart part) {
+ }
+ @Override
+ public void partOpened(IWorkbenchPart part) {
+ }
}
}