/*******************************************************************************
- * 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
package org.eclipse.tracecompass.tmf.ui.views.timegraph;
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
-
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IMarkerDelta;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
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.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.ITreeContentProvider;
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.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.RGBA;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
+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.TmfSelectionRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal;
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.TmfTraceAdapterManager;
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.ITimeGraphBookmarkListener;
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.TimeGraphBookmarkEvent;
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.TimeGraphViewer;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEventSource;
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.MarkerEvent;
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;
* 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 {
+public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeAligned, IResourceChangeListener {
/** Constant indicating that all levels of the time graph should be expanded */
protected static final int ALL_LEVELS = AbstractTreeViewer.ALL_LEVELS;
+ private static final Pattern RGBA_PATTERN = Pattern.compile("RGBA \\{(\\d+), (\\d+), (\\d+), (\\d+)\\}"); //$NON-NLS-1$
+
/**
* Redraw state enum
*/
/** The timegraph wrapper */
private ITimeGraphWrapper fTimeGraphWrapper;
+ private AtomicInteger fDirty = new AtomicInteger();
+
/** The selected trace */
private ITmfTrace fTrace;
+ /** The selected trace editor file*/
+ private IFile fEditorFile;
+
/** The timegraph entry list */
private List<TimeGraphEntry> fEntryList;
private final Map<ITmfTrace, List<TimeGraphEntry>> fEntryListMap = new HashMap<>();
/** The trace to filters hash map */
- private final Map<ITmfTrace, ViewerFilter[]> fFiltersMap = new HashMap<>();
+ 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 trace to build thread hash map */
private final Map<ITmfTrace, BuildThread> fBuildThreadMap = 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;
/** The pack done flag */
private boolean fPackDone = false;
+ /** The filter content provider, or null if filter is not used */
+ private ITreeContentProvider fFilterContentProvider;
+
/** The filter label provider, or null if filter is not used */
private TreeLabelProvider fFilterLabelProvider;
private int fAutoExpandLevel = ALL_LEVELS;
+ /** 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;
+
// ------------------------------------------------------------------------
// Classes
// ------------------------------------------------------------------------
Object getInput();
- void setFilters(ViewerFilter[] filters);
+ void setFilters(@NonNull ViewerFilter[] filters);
- ViewerFilter[] getFilters();
+ @NonNull ViewerFilter[] getFilters();
void redraw();
TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo();
int getAvailableWidth(int requestedOffset);
+
+ ITimeGraphEntry getSelection();
+
+ void setSelection(ITimeGraphEntry selection);
}
private class TimeGraphViewerWrapper implements ITimeGraphWrapper {
}
@Override
- public void setFilters(ViewerFilter[] filters) {
+ public void setFilters(@NonNull ViewerFilter[] filters) {
viewer.setFilters(filters);
}
@Override
- public ViewerFilter[] getFilters() {
+ public @NonNull ViewerFilter[] getFilters() {
return viewer.getFilters();
}
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);
+ }
}
private class TimeGraphComboWrapper implements ITimeGraphWrapper {
}
@Override
- public void setFilters(ViewerFilter[] filters) {
+ public void setFilters(@NonNull ViewerFilter[] filters) {
combo.setFilters(filters);
}
@Override
- public ViewerFilter[] getFilters() {
+ public @NonNull ViewerFilter[] getFilters() {
return combo.getFilters();
}
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);
+ }
}
/**
/**
* Zoom thread
- * @since 2.0
+ * @since 1.1
*/
protected abstract class ZoomThread extends Thread {
private final long fZoomStartTime;
public void cancel() {
fMonitor.setCanceled(true);
}
+
+ @Override
+ public final void run() {
+ doRun();
+ fDirty.decrementAndGet();
+ }
+
+ /**
+ * Run the zoom operation.
+ * @since 2.0
+ */
+ public abstract void doRun();
}
private class ZoomThreadByEntry extends ZoomThread {
}
@Override
- public void run() {
+ public void doRun() {
for (TimeGraphEntry entry : fZoomEntryList) {
if (getMonitor().isCanceled()) {
return;
fTimeGraphWrapper.getTimeGraphViewer().setLinks(events);
redraw();
}
- /* Refresh the markers when zooming */
- List<IMarkerEvent> markers = getMarkerList(getZoomStartTime(), getZoomEndTime(), getResolution(), getMonitor());
- if (markers != null) {
- fTimeGraphWrapper.getTimeGraphViewer().getTimeGraphControl().setMarkers(markers);
- 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();
}
private void zoom(@NonNull TimeGraphEntry entry, @NonNull IProgressMonitor monitor) {
/**
* Sets the tree column labels.
+ * <p>
* This should be called from the constructor.
*
* @param columns
* 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;
}
/**
* Sets the tree label provider.
+ * <p>
* This should be called from the constructor.
*
* @param tlp
* The tree label provider
*/
protected void setTreeLabelProvider(final TreeLabelProvider tlp) {
+ checkPartNotCreated();
fLabelProvider = tlp;
}
/**
- * Sets the time graph content provider. This should be called from the
- * constructor.
+ * Sets the time graph content provider.
+ * <p>
+ * This should be called from the constructor.
*
* @param tgcp
* The time graph content provider
* @since 1.0
*/
protected void setTimeGraphContentProvider(final @NonNull ITimeGraphContentProvider tgcp) {
+ checkPartNotCreated();
fTimeGraphContentProvider = tgcp;
}
/**
* Sets the relative weight of each part of the time graph combo.
+ * <p>
* 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) {
+ checkPartNotCreated();
fWeight = weights;
}
/**
* Sets the filter column labels.
+ * <p>
* This should be called from the constructor.
*
* @param filterColumns
* The array of filter column labels
*/
protected void setFilterColumns(final String[] filterColumns) {
+ checkPartNotCreated();
fFilterColumns = filterColumns;
}
+ /**
+ * Sets the filter content provider.
+ * <p>
+ * This should be called from the constructor.
+ *
+ * @param contentProvider
+ * The filter content provider
+ * @since 2.0
+ */
+ protected void setFilterContentProvider(final ITreeContentProvider contentProvider) {
+ checkPartNotCreated();
+ fFilterContentProvider = contentProvider;
+ }
+
/**
* Sets the filter label provider.
+ * <p>
* This should be called from the constructor.
*
* @param labelProvider
* The filter label provider
*/
protected void setFilterLabelProvider(final TreeLabelProvider labelProvider) {
+ checkPartNotCreated();
fFilterLabelProvider = labelProvider;
}
+ private void checkPartNotCreated() {
+ if (getParentComposite() != null) {
+ throw new IllegalStateException("This method must be called before createPartControl."); //$NON-NLS-1$
+ }
+ }
+
/**
* Gets the display width
*
}
/**
- * Sets the comparator class for the entries
+ * Sets the comparator class for the entries.
+ * <p>
+ * This comparator will apply recursively to entries that implement
+ * {@link TimeGraphEntry#sortChildren(Comparator)}.
*
* @param comparator
* A comparator object
combo.setTreeContentProvider(fTimeGraphContentProvider);
combo.setTreeLabelProvider(fLabelProvider);
combo.setTreeColumns(fColumns);
+ if (fColumnComparators != null) {
+ createColumnSelectionListener(combo.getTreeViewer());
+ }
}
fTimeGraphWrapper.setTimeGraphContentProvider(fTimeGraphContentProvider);
- fTimeGraphWrapper.setFilterContentProvider(fTimeGraphContentProvider);
+ fTimeGraphWrapper.setFilterContentProvider(fFilterContentProvider != null ? fFilterContentProvider : fTimeGraphContentProvider);
fTimeGraphWrapper.setFilterLabelProvider(fFilterLabelProvider);
fTimeGraphWrapper.setFilterColumns(fFilterColumns);
}
});
+ fTimeGraphWrapper.getTimeGraphViewer().addBookmarkListener(new ITimeGraphBookmarkListener() {
+ @Override
+ public void bookmarkAdded(final TimeGraphBookmarkEvent event) {
+ try {
+ ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
+ @Override
+ public void run(IProgressMonitor monitor) throws CoreException {
+ IMarkerEvent bookmark = event.getBookmark();
+ IMarker marker = fEditorFile.createMarker(IMarker.BOOKMARK);
+ marker.setAttribute(IMarker.MESSAGE, bookmark.getLabel());
+ marker.setAttribute(ITmfMarker.MARKER_TIME, Long.toString(bookmark.getTime()));
+ if (bookmark.getDuration() > 0) {
+ 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())));
+ } else {
+ marker.setAttribute(IMarker.LOCATION,
+ NLS.bind(org.eclipse.tracecompass.internal.tmf.ui.Messages.TmfMarker_LocationTime,
+ new TmfNanoTimestamp(bookmark.getTime())));
+ }
+ marker.setAttribute(ITmfMarker.MARKER_COLOR, bookmark.getColor().toString());
+ }
+ }, null);
+ } catch (CoreException e) {
+ Activator.getDefault().logError(e.getMessage());
+ }
+ }
+
+ @Override
+ public void bookmarkRemoved(TimeGraphBookmarkEvent event) {
+ try {
+ IMarkerEvent bookmark = event.getBookmark();
+ IMarker[] markers = fEditorFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO);
+ for (IMarker marker : markers) {
+ 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().toString().equals(marker.getAttribute(ITmfMarker.MARKER_COLOR))) {
+ marker.delete();
+ break;
+ }
+ }
+ } catch (CoreException e) {
+ Activator.getDefault().logError(e.getMessage());
+ }
+ }
+ });
+
fTimeGraphWrapper.getTimeGraphViewer().setTimeFormat(TimeFormat.CALENDAR);
IStatusLineManager statusLineManager = getViewSite().getActionBars().getStatusLineManager();
// make selection available to other views
getSite().setSelectionProvider(fTimeGraphWrapper.getSelectionProvider());
+
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
}
@Override
fTimeGraphWrapper.setFocus();
}
+ @Override
+ public void dispose() {
+ super.dispose();
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+ }
+
+ /**
+ * @since 2.0
+ */
+ @Override
+ public void resourceChanged(final IResourceChangeEvent event) {
+ for (final IMarkerDelta delta : event.findMarkerDeltas(IMarker.BOOKMARK, false)) {
+ if (delta.getResource().equals(fEditorFile)) {
+ fTimeGraphWrapper.getTimeGraphViewer().setBookmarks(refreshBookmarks(fEditorFile));
+ redraw();
+ return;
+ }
+ }
+ }
+
+ private static List<IMarkerEvent> refreshBookmarks(final IFile editorFile) {
+ List<IMarkerEvent> bookmarks = new ArrayList<>();
+ if (editorFile == null || !editorFile.exists()) {
+ return bookmarks;
+ }
+ try {
+ IMarker[] markers = editorFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO);
+ for (IMarker marker : markers) {
+ String label = marker.getAttribute(IMarker.MESSAGE, (String) null);
+ String time = marker.getAttribute(ITmfMarker.MARKER_TIME, (String) null);
+ String duration = marker.getAttribute(ITmfMarker.MARKER_DURATION, Long.toString(0));
+ String rgba = marker.getAttribute(ITmfMarker.MARKER_COLOR, (String) null);
+ if (label != null && time != null && rgba != null) {
+ Matcher matcher = RGBA_PATTERN.matcher(rgba);
+ if (matcher.matches()) {
+ try {
+ int red = Integer.valueOf(matcher.group(1));
+ int green = Integer.valueOf(matcher.group(2));
+ int blue = Integer.valueOf(matcher.group(3));
+ int alpha = Integer.valueOf(matcher.group(4));
+ 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());
+ }
+ }
+ }
+ }
+ } catch (CoreException e) {
+ Activator.getDefault().logError(e.getMessage());
+ }
+ return bookmarks;
+ }
+
+
+
// ------------------------------------------------------------------------
// Signal handlers
// ------------------------------------------------------------------------
}
}
}
+ fMarkerEventSourcesMap.remove(signal.getTrace());
synchronized (fEntryListMap) {
fEntryListMap.remove(signal.getTrace());
}
fFiltersMap.remove(signal.getTrace());
+ fViewContext.remove(signal.getTrace());
if (signal.getTrace() == fTrace) {
fTrace = null;
+ fEditorFile = null;
fStartTime = SWT.DEFAULT;
fEndTime = SWT.DEFAULT;
if (fZoomThread != null) {
if (beginTime == endTime) {
fTimeGraphWrapper.getTimeGraphViewer().setSelectedTime(beginTime, true);
} else {
- fTimeGraphWrapper.getTimeGraphViewer().setSelectionRange(beginTime, endTime);
+ fTimeGraphWrapper.getTimeGraphViewer().setSelectionRange(beginTime, endTime, true);
}
- synchingToTime(fTimeGraphWrapper.getTimeGraphViewer().getTime0());
+ synchingToTime(fTimeGraphWrapper.getTimeGraphViewer().getSelectionBegin());
}
});
}
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) {
if (viewTrace == null) {
return;
}
+ List<IMarkerEventSource> markerEventSources = new ArrayList<>();
synchronized (fBuildThreadMap) {
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();
}
}
+ fMarkerEventSourcesMap.put(viewTrace, markerEventSources);
}
/**
* The progress monitor object
* @return The list of events for the entry
*/
- protected abstract @Nullable List<ITimeEvent> getEventList(@NonNull TimeGraphEntry entry,
+ protected abstract @Nullable List<@NonNull ITimeEvent> getEventList(@NonNull TimeGraphEntry entry,
long startTime, long endTime, long resolution,
@NonNull IProgressMonitor monitor);
* The progress monitor object
* @return The list of link events
*/
- protected @Nullable List<ILinkEvent> getLinkList(long startTime, long endTime,
+ protected @Nullable List<@NonNull ILinkEvent> getLinkList(long startTime, long endTime,
long resolution, @NonNull IProgressMonitor monitor) {
return new ArrayList<>();
}
/**
- * Gets the list of markers for a trace in a given time range. Default
- * implementation returns an empty list.
+ * Gets the list of view-specific marker categories. Default implementation
+ * returns an empty list.
+ *
+ * @return The list of marker categories
+ * @since 2.0
+ */
+ protected @NonNull List<String> getViewMarkerCategories() {
+ return new ArrayList<>();
+ }
+
+ /**
+ * Gets the list of view-specific markers for a trace in a given time range.
+ * Default implementation returns an empty list.
*
* @param startTime
* Start of the time range
* @return The list of marker events
* @since 2.0
*/
- protected @Nullable List<IMarkerEvent> getMarkerList(long startTime, long endTime,
+ protected @NonNull List<IMarkerEvent> getViewMarkerList(long startTime, long endTime,
long resolution, @NonNull IProgressMonitor monitor) {
return new ArrayList<>();
}
+ /**
+ * Gets the list of trace-specific markers for a trace in a given time range.
+ *
+ * @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 marker events
+ * @since 2.0
+ */
+ protected @NonNull List<IMarkerEvent> getTraceMarkerList(long startTime, long endTime,
+ long resolution, @NonNull IProgressMonitor monitor) {
+ List<IMarkerEvent> markers = new ArrayList<>();
+ for (IMarkerEventSource markerEventSource : getMarkerEventSources(fTrace)) {
+ for (String category : markerEventSource.getMarkerCategories()) {
+ if (monitor.isCanceled()) {
+ break;
+ }
+ markers.addAll(markerEventSource.getMarkerList(checkNotNull(category), startTime, endTime, resolution, monitor));
+ }
+ }
+ return markers;
+ }
+
+ /**
+ * Get the list of current marker categories.
+ *
+ * @return The list of marker categories
+ * @since 2.0
+ */
+ private @NonNull List<String> getMarkerCategories() {
+ Set<String> categories = new LinkedHashSet<>(getViewMarkerCategories());
+ for (IMarkerEventSource markerEventSource : getMarkerEventSources(fTrace)) {
+ categories.addAll(markerEventSource.getMarkerCategories());
+ }
+ return new ArrayList<>(categories);
+ }
+
+ /**
+ * Gets the list of marker event sources for a given trace.
+ *
+ * @param trace
+ * The trace
+ * @return The list of marker event sources
+ * @since 2.0
+ */
+ private @NonNull List<IMarkerEventSource> getMarkerEventSources(ITmfTrace trace) {
+ List<IMarkerEventSource> markerEventSources = fMarkerEventSourcesMap.get(trace);
+ if (markerEventSources == null) {
+ markerEventSources = checkNotNull(Collections.<IMarkerEventSource>emptyList());
+ }
+ return markerEventSources;
+ }
+
/**
* Refresh the display
*/
} else if (fEntryComparator != null) {
List<TimeGraphEntry> list = new ArrayList<>(fEntryList);
Collections.sort(list, fEntryComparator);
+ for (ITimeGraphEntry entry : list) {
+ sortChildren(entry, fEntryComparator);
+ }
fEntryList.clear();
fEntryList.addAll(list);
}
- hasEntries = fEntryList.size() != 0;
+ hasEntries = !fEntryList.isEmpty();
}
- if (fEntryList != fTimeGraphWrapper.getInput()) {
- fTimeGraphWrapper.setInput(fEntryList);
- /* restore the previously saved filters, if any */
- fTimeGraphWrapper.setFilters(fFiltersMap.get(fTrace));
- fTimeGraphWrapper.getTimeGraphViewer().setLinks(null);
- } else {
- fTimeGraphWrapper.refresh();
+ boolean inputChanged = fEntryList != fTimeGraphWrapper.getInput();
+ 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);
long endTime = fTrace == null ? SWT.DEFAULT : ctx.getWindowRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
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);
+ fTimeGraphWrapper.getTimeGraphViewer().setSelectionRange(selectionBeginTime, selectionEndTime, false);
fTimeGraphWrapper.getTimeGraphViewer().setStartFinishTime(startTime, endTime);
+ if (inputChanged && selectionBeginTime != SWT.DEFAULT) {
+ synchingToTime(selectionBeginTime);
+ }
+
if (fTimeGraphWrapper instanceof TimeGraphComboWrapper && !fPackDone) {
for (TreeColumn column : ((TimeGraphComboWrapper) fTimeGraphWrapper).getTreeViewer().getTree().getColumns()) {
column.pack();
if (!zoomThread) {
startZoomThread(startTime, endTime);
}
+
}
});
}
});
}
- private void startZoomThread(long startTime, long endTime) {
+ private void sortChildren(ITimeGraphEntry entry, Comparator<ITimeGraphEntry> comparator) {
+ if (entry instanceof TimeGraphEntry) {
+ ((TimeGraphEntry) entry).sortChildren(comparator);
+ }
+ for (ITimeGraphEntry child : entry.getChildren()) {
+ sortChildren(child, comparator);
+ }
+ }
+
+ /**
+ * Start or restart the zoom thread.
+ *
+ * @param startTime
+ * the zoom start time
+ * @param endTime
+ * the zoom end time
+ * @since 2.0
+ */
+ protected final void startZoomThread(long startTime, long endTime) {
+ fDirty.incrementAndGet();
boolean restart = false;
if (fZoomThread != null) {
fZoomThread.cancel();
fZoomThread = createZoomThread(startTime, endTime, resolution, restart);
if (fZoomThread != null) {
fZoomThread.start();
+ } else {
+ fDirty.decrementAndGet();
}
}
* @param restart
* true if restarting zoom for the same time range
* @return a zoom thread
- * @since 2.0
+ * @since 1.1
*/
protected @Nullable ZoomThread createZoomThread(long startTime, long endTime, long resolution, boolean restart) {
final List<TimeGraphEntry> entryList = fEntryList;
private void contributeToActionBars() {
IActionBars bars = getViewSite().getActionBars();
fillLocalToolBar(bars.getToolBarManager());
+ fillLocalMenu(bars.getMenuManager());
}
/**
manager.add(fTimeGraphWrapper.getTimeGraphViewer().getResetScaleAction());
manager.add(fTimeGraphWrapper.getTimeGraphViewer().getPreviousEventAction());
manager.add(fTimeGraphWrapper.getTimeGraphViewer().getNextEventAction());
+ manager.add(new Separator());
+ manager.add(fTimeGraphWrapper.getTimeGraphViewer().getToggleBookmarkAction());
+ manager.add(fTimeGraphWrapper.getTimeGraphViewer().getPreviousMarkerAction());
+ manager.add(fTimeGraphWrapper.getTimeGraphViewer().getNextMarkerAction());
+ manager.add(new Separator());
manager.add(fPreviousResourceAction);
manager.add(fNextResourceAction);
manager.add(fTimeGraphWrapper.getTimeGraphViewer().getZoomInAction());
manager.add(new Separator());
}
+ /**
+ * Add actions to local menu manager
+ *
+ * @param manager the tool bar manager
+ * @since 2.0
+ */
+ protected void fillLocalMenu(IMenuManager manager) {
+ manager.add(fTimeGraphWrapper.getTimeGraphViewer().getMarkersMenu());
+ }
+
/**
* @since 1.0
*/
fTimeGraphWrapper.performAlign(offset, width);
}
}
+
+ /**
+ * 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.
+ *
+ * @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) {
+ return false;
+ }
+ return fDirty.get() != 0 || fZoomThread.getZoomStartTime() != fTimeGraphWrapper.getTimeGraphViewer().getTime0() || fZoomThread.getZoomEndTime() != fTimeGraphWrapper.getTimeGraphViewer().getTime1();
+ }
+
+ 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 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;
+ }
+ }
}