X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=tmf%2Forg.eclipse.tracecompass.tmf.ui%2Fsrc%2Forg%2Feclipse%2Ftracecompass%2Ftmf%2Fui%2Fviews%2Ftimegraph%2FAbstractTimeGraphView.java;h=1a2cc5df578d2640678f41efece55179964221d8;hb=9d3116862eb7cb3206b9941af4ee7d09b535d111;hp=79b67cfa9762fd9843ee32e88f47cba6f0d42fc1;hpb=4e94ae249a1db80edf5d9ba822e9f521894cbb2f;p=deliverable%2Ftracecompass.git diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java index 79b67cfa97..1a2cc5df57 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java @@ -1,5 +1,5 @@ /******************************************************************************* - * 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 @@ -22,11 +22,13 @@ import java.util.ArrayList; 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 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; @@ -40,32 +42,51 @@ 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.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.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.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; @@ -74,8 +95,8 @@ import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal; import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal; import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal; import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; -import org.eclipse.tracecompass.tmf.core.timestamp.TmfNanoTimestamp; import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.core.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; @@ -104,8 +125,16 @@ 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.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 @@ -120,6 +149,10 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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 */ @@ -134,6 +167,10 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA /** The timegraph wrapper */ private ITimeGraphWrapper fTimeGraphWrapper; + private AtomicInteger fDirty = new AtomicInteger(); + + private final Object fZoomThreadResultLock = new Object(); + /** The selected trace */ private ITmfTrace fTrace; @@ -147,13 +184,16 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA private final Map> fEntryListMap = new HashMap<>(); /** The trace to filters hash map */ - private final Map fFiltersMap = new HashMap<>(); + private final Map fFiltersMap = new HashMap<>(); + + /** The trace to view context hash map */ + private final Map fViewContext = new HashMap<>(); /** The trace to marker event sources hash map */ private final Map> fMarkerEventSourcesMap = new HashMap<>(); /** The trace to build thread hash map */ - private final Map fBuildThreadMap = new HashMap<>(); + private final Map fBuildJobMap = new HashMap<>(); /** The start time */ private long fStartTime = SWT.DEFAULT; @@ -188,6 +228,8 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA /** The tree column label array, or null if combo is not used */ private String[] fColumns; + private Comparator[] fColumnComparators; + /** The tree label provider, or null if combo is not used */ private TreeLabelProvider fLabelProvider = null; @@ -203,13 +245,47 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA /** 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 list of color resources created by this view */ - private final List 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 @@ -237,9 +313,9 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA Object getInput(); - void setFilters(ViewerFilter[] filters); + void setFilters(@NonNull ViewerFilter[] filters); - ViewerFilter[] getFilters(); + @NonNull ViewerFilter[] getFilters(); void redraw(); @@ -247,6 +323,10 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA void setAutoExpandLevel(int level); + boolean getExpandedState(ITimeGraphEntry entry); + + void setExpandedState(ITimeGraphEntry entry, boolean expanded); + void setFilterColumns(String[] columnNames); void setFilterContentProvider(ITreeContentProvider contentProvider); @@ -260,6 +340,13 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo(); int getAvailableWidth(int requestedOffset); + + ITimeGraphEntry getSelection(); + + void setSelection(ITimeGraphEntry selection); + + void selectAndReveal(@NonNull ITimeGraphEntry selection); + } private class TimeGraphViewerWrapper implements ITimeGraphWrapper { @@ -330,12 +417,12 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA } @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(); } @@ -364,6 +451,16 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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); @@ -378,6 +475,21 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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 { @@ -448,12 +560,12 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA } @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(); } @@ -482,6 +594,16 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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; } @@ -504,6 +626,21 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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); + } } /** @@ -557,28 +694,25 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA } - 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$ } } @@ -644,6 +778,40 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA public void cancel() { fMonitor.setCanceled(true); } + + @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(); + } + } + } + + /** + * Run the zoom operation. + * @since 2.0 + */ + public abstract void doRun(); } private class ZoomThreadByEntry extends ZoomThread { @@ -655,9 +823,12 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA } @Override - public void run() { + 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) { @@ -666,36 +837,43 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA zoom(entry, getMonitor()); } /* Refresh the arrows when zooming */ + LOGGER.config(() -> getLogMessage("ZoomThreadGettingLinks", null)); //$NON-NLS-1$ List 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 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 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); } } @@ -722,6 +900,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA super(id); fPresentation = pres; fDisplayWidth = Display.getDefault().getBounds().width; + fFindTarget = new FindTarget(); } // ------------------------------------------------------------------------ @@ -760,71 +939,123 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA /** * Sets the tree column labels. + *

