import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
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.SWT;
+import org.eclipse.swt.events.MenuDetectEvent;
+import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGBA;
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.common.core.NonNullUtils;
+import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.tmf.core.resources.ITmfMarker;
+import org.eclipse.tracecompass.tmf.core.signal.TmfMarkerEventSourceUpdatedSignal;
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.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.model.ITimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.MarkerEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
+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 static final Pattern RGBA_PATTERN = Pattern.compile("RGBA \\{(\\d+), (\\d+), (\\d+), (\\d+)\\}"); //$NON-NLS-1$
+ private static final Logger LOGGER = TraceCompassLog.getLogger(AbstractTimeGraphView.class);
+ private static final String LOG_STRING_WITH_PARAM = "[TimeGraphView:%s] viewId=%s, %s"; //$NON-NLS-1$
+ private static final String LOG_STRING = "[TimeGraphView:%s] viewId=%s"; //$NON-NLS-1$
+
/**
* Redraw state enum
*/
private AtomicInteger fDirty = new AtomicInteger();
+ private final Object fZoomThreadResultLock = new Object();
+
/** The selected trace */
private ITmfTrace fTrace;
private final Map<ITmfTrace, List<IMarkerEventSource>> fMarkerEventSourcesMap = new HashMap<>();
/** The trace to build thread hash map */
- private final Map<ITmfTrace, BuildThread> fBuildThreadMap = new HashMap<>();
+ private final Map<ITmfTrace, Job> fBuildJobMap = new HashMap<>();
/** The start time */
private long fStartTime = SWT.DEFAULT;
/** Flag to indicate to reveal selection */
private volatile boolean fIsRevealSelection = false;
+ /**
+ * Menu Manager for context-sensitive menu for time graph entries.
+ * This will be used on the tree viewer in case of the time graph combo
+ * or the on the namespace in case of a single time graph viewer.
+ */
+ 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);
+ }
}
/**
}
- private class BuildThread extends Thread {
+ // TODO: This can implement ICoreRunnable once support for Eclipse 4.5. is not necessary anymore.
+ private class BuildRunnable {
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$
+ public BuildRunnable(final @NonNull ITmfTrace trace, final @NonNull ITmfTrace parentTrace) {
fBuildTrace = trace;
fParentTrace = parentTrace;
- fMonitor = new NullProgressMonitor();
}
- @Override
- public void run() {
- buildEventList(fBuildTrace, fParentTrace, fMonitor);
- synchronized (fBuildThreadMap) {
- fBuildThreadMap.remove(fBuildTrace);
+ public void run(IProgressMonitor monitor) {
+ LOGGER.info(() -> getLogMessage("BuildThreadStart", "trace=" + fBuildTrace.getName())); //$NON-NLS-1$ //$NON-NLS-2$
+
+ buildEntryList(fBuildTrace, fParentTrace, NonNullUtils.checkNotNull(monitor));
+ synchronized (fBuildJobMap) {
+ fBuildJobMap.remove(fBuildTrace);
}
- }
- public void cancel() {
- fMonitor.setCanceled(true);
+ LOGGER.info(() -> getLogMessage("BuildThreadEnd", null)); //$NON-NLS-1$
}
}
@Override
public final void run() {
+ LOGGER.info(() -> getLogMessage("ZoomThreadStart", "start=" + fZoomStartTime + ", end=" + fZoomEndTime)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
doRun();
fDirty.decrementAndGet();
+
+ LOGGER.info(() -> getLogMessage("ZoomThreadEnd", null)); //$NON-NLS-1$
+ }
+
+ /**
+ * 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();
+ }
+ }
}
/**
@Override
public void doRun() {
+ LOGGER.config(() -> getLogMessage("ZoomThreadGettingStates", null)); //$NON-NLS-1$
+
for (TimeGraphEntry entry : fZoomEntryList) {
if (getMonitor().isCanceled()) {
+ LOGGER.info(() -> getLogMessage("ZoomThreadCanceled", null)); //$NON-NLS-1$
return;
}
if (entry == null) {
zoom(entry, getMonitor());
}
/* Refresh the arrows when zooming */
+ LOGGER.config(() -> getLogMessage("ZoomThreadGettingLinks", null)); //$NON-NLS-1$
List<ILinkEvent> events = getLinkList(getZoomStartTime(), getZoomEndTime(), getResolution(), getMonitor());
- if (events != null) {
- fTimeGraphWrapper.getTimeGraphViewer().setLinks(events);
- redraw();
- }
+
/* Refresh the view-specific markers when zooming */
+ LOGGER.config(() -> getLogMessage("ZoomThreadGettingMarkers", null)); //$NON-NLS-1$
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().setMarkerCategories(getMarkerCategories());
+ 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;
+ }
+
+ /**
+ * Formats a log message for this class
+ *
+ * @param event
+ * The event to log, that will be appended to the class name to
+ * make the full event name
+ * @param parameters
+ * The string of extra parameters to add to the log message, in
+ * the format name=value[, name=value]*, or <code>null</code> for
+ * no params
+ * @return The complete log message for this class
+ */
+ private String getLogMessage(String event, @Nullable String parameters) {
+ if (parameters == null) {
+ return String.format(LOG_STRING, event, getViewId());
+ }
+ return String.format(LOG_STRING_WITH_PARAM, event, getViewId(), parameters);
+ }
+
// ------------------------------------------------------------------------
// 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());
}
getSite().setSelectionProvider(fTimeGraphWrapper.getSelectionProvider());
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
+
+ createContextMenu();
+ fPartListener = new TimeGraphPartListener();
+ getSite().getPage().addPartListener(fPartListener);
}
@Override
@Override
public void dispose() {
super.dispose();
+ synchronized (fBuildJobMap) {
+ fBuildJobMap.values().forEach(buildJob -> {
+ buildJob.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;
- }
+ setStartTime(SWT.DEFAULT);
+ setEndTime(SWT.DEFAULT);
refresh();
}
}
fTimeGraphWrapper.refresh();
}
+ /**
+ * A marker event source has been updated
+ *
+ * @param signal
+ * the signal
+ * @since 2.1
+ */
+ @TmfSignalHandler
+ public void markerEventSourceUpdated(final TmfMarkerEventSourceUpdatedSignal signal) {
+ getTimeGraphViewer().setMarkerCategories(getMarkerCategories());
+ getTimeGraphViewer().setMarkers(null);
+ refresh();
+ }
+
// ------------------------------------------------------------------------
// Internal
// ------------------------------------------------------------------------
fViewContext.put(fTrace, new ViewContext(fCurrentSortColumn, fSortDirection, fTimeGraphWrapper.getSelection()));
}
fTrace = trace;
+
+ LOGGER.info(() -> getLogMessage("LoadingTrace", "trace=" + trace.getName())); //$NON-NLS-1$ //$NON-NLS-2$
+
restoreViewContext();
fEditorFile = TmfTraceManager.getInstance().getTraceEditorFile(trace);
synchronized (fEntryListMap) {
if (fEntryList == null) {
rebuild();
} else {
- fStartTime = fTrace.getStartTime().toNanos();
- fEndTime = fTrace.getEndTime().toNanos();
+ setStartTime(fTrace.getStartTime().toNanos());
+ setEndTime(fTrace.getEndTime().toNanos());
refresh();
}
}
if (viewTrace == null) {
return;
}
+ resetView(viewTrace);
+
List<IMarkerEventSource> markerEventSources = new ArrayList<>();
- synchronized (fBuildThreadMap) {
+ synchronized (fBuildJobMap) {
for (ITmfTrace trace : getTracesToBuild(viewTrace)) {
if (trace == null) {
break;
}
markerEventSources.addAll(TmfTraceAdapterManager.getAdapters(trace, IMarkerEventSource.class));
- BuildThread buildThread = new BuildThread(trace, viewTrace, getName());
- fBuildThreadMap.put(trace, buildThread);
- buildThread.start();
+ Job buildJob = new Job(getTitle() + Messages.AbstractTimeGraphView_BuildJob) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ new BuildRunnable(trace, viewTrace).run(monitor);
+ monitor.done();
+ return Status.OK_STATUS;
+ }
+ };
+ fBuildJobMap.put(trace, buildJob);
+ buildJob.schedule();
}
}
fMarkerEventSourcesMap.put(viewTrace, markerEventSources);
* some of which may receive events in live streaming mode.
*
* @param trace
- * The trace associated with this view
+ * The trace associated with this view, can be null
* @return List of traces with data to display
*/
- protected @NonNull Iterable<ITmfTrace> getTracesToBuild(@NonNull ITmfTrace trace) {
+ protected @NonNull Iterable<ITmfTrace> getTracesToBuild(@Nullable ITmfTrace trace) {
return TmfTraceManager.getTraceSet(trace);
}
/**
- * Build the entries list to show in this time graph
+ * Build the entry list to show in this time graph view.
+ * <p>
+ * Called from the BuildJob for each trace returned by
+ * {@link #getTracesToBuild(ITmfTrace)}.
+ * <p>
+ * Root entries must be added to the entry list by calling the
+ * {@link #addToEntryList(ITmfTrace, List)} method with the list of entries
+ * to add and where the trace in parameter should be the parentTrace.
+ * Entries that are children of other entries will be automatically picked
+ * up after refreshing the root entries.
+ * <p>
+ * The full event list is also normally computed for every entry that is
+ * created. It should be set for each entry by calling the
+ * {@link TimeGraphEntry#setEventList(List)}. These full event lists will be
+ * used to display something while the zoomed event lists are being
+ * calculated when the window range is updated. Also, when fully zoomed out,
+ * it is this list of events that is displayed.
+ * <p>
+ * Also, when all the entries have been added and their events set, this
+ * method can finish by calling the refresh() method like this:
*
- * Called from the BuildThread
+ * <pre>
+ * if (parentTrace.equals(getTrace())) {
+ * refresh();
+ * }
+ * </pre>
*
* @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
* Get the list of current marker categories.
*
* @return The list of marker categories
- * @since 2.0
+ * @since 2.1
*/
- private @NonNull List<String> getMarkerCategories() {
+ protected @NonNull List<String> getMarkerCategories() {
Set<String> categories = new LinkedHashSet<>(getViewMarkerCategories());
for (IMarkerEventSource markerEventSource : getMarkerEventSources(fTrace)) {
categories.addAll(markerEventSource.getMarkerCategories());
* Refresh the display
*/
protected void refresh() {
+ LOGGER.info(() -> getLogMessage("RefreshRequested", null)); //$NON-NLS-1$
final boolean zoomThread = Thread.currentThread() instanceof ZoomThread;
TmfUiRefreshHandler.getInstance().queueUpdate(this, new Runnable() {
@Override
public void run() {
+ LOGGER.info(() -> getLogMessage("RefreshStart", null)); //$NON-NLS-1$
if (fTimeGraphWrapper.isDisposed()) {
return;
}
startZoomThread(startTime, endTime);
}
fDirty.decrementAndGet();
+ LOGGER.info(() -> getLogMessage("RefreshEnd", null)); //$NON-NLS-1$
}
});
}
return;
}
}
+ LOGGER.info(() -> getLogMessage("RedrawRequested", null)); //$NON-NLS-1$
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
+ LOGGER.info(() -> getLogMessage("RedrawStart", null)); //$NON-NLS-1$
if (fTimeGraphWrapper.isDisposed()) {
return;
}
fRedrawState = State.IDLE;
}
}
+ LOGGER.info(() -> getLogMessage("RedrawEnd", null)); //$NON-NLS-1$
}
});
}
* @since 2.0
*/
protected final void startZoomThread(long startTime, long endTime) {
- long clampedStartTime = Math.min(Math.max(startTime, fStartTime), fEndTime);
- long clampedEndTime = Math.max(Math.min(endTime, fEndTime), fStartTime);
+ long clampedStartTime = (fStartTime == Long.MAX_VALUE ? startTime : Math.min(Math.max(startTime, fStartTime), fEndTime));
+ long clampedEndTime = (fEndTime == Long.MIN_VALUE ? endTime : Math.max(Math.min(endTime, fEndTime), fStartTime));
fDirty.incrementAndGet();
boolean restart = false;
if (fZoomThread != null) {
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);
return fSelection;
}
}
+
+ /**
+ * 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 (fBuildJobMap) {
+ for (ITmfTrace trace : getTracesToBuild(viewTrace)) {
+ Job buildJob = fBuildJobMap.remove(trace);
+ if (buildJob != null) {
+ buildJob.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);
+ if (combo != null) {
+ TreeViewer treeViewer = combo.getTreeViewer();
+ Tree tree = treeViewer.getTree();
+ Menu menu = fEntryMenuManager.createContextMenu(tree);
+ tree.setMenu(menu);
+ } else {
+ TimeGraphControl timeGraphControl = getTimeGraphViewer().getTimeGraphControl();
+ final Menu entryMenu = fEntryMenuManager.createContextMenu(timeGraphControl);
+ timeGraphControl.addTimeGraphEntryMenuListener(new MenuDetectListener() {
+ @Override
+ public void menuDetected(MenuDetectEvent event) {
+ Point p = timeGraphControl.toControl(event.x, event.y);
+ /*
+ * The TimeGraphControl will call the TimeGraphEntryMenuListener
+ * before the TimeEventMenuListener. If the event is
+ * triggered on the namespace then show the menu else
+ * clear the menu.
+ */
+ if (p.x < getTimeGraphViewer().getNameSpace()) {
+ timeGraphControl.setMenu(entryMenu);
+ } else {
+ timeGraphControl.setMenu(null);
+ event.doit = false;
+ }
+ }
+ });
+ }
+ fEntryMenuManager.addMenuListener(new IMenuListener() {
+ @Override
+ public void menuAboutToShow(IMenuManager manager) {
+ fillTimeGraphEntryContextMenu(fEntryMenuManager);
+ fEntryMenuManager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
+ }
+ });
+ getSite().registerContextMenu(fEntryMenuManager, fTimeGraphWrapper.getSelectionProvider());
+ }
+
+ /**
+ * Fill context menu
+ *
+ * @param menuManager
+ * a menuManager to fill
+ * @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) {
+ }
+ }
}