tmf: Bug 494077: Closing AbstractTimeGraphView should cancel its threads
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / views / timegraph / AbstractTimeGraphView.java
index 5d4a10b507052ba7d0501b4333e70c3d1e7a4301..abb3bca922fb526e064a86ddb6a33354d274b39c 100644 (file)
@@ -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
 
 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.GroupMarker;
 import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.IStatusLineManager;
 import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.commands.ActionHandler;
 import org.eclipse.jface.viewers.AbstractTreeViewer;
 import org.eclipse.jface.viewers.ILabelProvider;
 import org.eclipse.jface.viewers.ILabelProviderListener;
 import org.eclipse.jface.viewers.ISelectionProvider;
 import org.eclipse.jface.viewers.ITableLabelProvider;
 import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeSelection;
 import org.eclipse.jface.viewers.TreeViewer;
 import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MenuDetectEvent;
+import org.eclipse.swt.events.MenuDetectListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGBA;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Tree;
 import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.tracecompass.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;
@@ -54,20 +88,23 @@ 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;
 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;
@@ -76,11 +113,21 @@ import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTimeEvent;
 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.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
@@ -88,11 +135,13 @@ 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
      */
@@ -107,9 +156,16 @@ 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;
 
+    /** The selected trace editor file*/
+    private IFile fEditorFile;
+
     /** The timegraph entry list */
     private List<TimeGraphEntry> fEntryList;
 
@@ -117,7 +173,13 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
     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<>();
@@ -155,6 +217,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<ITimeGraphEntry>[] fColumnComparators;
+
     /** The tree label provider, or null if combo is not used */
     private TreeLabelProvider fLabelProvider = null;
 
@@ -170,11 +234,48 @@ 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 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
     // ------------------------------------------------------------------------
@@ -201,9 +302,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();
 
@@ -211,6 +312,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);
@@ -224,6 +329,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 {
@@ -294,12 +406,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();
         }
 
@@ -328,6 +440,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);
@@ -342,6 +464,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 {
@@ -412,12 +549,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();
         }
 
@@ -446,6 +583,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;
         }
@@ -468,6 +615,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);
+        }
     }
 
     /**
@@ -535,7 +697,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
 
         @Override
         public void run() {
-            buildEventList(fBuildTrace, fParentTrace, fMonitor);
+            buildEntryList(fBuildTrace, fParentTrace, fMonitor);
             synchronized (fBuildThreadMap) {
                 fBuildThreadMap.remove(fBuildTrace);
             }
@@ -548,7 +710,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
 
     /**
      * Zoom thread
-     * @since 2.0
+     * @since 1.1
      */
     protected abstract class ZoomThread extends Thread {
         private final long fZoomStartTime;
@@ -608,6 +770,36 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         public void cancel() {
             fMonitor.setCanceled(true);
         }
+
+        @Override
+        public final void run() {
+            doRun();
+            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
+         */
+        public abstract void doRun();
     }
 
     private class ZoomThreadByEntry extends ZoomThread {
@@ -619,7 +811,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         }
 
         @Override
-        public void run() {
+        public void doRun() {
             for (TimeGraphEntry entry : fZoomEntryList) {
                 if (getMonitor().isCanceled()) {
                     return;
@@ -631,35 +823,38 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
             }
             /* Refresh the arrows when zooming */
             List<ILinkEvent> events = getLinkList(getZoomStartTime(), getZoomEndTime(), getResolution(), getMonitor());
-            if (events != null) {
-                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);
+            /* 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()));
+            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);
             }
         }
 
@@ -686,6 +881,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         super(id);
         fPresentation = pres;
         fDisplayWidth = Display.getDefault().getBounds().width;
+        fFindTarget = new FindTarget();
     }
 
     // ------------------------------------------------------------------------
@@ -724,71 +920,123 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
 
     /**
      * 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 1.2
+     */
+    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
      *
@@ -808,7 +1056,10 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
     }
 
     /**
-     * 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
@@ -986,6 +1237,11 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         return Messages.AbstractTimeGraphView_PreviousTooltip;
     }
 
+
+    FindTarget getFindTarget() {
+        return fFindTarget;
+    }
+
     // ------------------------------------------------------------------------
     // ViewPart
     // ------------------------------------------------------------------------
@@ -1002,9 +1258,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);
 
@@ -1016,7 +1277,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);
             }
@@ -1025,12 +1286,62 @@ 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));
             }
         });
 
+        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,
+                                                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,
+                                                TmfTimestamp.fromNanos(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();
@@ -1047,6 +1358,12 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
 
         // make selection available to other views
         getSite().setSelectionProvider(fTimeGraphWrapper.getSelectionProvider());
+
+        ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
+
+        createContextMenu();
+        fPartListener = new TimeGraphPartListener();
+        getSite().getPage().addPartListener(fPartListener);
     }
 
     @Override
@@ -1054,6 +1371,71 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         fTimeGraphWrapper.setFocus();
     }
 
+    @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);
+    }
+
+    /**
+     * @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
     // ------------------------------------------------------------------------
@@ -1091,26 +1473,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();
-                }
-            }
-        }
-        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;
-            }
             refresh();
         }
     }
@@ -1127,8 +1495,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
@@ -1139,9 +1507,9 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
                 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());
             }
         });
     }
@@ -1161,8 +1529,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() {
@@ -1195,15 +1563,18 @@ 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;
+        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();
+                fStartTime = fTrace.getStartTime().toNanos();
+                fEndTime = fTrace.getEndTime().toNanos();
                 refresh();
             }
         }
@@ -1220,16 +1591,21 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         if (viewTrace == null) {
             return;
         }
+        resetView(viewTrace);
+
+        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);
     }
 
     /**
@@ -1260,9 +1636,11 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
     }
 
     /**
-     * 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
@@ -1270,11 +1648,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.
+     * <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
@@ -1288,7 +1671,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
      *            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);
 
@@ -1306,14 +1689,25 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
      *            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
@@ -1326,11 +1720,69 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
      * @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(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 = Collections.emptyList();
+        }
+        return markerEventSources;
+    }
+
     /**
      * Refresh the display
      */
@@ -1342,6 +1794,8 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
                 if (fTimeGraphWrapper.isDisposed()) {
                     return;
                 }
+                fDirty.incrementAndGet();
+
                 boolean hasEntries = false;
                 synchronized (fEntryListMap) {
                     fEntryList = fEntryListMap.get(fTrace);
@@ -1350,33 +1804,64 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
                     } 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);
                 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);
+                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();
@@ -1389,6 +1874,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
                 if (!zoomThread) {
                     startZoomThread(startTime, endTime);
                 }
+                fDirty.decrementAndGet();
             }
         });
     }
@@ -1425,18 +1911,46 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         });
     }
 
-    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) {
+        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();
         }
     }
 
@@ -1452,7 +1966,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
      * @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;
@@ -1474,6 +1988,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
     private void contributeToActionBars() {
         IActionBars bars = getViewSite().getActionBars();
         fillLocalToolBar(bars.getToolBarManager());
+        fillLocalMenu(bars.getMenuManager());
     }
 
     /**
@@ -1490,9 +2005,11 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         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().getPreviousBookmarkAction());
-        manager.add(fTimeGraphWrapper.getTimeGraphViewer().getNextBookmarkAction());
+        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());
@@ -1500,6 +2017,16 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         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
      */
@@ -1531,4 +2058,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<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) {
+        }
+    }
 }
This page took 0.043523 seconds and 5 git commands to generate.