/*******************************************************************************
- * Copyright (c) 2012, 2015 Ericsson, École Polytechnique de Montréal
+ * Copyright (c) 2012, 2016 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
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.graphics.Color;
+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.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.tmf.core.resources.ITmfMarker;
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.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 AtomicInteger fDirty = new AtomicInteger();
+ private final Object fZoomThreadResultLock = new Object();
+
/** The selected trace */
private ITmfTrace fTrace;
/** The trace to filters hash map */
private final Map<ITmfTrace, @NonNull ViewerFilter[]> fFiltersMap = new HashMap<>();
+ /** The trace to view context hash map */
+ private final Map<ITmfTrace, ViewContext> fViewContext = new HashMap<>();
+
/** The trace to marker event sources hash map */
private final Map<ITmfTrace, List<IMarkerEventSource>> fMarkerEventSourcesMap = new HashMap<>();
/** The tree column label array, or null if combo is not used */
private String[] fColumns;
+ private Comparator<ITimeGraphEntry>[] fColumnComparators;
+
/** The tree label provider, or null if combo is not used */
private TreeLabelProvider fLabelProvider = null;
private int fAutoExpandLevel = ALL_LEVELS;
- /** The list of color resources created by this view */
- private final List<Color> fColors = new ArrayList<>();
+ /** The default column index for sorting */
+ private int fInitialSortColumn = 0;
+
+ /** The default column index for sorting */
+ private int fCurrentSortColumn = 0;
+
+ /** The current sort direction */
+ private int fSortDirection = SWT.DOWN;
+
+ /** 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);
TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo();
int getAvailableWidth(int requestedOffset);
+
+ 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 int getAvailableWidth(int requestedOffset) {
return viewer.getAvailableWidth(requestedOffset);
}
+
+ @Override
+ public ITimeGraphEntry getSelection() {
+ return viewer.getSelection();
+ }
+
+ @Override
+ 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 int getAvailableWidth(int requestedOffset) {
return combo.getAvailableWidth(requestedOffset);
}
+
+ @Override
+ public ITimeGraphEntry getSelection() {
+ return combo.getTimeGraphViewer().getSelection();
+ }
+
+ @Override
+ 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();
}
// ------------------------------------------------------------------------
* The array of tree column labels
*/
protected void setTreeColumns(final String[] columns) {
+ setTreeColumns(columns, null, 0);
+ }
+
+ /**
+ * Sets the tree column labels.
+ * <p>
+ * This should be called from the constructor.
+ *
+ * @param columns
+ * The array of tree column labels
+ * @param comparators
+ * An array of column comparators for sorting of columns when
+ * clicking on column header
+ * @param initialSortColumn
+ * Index of column to sort initially
+ * @since 2.0
+ */
+ protected void setTreeColumns(final String[] columns, final Comparator<ITimeGraphEntry>[] comparators, int initialSortColumn) {
checkPartNotCreated();
fColumns = columns;
+ fColumnComparators = comparators;
+ fInitialSortColumn = initialSortColumn;
}
/**
*
* @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
// ------------------------------------------------------------------------
combo.setTreeContentProvider(fTimeGraphContentProvider);
combo.setTreeLabelProvider(fLabelProvider);
combo.setTreeColumns(fColumns);
+ 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().getRGBA().toString());
+ marker.setAttribute(ITmfMarker.MARKER_COLOR, bookmark.getColor().toString());
}
}, null);
} catch (CoreException e) {
if (bookmark.getLabel().equals(marker.getAttribute(IMarker.MESSAGE)) &&
Long.toString(bookmark.getTime()).equals(marker.getAttribute(ITmfMarker.MARKER_TIME, (String) null)) &&
Long.toString(bookmark.getDuration()).equals(marker.getAttribute(ITmfMarker.MARKER_DURATION, Long.toString(0))) &&
- bookmark.getColor().getRGBA().toString().equals(marker.getAttribute(ITmfMarker.MARKER_COLOR))) {
+ bookmark.getColor().toString().equals(marker.getAttribute(ITmfMarker.MARKER_COLOR))) {
marker.delete();
break;
}
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 (fBuildThreadMap) {
+ fBuildThreadMap.values().forEach(buildThread -> {
+ buildThread.cancel();
+ });
+ }
+ if (fZoomThread != null) {
+ fZoomThread.cancel();
+ }
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+ getSite().getPage().removePartListener(fPartListener);
}
/**
}
}
- private List<IMarkerEvent> refreshBookmarks(final IFile editorFile) {
+ private static List<IMarkerEvent> refreshBookmarks(final IFile editorFile) {
List<IMarkerEvent> bookmarks = new ArrayList<>();
- for (Color color : fColors) {
- color.dispose();
- }
- fColors.clear();
if (editorFile == null || !editorFile.exists()) {
return bookmarks;
}
int green = Integer.valueOf(matcher.group(2));
int blue = Integer.valueOf(matcher.group(3));
int alpha = Integer.valueOf(matcher.group(4));
- Color color = new Color(Display.getDefault(), red, green, blue, alpha);
- fColors.add(color);
+ RGBA color = new RGBA(red, green, blue, alpha);
bookmarks.add(new MarkerEvent(null, Long.valueOf(time), Long.valueOf(duration), IMarkerEvent.BOOKMARKS, color, label, true));
} catch (NumberFormatException e) {
Activator.getDefault().logError(e.getMessage());
return bookmarks;
}
+
+
// ------------------------------------------------------------------------
// Signal handlers
// ------------------------------------------------------------------------
*/
@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());
+ 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();
}
}
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();
+ final long beginTime = signal.getBeginTime().toNanos();
+ final long endTime = signal.getEndTime().toNanos();
Display.getDefault().asyncExec(new Runnable() {
@Override
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();
+ final long startTime = signal.getCurrentRange().getStartTime().toNanos();
+ final long endTime = signal.getCurrentRange().getEndTime().toNanos();
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (fTrace != null) {
/* save the filters of the previous trace */
fFiltersMap.put(fTrace, fTimeGraphWrapper.getFilters());
+ fViewContext.put(fTrace, new ViewContext(fCurrentSortColumn, fSortDirection, fTimeGraphWrapper.getSelection()));
}
fTrace = trace;
+ restoreViewContext();
fEditorFile = TmfTraceManager.getInstance().getTraceEditorFile(trace);
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();
+ setStartTime(fTrace.getStartTime().toNanos());
+ setEndTime(fTrace.getEndTime().toNanos());
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
if (monitor.isCanceled()) {
break;
}
- markers.addAll(markerEventSource.getMarkerList(checkNotNull(category), startTime, endTime, resolution, monitor));
+ markers.addAll(markerEventSource.getMarkerList(category, startTime, endTime, resolution, monitor));
}
}
return markers;
* @since 2.0
*/
private @NonNull List<String> getMarkerCategories() {
- Set<String> categories = new HashSet<>(getViewMarkerCategories());
+ Set<String> categories = new LinkedHashSet<>(getViewMarkerCategories());
for (IMarkerEventSource markerEventSource : getMarkerEventSources(fTrace)) {
categories.addAll(markerEventSource.getMarkerCategories());
}
private @NonNull List<IMarkerEventSource> getMarkerEventSources(ITmfTrace trace) {
List<IMarkerEventSource> markerEventSources = fMarkerEventSourcesMap.get(trace);
if (markerEventSources == null) {
- markerEventSources = checkNotNull(Collections.<IMarkerEventSource>emptyList());
+ markerEventSources = Collections.emptyList();
}
return markerEventSources;
}
if (fTimeGraphWrapper.isDisposed()) {
return;
}
+ fDirty.incrementAndGet();
+
boolean hasEntries = false;
synchronized (fEntryListMap) {
fEntryList = fEntryListMap.get(fTrace);
hasEntries = !fEntryList.isEmpty();
}
boolean inputChanged = fEntryList != fTimeGraphWrapper.getInput();
- if (inputChanged) {
- fTimeGraphWrapper.setInput(fEntryList);
- /* restore the previously saved filters, if any */
- fTimeGraphWrapper.setFilters(fFiltersMap.get(fTrace));
- fTimeGraphWrapper.getTimeGraphViewer().setLinks(null);
- fTimeGraphWrapper.getTimeGraphViewer().setBookmarks(refreshBookmarks(fEditorFile));
- fTimeGraphWrapper.getTimeGraphViewer().setMarkerCategories(getMarkerCategories());
- fTimeGraphWrapper.getTimeGraphViewer().setMarkers(null);
- } else {
- fTimeGraphWrapper.refresh();
+ TimeGraphCombo combo = getTimeGraphCombo();
+ try {
+ // Set redraw to false to only draw once
+ if (combo != null) {
+ combo.getTreeViewer().getTree().setRedraw(false);
+ }
+ getTimeGraphViewer().getTimeGraphControl().setRedraw(false);
+ if (inputChanged) {
+ fTimeGraphWrapper.setInput(fEntryList);
+ /* restore the previously saved filters, if any */
+ fTimeGraphWrapper.setFilters(fFiltersMap.get(fTrace));
+ fTimeGraphWrapper.getTimeGraphViewer().setLinks(null);
+ fTimeGraphWrapper.getTimeGraphViewer().setBookmarks(refreshBookmarks(fEditorFile));
+ fTimeGraphWrapper.getTimeGraphViewer().setMarkerCategories(getMarkerCategories());
+ fTimeGraphWrapper.getTimeGraphViewer().setMarkers(null);
+ applyViewContext();
+ } else {
+ fTimeGraphWrapper.refresh();
+ }
+ // reveal selection
+ if (fIsRevealSelection) {
+ fIsRevealSelection = false;
+ ITimeGraphEntry entry1 = fTimeGraphWrapper.getSelection();
+ fTimeGraphWrapper.setSelection(entry1);
+ }
+ } finally {
+ if (combo != null) {
+ combo.getTreeViewer().getTree().setRedraw(true);
+ }
+ getTimeGraphViewer().getTimeGraphControl().setRedraw(true);
}
long startBound = (fStartTime == Long.MAX_VALUE ? SWT.DEFAULT : fStartTime);
long endBound = (fEndTime == Long.MIN_VALUE ? SWT.DEFAULT : fEndTime);
fTimeGraphWrapper.getTimeGraphViewer().setTimeBounds(startBound, endBound);
TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
- long selectionBeginTime = fTrace == null ? SWT.DEFAULT : ctx.getSelectionRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
- long selectionEndTime = fTrace == null ? SWT.DEFAULT : ctx.getSelectionRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
- long startTime = fTrace == null ? SWT.DEFAULT : ctx.getWindowRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
- long endTime = fTrace == null ? SWT.DEFAULT : ctx.getWindowRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
+ long selectionBeginTime = fTrace == null ? SWT.DEFAULT : ctx.getSelectionRange().getStartTime().toNanos();
+ long selectionEndTime = fTrace == null ? SWT.DEFAULT : ctx.getSelectionRange().getEndTime().toNanos();
+ long startTime = fTrace == null ? SWT.DEFAULT : ctx.getWindowRange().getStartTime().toNanos();
+ long endTime = fTrace == null ? SWT.DEFAULT : ctx.getWindowRange().getEndTime().toNanos();
startTime = (fStartTime == Long.MAX_VALUE ? SWT.DEFAULT : Math.max(startTime, fStartTime));
endTime = (fEndTime == Long.MIN_VALUE ? SWT.DEFAULT : Math.min(endTime, fEndTime));
fTimeGraphWrapper.getTimeGraphViewer().setSelectionRange(selectionBeginTime, selectionEndTime, false);
if (!zoomThread) {
startZoomThread(startTime, endTime);
}
+ fDirty.decrementAndGet();
}
});
}
});
}
- private static void sortChildren(ITimeGraphEntry entry, Comparator<ITimeGraphEntry> comparator) {
+ private void sortChildren(ITimeGraphEntry entry, Comparator<ITimeGraphEntry> comparator) {
if (entry instanceof TimeGraphEntry) {
((TimeGraphEntry) entry).sortChildren(comparator);
}
* @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);
fDirty.incrementAndGet();
boolean restart = false;
if (fZoomThread != null) {
fZoomThread.cancel();
- if (fZoomThread.fZoomStartTime == startTime && fZoomThread.fZoomEndTime == endTime) {
+ if (fZoomThread.fZoomStartTime == clampedStartTime && fZoomThread.fZoomEndTime == clampedEndTime) {
restart = true;
}
}
- long resolution = Math.max(1, (endTime - startTime) / fDisplayWidth);
- fZoomThread = createZoomThread(startTime, endTime, resolution, restart);
+ 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();
}
* Returns whether or not the time graph view is dirty. The time graph view
* is considered dirty if it has yet to completely update its model.
*
+ * This method is meant to be used by tests in order to know when it is safe
+ * to proceed.
+ *
+ * Note: If a trace is smaller than the initial window range (see
+ * {@link ITmfTrace#getInitialRangeOffset}) this method will return true
+ * forever.
+ *
* @return true if the time graph view has yet to completely update its
* model, false otherwise
* @since 2.0
*/
public boolean isDirty() {
- if (fZoomThread == null) {
+ if (fTrace == null) {
return false;
}
- return fDirty.get() != 0 || fZoomThread.getZoomStartTime() != fTimeGraphWrapper.getTimeGraphViewer().getTime0() || fZoomThread.getZoomEndTime() != fTimeGraphWrapper.getTimeGraphViewer().getTime1();
+
+ TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
+ long startTime = ctx.getWindowRange().getStartTime().toNanos();
+ long endTime = ctx.getWindowRange().getEndTime().toNanos();
+
+ // If the time graph control hasn't updated all the way to the end of
+ // the window range then it's dirty. A refresh should happen later.
+ if (fTimeGraphWrapper.getTimeGraphViewer().getTime0() != startTime || fTimeGraphWrapper.getTimeGraphViewer().getTime1() != endTime) {
+ return true;
+ }
+
+ if (fZoomThread == null) {
+ // The zoom thread is null but we might be just about to create it (refresh called).
+ return fDirty.get() != 0;
+ }
+ // Dirty if the zoom thread is not done or if it hasn't zoomed all the
+ // way to the end of the window range. In the latter case, there should be
+ // a subsequent zoom thread that will be triggered.
+ return fDirty.get() != 0 || fZoomThread.getZoomStartTime() != startTime || fZoomThread.getZoomEndTime() != endTime;
+ }
+
+ private void createColumnSelectionListener(TreeViewer treeViewer) {
+ for (int i = 0; i < fColumnComparators.length; i++) {
+ final int index = i;
+ final Comparator<ITimeGraphEntry> comp = fColumnComparators[index];
+ final Tree tree = treeViewer.getTree();
+ final TreeColumn column = tree.getColumn(i);
+
+ if (comp != null) {
+ column.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ TreeColumn prevSortcolumn = tree.getSortColumn();
+ int direction = tree.getSortDirection();
+ if (prevSortcolumn == column) {
+ direction = (direction == SWT.DOWN) ? SWT.UP : SWT.DOWN;
+ } else {
+ direction = SWT.DOWN;
+ }
+ tree.setSortColumn(column);
+ tree.setSortDirection(direction);
+ fSortDirection = direction;
+ fCurrentSortColumn = index;
+ Comparator<ITimeGraphEntry> comparator = comp;
+
+ if (comparator instanceof ITimeGraphEntryComparator) {
+ ((ITimeGraphEntryComparator) comparator).setDirection(direction);
+ }
+ if (direction != SWT.DOWN) {
+ comparator = checkNotNull(Collections.reverseOrder(comparator));
+ }
+ setEntryComparator(comparator);
+ fIsRevealSelection = true;
+ if (fTimeGraphWrapper instanceof TimeGraphComboWrapper) {
+ ((TimeGraphComboWrapper) fTimeGraphWrapper).getTreeViewer().getControl().setFocus();
+ }
+ refresh();
+ }
+ });
+ }
+ }
+ }
+
+ 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);
+ if (combo != null) {
+ if (fColumnComparators != null) {
+ // restore sort settings
+ fSortDirection = SWT.DOWN;
+ fCurrentSortColumn = fInitialSortColumn;
+ if (viewContext != null) {
+ fSortDirection = viewContext.getSortDirection();
+ fCurrentSortColumn = viewContext.getSortColumn();
+ }
+ if ((fCurrentSortColumn < fColumnComparators.length) && (fColumnComparators[fCurrentSortColumn] != null)) {
+ Comparator<ITimeGraphEntry> comparator = fColumnComparators[fCurrentSortColumn];
+ if (comparator instanceof ITimeGraphEntryComparator) {
+ ((ITimeGraphEntryComparator) comparator).setDirection(fSortDirection);
+ }
+ if (fSortDirection != SWT.DOWN) {
+ comparator = checkNotNull(Collections.reverseOrder(comparator));
+ }
+ setEntryComparator(comparator);
+ }
+ }
+ }
+ }
+
+ private void applyViewContext() {
+ TimeGraphCombo combo = getTimeGraphCombo();
+ ViewContext viewContext = fViewContext.get(fTrace);
+ if (combo != null) {
+ TreeViewer treeViewer = combo.getTreeViewer();
+ final Tree tree = treeViewer.getTree();
+ final TreeColumn column = tree.getColumn(fCurrentSortColumn);
+ tree.setSortDirection(fSortDirection);
+ tree.setSortColumn(column);
+ combo.getTreeViewer().getControl().setFocus();
+ }
+ // restore and reveal selection
+ if ((viewContext != null) && (viewContext.getSelection() != null)) {
+ fTimeGraphWrapper.setSelection(viewContext.getSelection());
+ }
+ fViewContext.remove(fTrace);
}
+ private static class ViewContext {
+ private int fSortColumnIndex;
+ private int fSortDirection;
+ private @Nullable ITimeGraphEntry fSelection;
+
+ ViewContext(int sortColunm, int sortDirection, ITimeGraphEntry selection) {
+ fSortColumnIndex = sortColunm;
+ fSortDirection = sortDirection;
+ fSelection = selection;
+ }
+ /**
+ * @return the sortColumn
+ */
+ public int getSortColumn() {
+ return fSortColumnIndex;
+ }
+ /**
+ * @return the sortDirection
+ */
+ public int getSortDirection() {
+ return fSortDirection;
+ }
+ /**
+ * @return the selection
+ */
+ public ITimeGraphEntry getSelection() {
+ 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 (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);
+ 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) {
+ }
+ }
}