* This should be called from the constructor. * * @param columns * The array of tree column labels */ protected void setTreeColumns(final String[] columns) { + setTreeColumns(columns, null, 0); + } + + /** + * Sets the tree column labels. + *

+ * 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[] comparators, int initialSortColumn) { + checkPartNotCreated(); fColumns = columns; + fColumnComparators = comparators; + fInitialSortColumn = initialSortColumn; } /** * Sets the tree label provider. + *

* 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. + *

+ * 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. + *

* 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. + *

* 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. + *

+ * This should be called from the constructor. + * + * @param contentProvider + * The filter content provider + * @since 1.2 + */ + protected void setFilterContentProvider(final ITreeContentProvider contentProvider) { + checkPartNotCreated(); + fFilterContentProvider = contentProvider; + } + /** * Sets the filter label provider. + *

* 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 * @@ -844,7 +1075,10 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA } /** - * Sets the comparator class for the entries + * Sets the comparator class for the entries. + *

+ * This comparator will apply recursively to entries that implement + * {@link TimeGraphEntry#sortChildren(Comparator)}. * * @param comparator * A comparator object @@ -1022,6 +1256,30 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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 null 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 // ------------------------------------------------------------------------ @@ -1038,9 +1296,14 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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(fTimeGraphContentProvider); + fTimeGraphWrapper.setFilterContentProvider(fFilterContentProvider != null ? fFilterContentProvider : fTimeGraphContentProvider); fTimeGraphWrapper.setFilterLabelProvider(fFilterLabelProvider); fTimeGraphWrapper.setFilterColumns(fFilterColumns); @@ -1052,7 +1315,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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); } @@ -1061,8 +1324,8 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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)); } }); @@ -1082,14 +1345,14 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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) { @@ -1106,7 +1369,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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; } @@ -1135,6 +1398,10 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA getSite().setSelectionProvider(fTimeGraphWrapper.getSelectionProvider()); ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); + + createContextMenu(); + fPartListener = new TimeGraphPartListener(); + getSite().getPage().addPartListener(fPartListener); } @Override @@ -1145,7 +1412,16 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA @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); } /** @@ -1162,12 +1438,8 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA } } - private List refreshBookmarks(final IFile editorFile) { + private static List refreshBookmarks(final IFile editorFile) { List bookmarks = new ArrayList<>(); - for (Color color : fColors) { - color.dispose(); - } - fColors.clear(); if (editorFile == null || !editorFile.exists()) { return bookmarks; } @@ -1186,8 +1458,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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()); @@ -1201,6 +1472,8 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA return bookmarks; } + + // ------------------------------------------------------------------------ // Signal handlers // ------------------------------------------------------------------------ @@ -1238,28 +1511,12 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA */ @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(); } } @@ -1276,8 +1533,8 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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 @@ -1290,7 +1547,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA } else { fTimeGraphWrapper.getTimeGraphViewer().setSelectionRange(beginTime, endTime, true); } - synchingToTime(fTimeGraphWrapper.getTimeGraphViewer().getTime0()); + synchingToTime(fTimeGraphWrapper.getTimeGraphViewer().getSelectionBegin()); } }); } @@ -1310,8 +1567,8 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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() { @@ -1332,6 +1589,20 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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 // ------------------------------------------------------------------------ @@ -1344,16 +1615,21 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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; + + LOGGER.info(() -> getLogMessage("LoadingTrace", "trace=" + trace.getName())); //$NON-NLS-1$ //$NON-NLS-2$ + + 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(); } } @@ -1370,16 +1646,25 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA if (viewTrace == null) { return; } + resetView(viewTrace); + List 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); @@ -1405,17 +1690,40 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA * 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 getTracesToBuild(@NonNull ITmfTrace trace) { + protected @NonNull Iterable 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. + *

+ * Called from the BuildJob for each trace returned by + * {@link #getTracesToBuild(ITmfTrace)}. + *

+ * 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. + *

+ * 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. + *

+ * 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 + *

+     * if (parentTrace.equals(getTrace())) {
+     *     refresh();
+     * }
+     * 
* * @param trace * The trace being built @@ -1423,11 +1731,16 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA * 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. + *

+ * 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 @@ -1517,7 +1830,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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; @@ -1527,10 +1840,10 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA * Get the list of current marker categories. * * @return The list of marker categories - * @since 2.0 + * @since 2.1 */ - private @NonNull List getMarkerCategories() { - Set categories = new HashSet<>(getViewMarkerCategories()); + protected @NonNull List getMarkerCategories() { + Set categories = new LinkedHashSet<>(getViewMarkerCategories()); for (IMarkerEventSource markerEventSource : getMarkerEventSources(fTrace)) { categories.addAll(markerEventSource.getMarkerCategories()); } @@ -1548,7 +1861,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA private @NonNull List getMarkerEventSources(ITmfTrace trace) { List markerEventSources = fMarkerEventSourcesMap.get(trace); if (markerEventSources == null) { - markerEventSources = checkNotNull(Collections.emptyList()); + markerEventSources = Collections.emptyList(); } return markerEventSources; } @@ -1557,13 +1870,17 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA * 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; } + fDirty.incrementAndGet(); + boolean hasEntries = false; synchronized (fEntryListMap) { fEntryList = fEntryListMap.get(fTrace); @@ -1572,36 +1889,64 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA } else if (fEntryComparator != null) { List 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); - fTimeGraphWrapper.getTimeGraphViewer().setBookmarks(refreshBookmarks(fEditorFile)); - fTimeGraphWrapper.getTimeGraphViewer().setMarkerCategories(getMarkerCategories()); - fTimeGraphWrapper.getTimeGraphViewer().setMarkers(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); 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); 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(); @@ -1614,6 +1959,8 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA if (!zoomThread) { startZoomThread(startTime, endTime); } + fDirty.decrementAndGet(); + LOGGER.info(() -> getLogMessage("RefreshEnd", null)); //$NON-NLS-1$ } }); } @@ -1630,9 +1977,11 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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; } @@ -1646,22 +1995,51 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA fRedrawState = State.IDLE; } } + LOGGER.info(() -> getLogMessage("RedrawEnd", null)); //$NON-NLS-1$ } }); } - private void startZoomThread(long startTime, long endTime) { + private void sortChildren(ITimeGraphEntry entry, Comparator 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) { + 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) { 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(); } } @@ -1769,4 +2147,319 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA 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. + * + * 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 (fTrace == null) { + return false; + } + + 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 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 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 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 (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) { + } + } }