tmf: Bug: 499359: Fix deadlock in table when closing trace selection
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / viewers / events / TmfEventsTable.java
index 23aabf2ef88ec54e01f9fd234617b2522c9051e3..9e9cd729002f52593a7ab008fd6f0051d553c626 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010, 2015 Ericsson
+ * Copyright (c) 2010, 2016 Ericsson and others.
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
@@ -18,8 +18,7 @@
 
 package org.eclipse.tracecompass.tmf.ui.viewers.events;
 
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
@@ -39,10 +38,13 @@ import org.eclipse.core.commands.NotHandledException;
 import org.eclipse.core.commands.ParameterizedCommand;
 import org.eclipse.core.commands.common.NotDefinedException;
 import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.IWorkspaceRunnable;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
@@ -51,10 +53,12 @@ import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.ListenerList;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EValidator;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.IMenuListener;
@@ -62,13 +66,15 @@ import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.IStatusLineManager;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.Separator;
-import org.eclipse.jface.dialogs.InputDialog;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.resource.ColorRegistry;
 import org.eclipse.jface.resource.FontRegistry;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
 import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.jface.util.OpenStrategy;
 import org.eclipse.jface.util.PropertyChangeEvent;
@@ -81,6 +87,7 @@ import org.eclipse.jface.viewers.LabelProvider;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.window.Window;
+import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.SashForm;
 import org.eclipse.swt.custom.StyleRange;
@@ -112,6 +119,7 @@ import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.MessageBox;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
 import org.eclipse.swt.widgets.TableColumn;
 import org.eclipse.swt.widgets.TableItem;
 import org.eclipse.swt.widgets.Text;
@@ -121,7 +129,7 @@ import org.eclipse.tracecompass.internal.tmf.ui.Activator;
 import org.eclipse.tracecompass.internal.tmf.ui.Messages;
 import org.eclipse.tracecompass.internal.tmf.ui.commands.CopyToClipboardOperation;
 import org.eclipse.tracecompass.internal.tmf.ui.commands.ExportToTextCommandHandler;
-import org.eclipse.tracecompass.internal.tmf.ui.dialogs.MultiLineInputDialog;
+import org.eclipse.tracecompass.internal.tmf.ui.dialogs.AddBookmarkDialog;
 import org.eclipse.tracecompass.tmf.core.component.ITmfEventProvider;
 import org.eclipse.tracecompass.tmf.core.component.TmfComponent;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
@@ -133,11 +141,13 @@ import org.eclipse.tracecompass.tmf.core.event.lookup.ITmfModelLookup;
 import org.eclipse.tracecompass.tmf.core.event.lookup.ITmfSourceLookup;
 import org.eclipse.tracecompass.tmf.core.filter.ITmfFilter;
 import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode;
-import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterAndNode;
 import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterMatchesNode;
 import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode;
+import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterObjectNode;
+import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterRootNode;
 import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType;
 import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
+import org.eclipse.tracecompass.tmf.core.resources.ITmfMarker;
 import org.eclipse.tracecompass.tmf.core.signal.TmfEventFilterAppliedSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfEventSearchAppliedSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfEventSelectedSignal;
@@ -152,7 +162,9 @@ import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
 import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
 import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
+import org.eclipse.tracecompass.tmf.core.util.Pair;
 import org.eclipse.tracecompass.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
+import org.eclipse.tracecompass.tmf.ui.viewers.events.TmfEventsTableHeader.IEventsTableHeaderListener;
 import org.eclipse.tracecompass.tmf.ui.viewers.events.columns.TmfEventTableColumn;
 import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSetting;
 import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSettingsManager;
@@ -160,13 +172,17 @@ import org.eclipse.tracecompass.tmf.ui.views.colors.IColorSettingsListener;
 import org.eclipse.tracecompass.tmf.ui.views.filter.FilterManager;
 import org.eclipse.tracecompass.tmf.ui.widgets.rawviewer.TmfRawEventViewer;
 import org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.TmfVirtualTable;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
 import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartSite;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.commands.ICommandService;
 import org.eclipse.ui.dialogs.ListDialog;
 import org.eclipse.ui.handlers.IHandlerService;
 import org.eclipse.ui.ide.IDE;
 import org.eclipse.ui.ide.IGotoMarker;
+import org.eclipse.ui.texteditor.ITextEditor;
 import org.eclipse.ui.themes.ColorUtil;
 import org.eclipse.ui.themes.IThemeManager;
 
@@ -188,7 +204,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     /**
      * Empty string array, used by {@link #getItemStrings}.
      */
-    protected static final @NonNull String[] EMPTY_STRING_ARRAY = new String[0];
+    protected static final String @NonNull [] EMPTY_STRING_ARRAY = new String[0];
 
     /**
      * Empty string
@@ -208,21 +224,398 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private static final Image SEARCH_MATCH_BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(
             "icons/elcl16/search_match_bookmark.gif"); //$NON-NLS-1$
     private static final Image FILTER_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/filter_items.gif"); //$NON-NLS-1$
+    private static final Image FILTER_ADD_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/filter_add.gif"); //$NON-NLS-1$
     private static final Image STOP_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/stop.gif"); //$NON-NLS-1$
     private static final String SEARCH_HINT = Messages.TmfEventsTable_SearchHint;
-    private static final String FILTER_HINT = Messages.TmfEventsTable_FilterHint;
     private static final int MAX_CACHE_SIZE = 1000;
 
     private static final int MARGIN_COLUMN_INDEX = 0;
     private static final int FILTER_SUMMARY_INDEX = 1;
     private static final int EVENT_COLUMNS_START_INDEX = MARGIN_COLUMN_INDEX + 1;
 
+    private final ISchedulingRule fTimeSelectMutexRule = new ISchedulingRule() {
+        @Override
+        public boolean isConflicting(ISchedulingRule rule) {
+            return (rule == this);
+        }
+
+        @Override
+        public boolean contains(ISchedulingRule rule) {
+            return (rule == this);
+        }
+    };
+
+    private Job fTimeSelectJob = null;
+
+    private final class ColumnListener extends ControlAdapter {
+        /*
+         * Make sure that the margin column is always first and keep the column
+         * order variable up to date.
+         */
+        @Override
+        public void controlMoved(ControlEvent e) {
+            int[] order = fTable.getColumnOrder();
+            if (order[0] != MARGIN_COLUMN_INDEX) {
+                for (int i = order.length - 1; i > 0; i--) {
+                    if (order[i] == MARGIN_COLUMN_INDEX) {
+                        order[i] = order[i - 1];
+                        order[i - 1] = MARGIN_COLUMN_INDEX;
+                    }
+                }
+                fTable.setColumnOrder(order);
+            }
+            fColumnOrder = order;
+            fTable.layout();
+        }
+
+        @Override
+        public void controlResized(ControlEvent e) {
+            TableColumn column = (TableColumn) e.widget;
+            if (column.getResizable() && !isExpanded(column)) {
+                int i = (int) column.getData(Key.INDEX);
+                fColumnSize[i] = column.getWidth();
+                column.setData(Key.WIDTH, fColumnSize[i]);
+            }
+        }
+    }
+
+    private final class TableSelectionListener extends SelectionAdapter {
+        @Override
+        public void widgetSelected(final SelectionEvent e) {
+            if (e.item == null) {
+                return;
+            }
+            updateStatusLine(null);
+            if (fTable.getSelectionIndices().length > 0) {
+                if (e.item.getData(Key.RANK) instanceof Long) {
+                    fSelectedRank = (Long) e.item.getData(Key.RANK);
+                    fRawViewer.selectAndReveal((Long) e.item.getData(Key.RANK));
+                } else {
+                    fSelectedRank = -1;
+                }
+                if (fTable.getSelectionIndices().length == 1) {
+                    fSelectedBeginRank = fSelectedRank;
+                }
+                if (e.item.getData(Key.TIMESTAMP) instanceof ITmfTimestamp) {
+                    final ITmfTimestamp ts = NonNullUtils.checkNotNull((ITmfTimestamp) e.item.getData(Key.TIMESTAMP));
+                    if (fTable.getSelectionIndices().length == 1) {
+                        fSelectedBeginTimestamp = ts;
+                    }
+                    ITmfTimestamp selectedBeginTimestamp = fSelectedBeginTimestamp;
+                    if (selectedBeginTimestamp != null) {
+                        broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, selectedBeginTimestamp, ts));
+                        if (fTable.getSelectionIndices().length == 2) {
+                            updateStatusLine(ts.getDelta(selectedBeginTimestamp));
+                        }
+                    }
+                } else {
+                    if (fTable.getSelectionIndices().length == 1) {
+                        fSelectedBeginTimestamp = null;
+                    }
+                }
+            }
+            if (e.item.getData() instanceof ITmfEvent) {
+                broadcast(new TmfEventSelectedSignal(TmfEventsTable.this, (ITmfEvent) e.item.getData()));
+                fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(e.item.getData())));
+            } else {
+                fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
+            }
+        }
+    }
+
+    private final class MouseDoubleClickListener extends MouseAdapter {
+        @Override
+        public void mouseDoubleClick(final MouseEvent event) {
+            if (event.button != 1) {
+                return;
+            }
+            // Identify the selected row
+            final Point point = new Point(event.x, event.y);
+            final TableItem item = fTable.getItem(point);
+            if (item != null) {
+                final Rectangle imageBounds = item.getImageBounds(0);
+                imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
+                if (imageBounds.contains(point)) {
+                    final Long rank = (Long) item.getData(Key.RANK);
+                    if (rank != null) {
+                        toggleBookmark(rank);
+                    }
+                }
+            }
+        }
+    }
+
+    private final class RawSelectionListener implements Listener {
+        @Override
+        public void handleEvent(final Event e) {
+            if (fTrace == null) {
+                return;
+            }
+            long rank;
+            if (e.data instanceof Long) {
+                rank = (Long) e.data;
+            } else if (e.data instanceof ITmfLocation) {
+                rank = findRank((ITmfLocation) e.data);
+            } else {
+                return;
+            }
+            int index = (int) rank;
+            if (fTable.getData(Key.FILTER_OBJ) != null) {
+                // +1 for top filter status row
+                index = fCache.getFilteredEventIndex(rank) + 1;
+            }
+            // +1 for header row
+            fTable.setSelection(index + 1);
+            fSelectedRank = rank;
+            fSelectedBeginRank = fSelectedRank;
+            updateStatusLine(null);
+            final TableItem[] selection = fTable.getSelection();
+            if ((selection != null) && (selection.length > 0)) {
+                TableItem item = fTable.getSelection()[0];
+                final TmfTimestamp ts = (TmfTimestamp) item.getData(Key.TIMESTAMP);
+                if (ts != null) {
+                    broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, ts));
+                }
+                if (item.getData() instanceof ITmfEvent) {
+                    broadcast(new TmfEventSelectedSignal(TmfEventsTable.this, (ITmfEvent) item.getData()));
+                    fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(item.getData())));
+                } else {
+                    fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
+                }
+            }
+        }
+
+        private long findRank(final ITmfLocation selectedLocation) {
+            final double selectedRatio = fTrace.getLocationRatio(selectedLocation);
+            long low = 0;
+            long high = fTrace.getNbEvents();
+            long rank = high / 2;
+            double ratio = -1;
+            while (ratio != selectedRatio) {
+                ITmfContext context = fTrace.seekEvent(rank);
+                ratio = fTrace.getLocationRatio(context.getLocation());
+                context.dispose();
+                if (ratio < selectedRatio) {
+                    low = rank;
+                    rank = (rank + high) / 2;
+                } else if (ratio > selectedRatio) {
+                    high = rank;
+                    rank = (rank + low) / 2;
+                }
+                if ((high - low) < 2) {
+                    break;
+                }
+            }
+            return rank;
+        }
+    }
+
+    private final class SetDataListener implements Listener {
+        @Override
+        public void handleEvent(final Event event) {
+
+            final TableItem item = (TableItem) event.item;
+            int index = event.index - 1; // -1 for the header row
+
+            if (event.index == 0) {
+                setHeaderRowItemData(item);
+                return;
+            }
+
+            if (fTable.getData(Key.FILTER_OBJ) != null) {
+                if ((event.index == 1) || (event.index == (fTable.getItemCount() - 1))) {
+                    setFilterStatusRowItemData(item);
+                    return;
+                }
+                /* -1 for top filter status row */
+                index = index - 1;
+            }
+
+            final CachedEvent cachedEvent = fCache.getEvent(index);
+            if (cachedEvent != null) {
+                setItemData(item, cachedEvent, cachedEvent.rank);
+                return;
+            }
+
+            // Else, fill the cache asynchronously (and off the UI thread)
+            event.doit = false;
+        }
+    }
+
+    private static final class PainItemListener implements Listener {
+        @Override
+        public void handleEvent(Event event) {
+            TableItem item = (TableItem) event.item;
+
+            // we promised to paint the table item's foreground
+            GC gc = event.gc;
+            Image image = item.getImage(event.index);
+            if (image != null) {
+                Rectangle imageBounds = item.getImageBounds(event.index);
+                /*
+                 * The image bounds don't match the default image position.
+                 */
+                if (IS_LINUX) {
+                    gc.drawImage(image, imageBounds.x + 1, imageBounds.y + 3);
+                } else {
+                    gc.drawImage(image, imageBounds.x, imageBounds.y + 1);
+                }
+            }
+            gc.setForeground(item.getForeground(event.index));
+            gc.setFont(item.getFont(event.index));
+            String text = item.getText(event.index);
+            Rectangle textBounds = item.getTextBounds(event.index);
+            /*
+             * The text bounds don't match the default text position.
+             */
+            if (IS_LINUX) {
+                gc.drawText(text, textBounds.x + 1, textBounds.y + 3, true);
+            } else {
+                gc.drawText(text, textBounds.x - 1, textBounds.y + 2, true);
+            }
+        }
+    }
+
+    private static final class EraseItemListener implements Listener {
+        @Override
+        public void handleEvent(Event event) {
+            TableItem item = (TableItem) event.item;
+            List<?> styleRanges = (List<?>) item.getData(Key.STYLE_RANGES);
+
+            GC gc = event.gc;
+            Color background = item.getBackground(event.index);
+            /*
+             * Paint the background if it is not the default system color. In
+             * Windows, if you let the widget draw the background, it will not
+             * show the item's background color if the item is selected or hot.
+             * If there are no style ranges and the item background is the
+             * default system color, we do not want to paint it or otherwise we
+             * would override the platform theme (e.g. alternating colors).
+             */
+            if (styleRanges != null || !background.equals(item.getParent().getBackground())) {
+                // we will paint the table item's background
+                event.detail &= ~SWT.BACKGROUND;
+
+                // paint the item's default background
+                gc.setBackground(background);
+                gc.fillRectangle(event.x, event.y, event.width, event.height);
+            }
+
+            /*
+             * We will paint the table item's foreground. In Windows, if you
+             * paint the background but let the widget draw the foreground, it
+             * will override your background, unless the item is selected or
+             * hot.
+             */
+            event.detail &= ~SWT.FOREGROUND;
+
+            // paint the highlighted background for all style ranges
+            if (styleRanges != null) {
+                Rectangle textBounds = item.getTextBounds(event.index);
+                String text = item.getText(event.index);
+                for (Object o : styleRanges) {
+                    if (o instanceof StyleRange) {
+                        StyleRange styleRange = (StyleRange) o;
+                        if (styleRange.data.equals(event.index)) {
+                            int startIndex = styleRange.start;
+                            int endIndex = startIndex + styleRange.length;
+                            int startX = gc.textExtent(text.substring(0, startIndex)).x;
+                            int endX = gc.textExtent(text.substring(0, endIndex)).x;
+                            gc.setBackground(styleRange.background);
+                            gc.fillRectangle(textBounds.x + startX, textBounds.y, (endX - startX), textBounds.height);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private final class TooltipListener implements Listener {
+        Shell tooltipShell = null;
+
+        @Override
+        public void handleEvent(final Event event) {
+            switch (event.type) {
+            case SWT.MouseHover:
+                final TableItem item = fTable.getItem(new Point(event.x, event.y));
+                if (item == null) {
+                    return;
+                }
+                String text;
+                if (fTable.indexOf(item) == 0) {
+                    if (fHeaderState == HeaderState.SEARCH && item.getBounds(0).contains(event.x, event.y)) {
+                        text = Messages.TmfEventsTable_AddAsFilterText;
+                    } else {
+                        return;
+                    }
+                } else {
+                    final Long rank = (Long) item.getData(Key.RANK);
+                    if (rank == null) {
+                        return;
+                    }
+                    final String tooltipText = (String) item.getData(Key.BOOKMARK);
+                    final Rectangle bounds = item.getImageBounds(0);
+                    bounds.width = BOOKMARK_IMAGE.getBounds().width;
+                    if (!bounds.contains(event.x, event.y)) {
+                        return;
+                    }
+                    text = rank.toString() + (tooltipText != null ? ": " + tooltipText : EMPTY_STRING); //$NON-NLS-1$
+                }
+                if ((tooltipShell != null) && !tooltipShell.isDisposed()) {
+                    tooltipShell.dispose();
+                }
+                tooltipShell = new Shell(fTable.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
+                tooltipShell.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+                final FillLayout layout = new FillLayout();
+                layout.marginWidth = 2;
+                tooltipShell.setLayout(layout);
+                final Label label = new Label(tooltipShell, SWT.WRAP);
+                label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+                label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+                label.setText(text);
+                label.addListener(SWT.MouseExit, this);
+                label.addListener(SWT.MouseDown, this);
+                label.addListener(SWT.MouseWheel, this);
+                final Point size = tooltipShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+                /*
+                 * Bug in Linux. The coordinates of the event have an origin
+                 * that excludes the table header but the method toDisplay()
+                 * expects coordinates relative to an origin that includes the
+                 * table header.
+                 */
+                int y = event.y;
+                if (IS_LINUX) {
+                    y += fTable.getHeaderHeight();
+                }
+                Point pt = fTable.toDisplay(event.x, y);
+                pt.x += BOOKMARK_IMAGE.getBounds().width;
+                pt.y += item.getBounds().height;
+                tooltipShell.setBounds(pt.x, pt.y, size.x, size.y);
+                tooltipShell.setVisible(true);
+                break;
+            case SWT.Dispose:
+            case SWT.KeyDown:
+            case SWT.MouseMove:
+            case SWT.MouseExit:
+            case SWT.MouseDown:
+            case SWT.MouseWheel:
+                if (tooltipShell != null) {
+                    tooltipShell.dispose();
+                    tooltipShell = null;
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
     /**
      * The events table search/filter/data keys
      *
      * @author Patrick Tasse
-     * @noimplement This interface only contains Event Table specific
-     *              static definitions.
+     * @noimplement This interface only contains Event Table specific static
+     *              definitions.
      */
     public interface Key {
 
@@ -260,9 +653,16 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         /**
          * The width of a table item
          *
-         * @since 2.0
+         * @since 1.1
          */
         String WIDTH = "$width"; //$NON-NLS-1$
+
+        /**
+         * The position of the column
+         *
+         * @since 2.1
+         */
+        String INDEX = "$index"; //$NON-NLS-1$
     }
 
     /**
@@ -272,11 +672,15 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      * @author Patrick Tasse
      */
     public static enum HeaderState {
-        /** A search is being run */
-        SEARCH,
+        /**
+         * No search filter is applied
+         *
+         * @since 2.0
+         */
+        NO_SEARCH,
 
-        /** A filter is applied */
-        FILTER
+        /** A search filter is applied */
+        SEARCH
     }
 
     interface Direction {
@@ -288,15 +692,20 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     // Table data
     // ------------------------------------------------------------------------
 
+    /** The header bar */
+    private TmfEventsTableHeader fHeaderBar;
+
     /** The virtual event table */
     protected TmfVirtualTable fTable;
 
     private Composite fComposite;
     private SashForm fSashForm;
+    private Composite fTableComposite;
     private TmfRawEventViewer fRawViewer;
     private ITmfTrace fTrace;
     private volatile boolean fPackDone = false;
-    private HeaderState fHeaderState = HeaderState.SEARCH;
+    private volatile boolean fPackMarginDone = false;
+    private HeaderState fHeaderState = HeaderState.NO_SEARCH;
     private long fSelectedRank = -1;
     private long fSelectedBeginRank = -1;
     private ITmfTimestamp fSelectedBeginTimestamp = null;
@@ -310,6 +719,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private final Object fFilterSyncObj = new Object();
     private SearchThread fSearchThread;
     private final Object fSearchSyncObj = new Object();
+    private boolean fCollapseFilterEnabled = false;
 
     /**
      * List of selection change listeners (element type:
@@ -357,6 +767,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private MenuManager fTablePopupMenuManager;
     private MenuManager fHeaderPopupMenuManager;
 
+    private boolean[] fColumnResizable;
+
+    private int[] fColumnSize;
+
     // ------------------------------------------------------------------------
     // Constructors
     // ------------------------------------------------------------------------
@@ -397,17 +811,17 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     }
 
     @Deprecated
-    private static @NonNull Iterable<ITmfEventAspect> convertFromColumnData(
+    private static @NonNull Iterable<ITmfEventAspect<?>> convertFromColumnData(
             org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.ColumnData[] columnData) {
 
-        ImmutableList.Builder<ITmfEventAspect> builder = new ImmutableList.Builder<>();
+        ImmutableList.Builder<ITmfEventAspect<?>> builder = new ImmutableList.Builder<>();
         for (org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.ColumnData col : columnData) {
             String fieldName = col.header;
             if (fieldName != null) {
                 builder.add(new TmfContentFieldAspect(fieldName, fieldName));
             }
         }
-        return checkNotNull(builder.build());
+        return builder.build();
     }
 
     /**
@@ -426,11 +840,11 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      *            </p>
      */
     public TmfEventsTable(final Composite parent, int cacheSize,
-            @NonNull Iterable<ITmfEventAspect> aspects) {
+            @NonNull Iterable<ITmfEventAspect<?>> aspects) {
         super("TmfEventsTable"); //$NON-NLS-1$
 
         fComposite = new Composite(parent, SWT.NONE);
-        final GridLayout gl = new GridLayout(1, false);
+        GridLayout gl = new GridLayout(1, false);
         gl.marginHeight = 0;
         gl.marginWidth = 0;
         gl.verticalSpacing = 0;
@@ -439,9 +853,45 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fSashForm = new SashForm(fComposite, SWT.HORIZONTAL);
         fSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
 
+        // Create a composite for the table and its header bar
+        fTableComposite = new Composite(fSashForm, SWT.NONE);
+        fTableComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+        gl = new GridLayout(1, false);
+        gl.marginHeight = 0;
+        gl.marginWidth = 0;
+        gl.verticalSpacing = 0;
+        fTableComposite.setLayout(gl);
+
+        // Create an events table header bar
+        fHeaderBar = new TmfEventsTableHeader(fTableComposite, SWT.NONE, new IEventsTableHeaderListener() {
+            @Override
+            public void filterSelected(ITmfFilter filter) {
+                if (filter instanceof TmfFilterMatchesNode) {
+                    TmfFilterMatchesNode matchFilter = (TmfFilterMatchesNode) filter;
+                    for (TableColumn col : fTable.getColumns()) {
+                        if (col.getData(Key.ASPECT) == matchFilter.getEventAspect()) {
+                            col.setData(Key.FILTER_TXT, matchFilter.getRegex());
+                        } else {
+                            col.setData(Key.FILTER_TXT, null);
+                        }
+                    }
+                    fTable.refresh();
+                    fTable.redraw();
+                }
+            }
+
+            @Override
+            public void filterRemoved(ITmfFilter filter) {
+                for (TableColumn col : fTable.getColumns()) {
+                    col.setData(Key.FILTER_TXT, null);
+                }
+                removeFilter(filter);
+            }
+        });
+
         // Create a virtual table
         final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION;
-        fTable = new TmfVirtualTable(fSashForm, style);
+        fTable = new TmfVirtualTable(fTableComposite, style);
 
         // Set the table layout
         final GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
@@ -452,9 +902,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fTable.setLinesVisible(true);
 
         // Setup the columns
-        for (ITmfEventAspect aspect : aspects) {
+        for (ITmfEventAspect<?> aspect : aspects) {
             if (aspect != null) {
                 fColumns.add(new TmfEventTableColumn(aspect));
+
             }
         }
 
@@ -462,41 +913,28 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fColumns.add(MARGIN_COLUMN_INDEX, collapseCol);
 
         fHeaderMenu = new Menu(fTable);
+
+        fColumnSize = new int[fColumns.size()];
+        fColumnResizable = new boolean[fColumns.size()];
+        int i = 0;
         // Create the UI columns in the table
         for (TmfEventTableColumn col : fColumns) {
             TableColumn column = fTable.newTableColumn(SWT.LEFT);
             column.setText(col.getHeaderName());
             column.setToolTipText(col.getHeaderTooltip());
             column.setData(Key.ASPECT, col.getEventAspect());
-            column.pack();
+            column.setData(Key.INDEX, i);
             if (col instanceof TmfMarginColumn) {
                 column.setResizable(false);
             } else {
+                column.pack();
                 column.setMoveable(true);
-                column.setData(Key.WIDTH, -1);
+                column.setData(Key.WIDTH, column.getWidth());
+                fColumnSize[i] = column.getWidth();
             }
-            column.addControlListener(new ControlAdapter() {
-                /*
-                 * Make sure that the margin column is always first and keep the
-                 * column order variable up to date.
-                 */
-                @Override
-                public void controlMoved(ControlEvent e) {
-                    int[] order = fTable.getColumnOrder();
-                    if (order[0] == MARGIN_COLUMN_INDEX) {
-                        fColumnOrder = order;
-                        return;
-                    }
-                    for (int i = order.length - 1; i > 0; i--) {
-                        if (order[i] == MARGIN_COLUMN_INDEX) {
-                            order[i] = order[i - 1];
-                            order[i - 1] = MARGIN_COLUMN_INDEX;
-                        }
-                    }
-                    fTable.setColumnOrder(order);
-                    fColumnOrder = fTable.getColumnOrder();
-                }
-            });
+            column.addControlListener(new ColumnListener());
+            fColumnResizable[i] = column.getResizable();
+            i++;
         }
         fColumnOrder = fTable.getColumnOrder();
 
@@ -507,191 +945,26 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         createHeaderEditor();
 
         // Handle the table item selection
-        fTable.addSelectionListener(new SelectionAdapter() {
-            @Override
-            public void widgetSelected(final SelectionEvent e) {
-                if (e.item == null) {
-                    return;
-                }
-                updateStatusLine(null);
-                if (fTable.getSelectionIndices().length > 0) {
-                    if (e.item.getData(Key.RANK) instanceof Long) {
-                        fSelectedRank = (Long) e.item.getData(Key.RANK);
-                        fRawViewer.selectAndReveal((Long) e.item.getData(Key.RANK));
-                    } else {
-                        fSelectedRank = -1;
-                    }
-                    if (fTable.getSelectionIndices().length == 1) {
-                        fSelectedBeginRank = fSelectedRank;
-                    }
-                    if (e.item.getData(Key.TIMESTAMP) instanceof ITmfTimestamp) {
-                        final ITmfTimestamp ts = NonNullUtils.checkNotNull((ITmfTimestamp) e.item.getData(Key.TIMESTAMP));
-                        if (fTable.getSelectionIndices().length == 1) {
-                            fSelectedBeginTimestamp = ts;
-                        }
-                        ITmfTimestamp selectedBeginTimestamp = fSelectedBeginTimestamp;
-                        if (selectedBeginTimestamp != null) {
-                            broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, selectedBeginTimestamp, ts));
-                            if (fTable.getSelectionIndices().length == 2) {
-                                updateStatusLine(ts.getDelta(selectedBeginTimestamp));
-                            }
-                        }
-                    } else {
-                        if (fTable.getSelectionIndices().length == 1) {
-                            fSelectedBeginTimestamp = null;
-                        }
-                    }
-                }
-                if (e.item.getData() instanceof ITmfEvent) {
-                    broadcast(new TmfEventSelectedSignal(TmfEventsTable.this, (ITmfEvent) e.item.getData()));
-                    fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(e.item.getData())));
-                } else {
-                    fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
-                }
-            }
-        });
+        fTable.addSelectionListener(new TableSelectionListener());
 
         int realCacheSize = Math.max(cacheSize, Display.getDefault().getBounds().height / fTable.getItemHeight());
         realCacheSize = Math.min(realCacheSize, MAX_CACHE_SIZE);
         fCache = new TmfEventsCache(realCacheSize, this);
 
         // Handle the table item requests
-        fTable.addListener(SWT.SetData, new Listener() {
-
-            @Override
-            public void handleEvent(final Event event) {
-
-                final TableItem item = (TableItem) event.item;
-                int index = event.index - 1; // -1 for the header row
-
-                if (event.index == 0) {
-                    setHeaderRowItemData(item);
-                    return;
-                }
-
-                if (fTable.getData(Key.FILTER_OBJ) != null) {
-                    if ((event.index == 1) || (event.index == (fTable.getItemCount() - 1))) {
-                        setFilterStatusRowItemData(item);
-                        return;
-                    }
-                    /* -1 for top filter status row */
-                    index = index - 1;
-                }
-
-                final CachedEvent cachedEvent = fCache.getEvent(index);
-                if (cachedEvent != null) {
-                    setItemData(item, cachedEvent, cachedEvent.rank);
-                    return;
-                }
-
-                // Else, fill the cache asynchronously (and off the UI thread)
-                event.doit = false;
-            }
+        fTable.addListener(SWT.SetData, new SetDataListener());
+
+        fTable.addMenuDetectListener(event -> {
+            fLastMenuCursorLocation = new Point(event.x, event.y);
+            Point pt = fTable.getDisplay().map(null, fTable, fLastMenuCursorLocation);
+            Rectangle clientArea = fTable.getClientArea();
+            boolean header = clientArea.y <= pt.y && pt.y < (clientArea.y + fTable.getHeaderHeight());
+            fTable.setMenu(header ? fHeaderMenu : fTablePopup);
         });
 
-        fTable.addListener(SWT.MenuDetect, new Listener() {
-            @Override
-            public void handleEvent(Event event) {
-                fLastMenuCursorLocation = new Point(event.x, event.y);
-                Point pt = fTable.getDisplay().map(null, fTable, fLastMenuCursorLocation);
-                Rectangle clientArea = fTable.getClientArea();
-                boolean header = clientArea.y <= pt.y && pt.y < (clientArea.y + fTable.getHeaderHeight());
-                fTable.setMenu(header ? fHeaderMenu : fTablePopup);
-            }
-        });
+        fTable.addMouseListener(new MouseDoubleClickListener());
 
-        fTable.addMouseListener(new MouseAdapter() {
-            @Override
-            public void mouseDoubleClick(final MouseEvent event) {
-                if (event.button != 1) {
-                    return;
-                }
-                // Identify the selected row
-                final Point point = new Point(event.x, event.y);
-                final TableItem item = fTable.getItem(point);
-                if (item != null) {
-                    final Rectangle imageBounds = item.getImageBounds(0);
-                    imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
-                    if (imageBounds.contains(point)) {
-                        final Long rank = (Long) item.getData(Key.RANK);
-                        if (rank != null) {
-                            toggleBookmark(rank);
-                        }
-                    }
-                }
-            }
-        });
-
-        final Listener tooltipListener = new Listener() {
-            Shell tooltipShell = null;
-
-            @Override
-            public void handleEvent(final Event event) {
-                switch (event.type) {
-                case SWT.MouseHover:
-                    final TableItem item = fTable.getItem(new Point(event.x, event.y));
-                    if (item == null) {
-                        return;
-                    }
-                    final Long rank = (Long) item.getData(Key.RANK);
-                    if (rank == null) {
-                        return;
-                    }
-                    final String tooltipText = (String) item.getData(Key.BOOKMARK);
-                    final Rectangle bounds = item.getImageBounds(0);
-                    bounds.width = BOOKMARK_IMAGE.getBounds().width;
-                    if (!bounds.contains(event.x, event.y)) {
-                        return;
-                    }
-                    if ((tooltipShell != null) && !tooltipShell.isDisposed()) {
-                        tooltipShell.dispose();
-                    }
-                    tooltipShell = new Shell(fTable.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
-                    tooltipShell.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
-                    final FillLayout layout = new FillLayout();
-                    layout.marginWidth = 2;
-                    tooltipShell.setLayout(layout);
-                    final Label label = new Label(tooltipShell, SWT.WRAP);
-                    String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : EMPTY_STRING); //$NON-NLS-1$
-                    label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
-                    label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
-                    label.setText(text);
-                    label.addListener(SWT.MouseExit, this);
-                    label.addListener(SWT.MouseDown, this);
-                    label.addListener(SWT.MouseWheel, this);
-                    final Point size = tooltipShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
-                    /*
-                     * Bug in Linux. The coordinates of the event have an origin
-                     * that excludes the table header but the method toDisplay()
-                     * expects coordinates relative to an origin that includes
-                     * the table header.
-                     */
-                    int y = event.y;
-                    if (IS_LINUX) {
-                        y += fTable.getHeaderHeight();
-                    }
-                    Point pt = fTable.toDisplay(event.x, y);
-                    pt.x += BOOKMARK_IMAGE.getBounds().width;
-                    pt.y += item.getBounds().height;
-                    tooltipShell.setBounds(pt.x, pt.y, size.x, size.y);
-                    tooltipShell.setVisible(true);
-                    break;
-                case SWT.Dispose:
-                case SWT.KeyDown:
-                case SWT.MouseMove:
-                case SWT.MouseExit:
-                case SWT.MouseDown:
-                case SWT.MouseWheel:
-                    if (tooltipShell != null) {
-                        tooltipShell.dispose();
-                        tooltipShell = null;
-                    }
-                    break;
-                default:
-                    break;
-                }
-            }
-        };
+        final Listener tooltipListener = new TooltipListener();
 
         fTable.addListener(SWT.MouseHover, tooltipListener);
         fTable.addListener(SWT.Dispose, tooltipListener);
@@ -701,94 +974,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fTable.addListener(SWT.MouseDown, tooltipListener);
         fTable.addListener(SWT.MouseWheel, tooltipListener);
 
-        fTable.addListener(SWT.EraseItem, new Listener() {
-            @Override
-            public void handleEvent(Event event) {
-                TableItem item = (TableItem) event.item;
-                List<?> styleRanges = (List<?>) item.getData(Key.STYLE_RANGES);
-
-                GC gc = event.gc;
-                Color background = item.getBackground(event.index);
-                /*
-                 * Paint the background if it is not the default system color.
-                 * In Windows, if you let the widget draw the background, it
-                 * will not show the item's background color if the item is
-                 * selected or hot. If there are no style ranges and the item
-                 * background is the default system color, we do not want to
-                 * paint it or otherwise we would override the platform theme
-                 * (e.g. alternating colors).
-                 */
-                if (styleRanges != null || !background.equals(item.getParent().getBackground())) {
-                    // we will paint the table item's background
-                    event.detail &= ~SWT.BACKGROUND;
-
-                    // paint the item's default background
-                    gc.setBackground(background);
-                    gc.fillRectangle(event.x, event.y, event.width, event.height);
-                }
-
-                /*
-                 * We will paint the table item's foreground. In Windows, if you
-                 * paint the background but let the widget draw the foreground,
-                 * it will override your background, unless the item is selected
-                 * or hot.
-                 */
-                event.detail &= ~SWT.FOREGROUND;
-
-                // paint the highlighted background for all style ranges
-                if (styleRanges != null) {
-                    Rectangle textBounds = item.getTextBounds(event.index);
-                    String text = item.getText(event.index);
-                    for (Object o : styleRanges) {
-                        if (o instanceof StyleRange) {
-                            StyleRange styleRange = (StyleRange) o;
-                            if (styleRange.data.equals(event.index)) {
-                                int startIndex = styleRange.start;
-                                int endIndex = startIndex + styleRange.length;
-                                int startX = gc.textExtent(text.substring(0, startIndex)).x;
-                                int endX = gc.textExtent(text.substring(0, endIndex)).x;
-                                gc.setBackground(styleRange.background);
-                                gc.fillRectangle(textBounds.x + startX, textBounds.y, (endX - startX), textBounds.height);
-                            }
-                        }
-                    }
-                }
-            }
-        });
+        fTable.addListener(SWT.EraseItem, new EraseItemListener());
 
-        fTable.addListener(SWT.PaintItem, new Listener() {
-            @Override
-            public void handleEvent(Event event) {
-                TableItem item = (TableItem) event.item;
-
-                // we promised to paint the table item's foreground
-                GC gc = event.gc;
-                Image image = item.getImage(event.index);
-                if (image != null) {
-                    Rectangle imageBounds = item.getImageBounds(event.index);
-                    /*
-                     * The image bounds don't match the default image position.
-                     */
-                    if (IS_LINUX) {
-                        gc.drawImage(image, imageBounds.x + 1, imageBounds.y + 3);
-                    } else {
-                        gc.drawImage(image, imageBounds.x, imageBounds.y + 1);
-                    }
-                }
-                gc.setForeground(item.getForeground(event.index));
-                gc.setFont(item.getFont(event.index));
-                String text = item.getText(event.index);
-                Rectangle textBounds = item.getTextBounds(event.index);
-                /*
-                 * The text bounds don't match the default text position.
-                 */
-                if (IS_LINUX) {
-                    gc.drawText(text, textBounds.x + 1, textBounds.y + 3, true);
-                } else {
-                    gc.drawText(text, textBounds.x - 1, textBounds.y + 2, true);
-                }
-            }
-        });
+        fTable.addListener(SWT.PaintItem, new PainItemListener());
 
         // Create resources
         createResources();
@@ -803,75 +991,16 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
         fRawViewer = new TmfRawEventViewer(fSashForm, SWT.H_SCROLL | SWT.V_SCROLL);
 
-        fRawViewer.addSelectionListener(new Listener() {
-            @Override
-            public void handleEvent(final Event e) {
-                if (fTrace == null) {
-                    return;
-                }
-                long rank;
-                if (e.data instanceof Long) {
-                    rank = (Long) e.data;
-                } else if (e.data instanceof ITmfLocation) {
-                    rank = findRank((ITmfLocation) e.data);
-                } else {
-                    return;
-                }
-                int index = (int) rank;
-                if (fTable.getData(Key.FILTER_OBJ) != null) {
-                    // +1 for top filter status row
-                    index = fCache.getFilteredEventIndex(rank) + 1;
-                }
-                // +1 for header row
-                fTable.setSelection(index + 1);
-                fSelectedRank = rank;
-                fSelectedBeginRank = fSelectedRank;
-                updateStatusLine(null);
-                final TableItem[] selection = fTable.getSelection();
-                if ((selection != null) && (selection.length > 0)) {
-                    TableItem item = fTable.getSelection()[0];
-                    final TmfTimestamp ts = (TmfTimestamp) item.getData(Key.TIMESTAMP);
-                    if (ts != null) {
-                        broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, ts));
-                    }
-                    if (item.getData() instanceof ITmfEvent) {
-                        broadcast(new TmfEventSelectedSignal(TmfEventsTable.this, (ITmfEvent) item.getData()));
-                        fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(item.getData())));
-                    } else {
-                        fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
-                    }
-                }
-            }
-
-            private long findRank(final ITmfLocation selectedLocation) {
-                final double selectedRatio = fTrace.getLocationRatio(selectedLocation);
-                long low = 0;
-                long high = fTrace.getNbEvents();
-                long rank = high / 2;
-                double ratio = -1;
-                while (ratio != selectedRatio) {
-                    ITmfContext context = fTrace.seekEvent(rank);
-                    ratio = fTrace.getLocationRatio(context.getLocation());
-                    context.dispose();
-                    if (ratio < selectedRatio) {
-                        low = rank;
-                        rank = (rank + high) / 2;
-                    } else if (ratio > selectedRatio) {
-                        high = rank;
-                        rank = (rank + low) / 2;
-                    }
-                    if ((high - low) < 2) {
-                        break;
-                    }
-                }
-                return rank;
-            }
-        });
+        fRawViewer.addSelectionListener(new RawSelectionListener());
 
         fSashForm.setWeights(new int[] { 1, 1 });
         fRawViewer.setVisible(false);
 
         createPopupMenu();
+
+        fComposite.addDisposeListener((e) -> {
+            internalDispose();
+        });
     }
 
     /**
@@ -882,18 +1011,25 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      * @param column
      *            the column
      */
-    private static IAction createHeaderAction(final TableColumn column) {
+    private IAction createHeaderAction(final TableColumn column) {
         final IAction columnMenuAction = new Action(column.getText(), IAction.AS_CHECK_BOX) {
             @Override
             public void run() {
-                if (isChecked()) {
-                    column.setWidth((int) column.getData(Key.WIDTH));
+                boolean isChecked = isChecked();
+                if (isChecked) {
+                    int width = (int) column.getData(Key.WIDTH);
                     column.setResizable(true);
+                    if (width == 0) {
+                        column.pack();
+                    } else {
+                        column.setWidth(width);
+                    }
                 } else {
-                    column.setData(Key.WIDTH, column.getWidth());
-                    column.setWidth(0);
                     column.setResizable(false);
+                    column.setWidth(0);
                 }
+                int pos = (int) column.getData(Key.INDEX);
+                fColumnResizable[pos] = isChecked;
             }
         };
         columnMenuAction.setChecked(column.getResizable());
@@ -905,23 +1041,16 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             @Override
             public void run() {
                 for (TableColumn column : fTable.getColumns()) {
-                    final Object widthVal = column.getData(Key.WIDTH);
-                    if (widthVal instanceof Integer) {
-                        Integer width = (Integer) widthVal;
-                        if (!column.getResizable()) {
+                    int index = (int) column.getData(Key.INDEX);
+                    if (index != MARGIN_COLUMN_INDEX) {
+                        final int width = (int) column.getData(Key.WIDTH);
+                        column.setResizable(true);
+                        if (width == 0) {
+                            column.pack();
+                        } else {
                             column.setWidth(width);
-                            column.setResizable(true);
-                            /*
-                             * This is because Linux always resizes the last
-                             * column to fill in the void, this means that
-                             * hiding a column resizes others and we can have 10
-                             * columns that are 1000 pixels wide by hiding the
-                             * last one progressively.
-                             */
-                            if (IS_LINUX) {
-                                column.pack();
-                            }
                         }
+                        fColumnResizable[index] = true;
                     }
                 }
             }
@@ -948,7 +1077,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 for (int i : fTable.getColumnOrder()) {
                     TableColumn column = fTable.getColumns()[i];
                     // Omit the margin column and hidden columns
-                    if (i >= EVENT_COLUMNS_START_INDEX && (column.getResizable() || column.getWidth() > 0)) {
+                    if (isVisibleEventColumn(column)) {
                         columns.add(fColumns.get(i));
                     }
                 }
@@ -970,7 +1099,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         final IAction showTableAction = new Action(Messages.TmfEventsTable_ShowTableActionText) {
             @Override
             public void run() {
-                fTable.setVisible(true);
+                fTableComposite.setVisible(true);
                 fSashForm.layout();
             }
         };
@@ -978,7 +1107,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         final IAction hideTableAction = new Action(Messages.TmfEventsTable_HideTableActionText) {
             @Override
             public void run() {
-                fTable.setVisible(false);
+                fTableComposite.setVisible(false);
                 fSashForm.layout();
             }
         };
@@ -1013,21 +1142,61 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 final TableItem item = items[0];
 
                 final Object data = item.getData();
-                if (data instanceof ITmfSourceLookup) {
-                    ITmfSourceLookup event = (ITmfSourceLookup) data;
-                    ITmfCallsite cs = event.getCallsite();
-                    if (cs == null || cs.getFileName() == null) {
-                        return;
-                    }
-                    IMarker marker = null;
-                    try {
-                        String fileName = cs.getFileName();
-                        final String trimmedPath = fileName.replaceAll("\\.\\./", EMPTY_STRING); //$NON-NLS-1$
+                if (!(data instanceof ITmfSourceLookup)) {
+                    return;
+                }
+                ITmfSourceLookup event = (ITmfSourceLookup) data;
+                ITmfCallsite cs = event.getCallsite();
+                if (cs == null) {
+                    return;
+                }
+                Long lineNo = cs.getLineNo();
+                if (lineNo == null) {
+                    /* Not enough information to provide a full callsite */
+                    return;
+                }
+
+                String fileName = cs.getFileName();
+                final String trimmedPath = fileName.replaceAll("\\.\\./", EMPTY_STRING); //$NON-NLS-1$
+                File fileToOpen = new File(trimmedPath);
+
+                try {
+                    if (fileToOpen.exists() && fileToOpen.isFile()) {
+                        /*
+                         * The path points to a "real" file, attempt to open
+                         * that
+                         */
+                        IFileStore fileStore = EFS.getLocalFileSystem().getStore(fileToOpen.toURI());
+                        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+
+                        IEditorPart editor = IDE.openEditorOnFileStore(page, fileStore);
+                        if (editor instanceof ITextEditor) {
+                            /*
+                             * Calculate the "document offset" corresponding to
+                             * the line number, then seek there.
+                             */
+                            ITextEditor textEditor = (ITextEditor) editor;
+                            int lineNumber = lineNo.intValue();
+                            IDocument document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
+
+                            IRegion region = document.getLineInformation(lineNumber - 1);
+                            if (region != null) {
+                                textEditor.selectAndReveal(region.getOffset(), region.getLength());
+                            }
+                        }
+
+                    } else {
+                        /*
+                         * The file was not found on disk, attempt to find it in
+                         * the workspace instead.
+                         */
+                        IMarker marker = null;
                         final ArrayList<IFile> files = new ArrayList<>();
+                        IPath p = new Path(trimmedPath);
                         ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
                             @Override
                             public boolean visit(IResource resource) throws CoreException {
-                                if (resource instanceof IFile && resource.getFullPath().toString().endsWith(trimmedPath)) {
+                                if (resource instanceof IFile && resource.getFullPath().toString().endsWith(p.lastSegment())) {
                                     files.add((IFile) resource);
                                 }
                                 return true;
@@ -1056,15 +1225,15 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                         }
                         if (file != null) {
                             marker = file.createMarker(IMarker.MARKER);
-                            marker.setAttribute(IMarker.LINE_NUMBER, Long.valueOf(cs.getLineNumber()).intValue());
+                            marker.setAttribute(IMarker.LINE_NUMBER, lineNo.intValue());
                             IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), marker);
                             marker.delete();
-                        } else if (files.size() == 0) {
+                        } else if (files.isEmpty()) {
                             displayException(new FileNotFoundException('\'' + cs.toString() + '\'' + '\n' + Messages.TmfEventsTable_OpenSourceCodeNotFound));
                         }
-                    } catch (CoreException e) {
-                        displayException(e);
                     }
+                } catch (BadLocationException | CoreException e) {
+                    displayException(e);
                 }
             }
         };
@@ -1139,7 +1308,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     for (int i : fTable.getColumnOrder()) {
                         TableColumn column = fTable.getColumns()[i];
                         // Omit the margin column and hidden columns
-                        if (i >= EVENT_COLUMNS_START_INDEX && (column.getResizable() || column.getWidth() > 0)) {
+                        if (isVisibleEventColumn(column)) {
                             exportColumns.add(fColumns.get(i));
                         }
                     }
@@ -1152,21 +1321,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             }
         };
 
-        final IAction showSearchBarAction = new Action(Messages.TmfEventsTable_ShowSearchBarActionText) {
-            @Override
-            public void run() {
-                fHeaderState = HeaderState.SEARCH;
-                fTable.refresh();
-                fTable.redraw();
-            }
-        };
-
-        final IAction showFilterBarAction = new Action(Messages.TmfEventsTable_ShowFilterBarActionText) {
+        final IAction addAsFilterAction = new Action(Messages.TmfEventsTable_AddAsFilterText) {
             @Override
             public void run() {
-                fHeaderState = HeaderState.FILTER;
-                fTable.refresh();
-                fTable.redraw();
+                applySearchAsFilter();
             }
         };
 
@@ -1220,10 +1378,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             public void menuAboutToShow(final IMenuManager manager) {
                 if (fTable.getSelectionIndices().length == 1 && fTable.getSelectionIndices()[0] == 0) {
                     // Right-click on header row
-                    if (fHeaderState == HeaderState.FILTER) {
-                        fTablePopupMenuManager.add(showSearchBarAction);
-                    } else {
-                        fTablePopupMenuManager.add(showFilterBarAction);
+                    if (fHeaderState == HeaderState.SEARCH) {
+                        fTablePopupMenuManager.add(addAsFilterAction);
                     }
                     return;
                 }
@@ -1303,7 +1459,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     }
                 }
 
-                if (isCollapsible && !(fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter)) {
+                if (isCollapsible && !fCollapseFilterEnabled) {
                     fTablePopupMenuManager.add(collapseAction);
                     fTablePopupMenuManager.add(new Separator());
                 }
@@ -1379,16 +1535,19 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
     @Override
     public void dispose() {
+        fComposite.dispose();
+    }
+
+    private void internalDispose() {
         stopSearchThread();
         stopFilterThread();
         PlatformUI.getWorkbench().getThemeManager().removePropertyChangeListener(this);
         ColorSettingsManager.removeColorSettingsListener(this);
-        fComposite.dispose();
+        fCache.clear();
         if ((fTrace != null) && fDisposeOnClose) {
             fTrace.dispose();
         }
         fResourceManager.dispose();
-        fRawViewer.dispose();
         if (fRawViewerPopupMenuManager != null) {
             fRawViewerPopupMenuManager.dispose();
         }
@@ -1452,7 +1611,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         }
         item.setText(itemStrings);
         item.setData(tmfEvent);
-        item.setData(Key.TIMESTAMP, new TmfTimestamp(tmfEvent.getTimestamp()));
+        item.setData(Key.TIMESTAMP, tmfEvent.getTimestamp());
         item.setData(Key.RANK, rank);
 
         final Collection<Long> markerIds = fBookmarksMap.get(rank);
@@ -1465,7 +1624,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             try {
                 for (long markerId : markerIds) {
                     final IMarker marker = fBookmarksFile.findMarker(markerId);
-                    parts.add(marker.getAttribute(IMarker.MESSAGE));
+                    if (marker != null) {
+                        parts.add(marker.getAttribute(IMarker.MESSAGE));
+                    }
                 }
             } catch (CoreException e) {
                 displayException(e);
@@ -1512,10 +1673,12 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         for (int index = 0; index < fTable.getColumns().length; index++) {
             TableColumn column = fTable.getColumns()[index];
             String regex = null;
-            if (fHeaderState == HeaderState.FILTER) {
+            if (fHeaderState == HeaderState.SEARCH) {
+                if (searchMatch) {
+                    regex = (String) column.getData(Key.SEARCH_TXT);
+                }
+            } else {
                 regex = (String) column.getData(Key.FILTER_TXT);
-            } else if (searchMatch) {
-                regex = (String) column.getData(Key.SEARCH_TXT);
             }
             if (regex != null) {
                 String text = item.getText(index);
@@ -1555,25 +1718,18 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      *            The item to use as table header
      */
     protected void setHeaderRowItemData(final TableItem item) {
-        String txtKey = null;
-        if (fHeaderState == HeaderState.SEARCH) {
+        if (fHeaderState == HeaderState.NO_SEARCH) {
             item.setImage(SEARCH_IMAGE);
-            txtKey = Key.SEARCH_TXT;
-        } else if (fHeaderState == HeaderState.FILTER) {
-            item.setImage(FILTER_IMAGE);
-            txtKey = Key.FILTER_TXT;
+        } else if (fHeaderState == HeaderState.SEARCH) {
+            item.setImage(FILTER_ADD_IMAGE);
         }
         item.setForeground(fGrayColor);
         // Ignore collapse and image column
         for (int i = EVENT_COLUMNS_START_INDEX; i < fTable.getColumns().length; i++) {
             final TableColumn column = fTable.getColumns()[i];
-            final String filter = (String) column.getData(txtKey);
+            final String filter = (String) column.getData(Key.SEARCH_TXT);
             if (filter == null) {
-                if (fHeaderState == HeaderState.SEARCH) {
-                    item.setText(i, SEARCH_HINT);
-                } else if (fHeaderState == HeaderState.FILTER) {
-                    item.setText(i, FILTER_HINT);
-                }
+                item.setText(i, SEARCH_HINT);
                 item.setForeground(i, fGrayColor);
                 item.setFont(i, fFont);
             } else {
@@ -1582,6 +1738,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 item.setFont(i, fBoldFont);
             }
         }
+        if (!fPackMarginDone) {
+            packMarginColumn();
+            fPackMarginDone = true;
+        }
     }
 
     /**
@@ -1646,13 +1806,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     // Margin column selected
                     if (item.getBounds(0).contains(point)) {
                         if (fHeaderState == HeaderState.SEARCH) {
-                            fHeaderState = HeaderState.FILTER;
-                        } else if (fHeaderState == HeaderState.FILTER) {
-                            fHeaderState = HeaderState.SEARCH;
+                            applySearchAsFilter();
                         }
-                        fTable.setSelection(0);
-                        fTable.refresh();
-                        fTable.redraw();
                         return;
                     }
 
@@ -1672,19 +1827,12 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
                     column = fTable.getColumns()[columnIndex];
 
-                    String txtKey = null;
-                    if (fHeaderState == HeaderState.SEARCH) {
-                        txtKey = Key.SEARCH_TXT;
-                    } else if (fHeaderState == HeaderState.FILTER) {
-                        txtKey = Key.FILTER_TXT;
-                    }
-
                     /*
                      * The control that will be the editor must be a child of
                      * the Table
                      */
                     final Text newEditor = (Text) fTable.createTableEditorControl(Text.class);
-                    final String headerString = (String) column.getData(txtKey);
+                    final String headerString = (String) column.getData(Key.SEARCH_TXT);
                     if (headerString != null) {
                         newEditor.setText(headerString);
                     }
@@ -1703,7 +1851,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                             if (e.character == SWT.CR) {
                                 updateHeader(newEditor.getText());
                                 applyHeader();
-
+                                if ((e.stateMask & SWT.CTRL) != 0) {
+                                    applySearchAsFilter();
+                                }
                                 /*
                                  * Set focus on the table so that the next
                                  * carriage return goes to the next result
@@ -1711,6 +1861,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                                 TmfEventsTable.this.getTable().setFocus();
                             } else if (e.character == SWT.ESC) {
                                 tableEditor.getEditor().dispose();
+                                TmfEventsTable.this.getTable().setFocus();
                             }
                         }
                     });
@@ -1724,28 +1875,19 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
              * returns true is value was changed
              */
             private boolean updateHeader(final String regex) {
-                String objKey = null;
-                String txtKey = null;
-                if (fHeaderState == HeaderState.SEARCH) {
-                    objKey = Key.SEARCH_OBJ;
-                    txtKey = Key.SEARCH_TXT;
-                } else if (fHeaderState == HeaderState.FILTER) {
-                    objKey = Key.FILTER_OBJ;
-                    txtKey = Key.FILTER_TXT;
-                }
                 if (regex.length() > 0) {
                     try {
                         Pattern.compile(regex);
-                        if (regex.equals(column.getData(txtKey))) {
+                        if (regex.equals(column.getData(Key.SEARCH_TXT))) {
                             tableEditor.getEditor().dispose();
                             return false;
                         }
                         final TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null);
-                        ITmfEventAspect aspect = (ITmfEventAspect) column.getData(Key.ASPECT);
+                        ITmfEventAspect<?> aspect = (ITmfEventAspect<?>) column.getData(Key.ASPECT);
                         filter.setEventAspect(aspect);
                         filter.setRegex(regex);
-                        column.setData(objKey, filter);
-                        column.setData(txtKey, regex);
+                        column.setData(Key.SEARCH_OBJ, filter);
+                        column.setData(Key.SEARCH_TXT, regex);
                     } catch (final PatternSyntaxException ex) {
                         tableEditor.getEditor().dispose();
                         MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
@@ -1753,50 +1895,37 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                         return false;
                     }
                 } else {
-                    if (column.getData(txtKey) == null) {
+                    if (column.getData(Key.SEARCH_TXT) == null) {
                         tableEditor.getEditor().dispose();
                         return false;
                     }
-                    column.setData(objKey, null);
-                    column.setData(txtKey, null);
+                    column.setData(Key.SEARCH_OBJ, null);
+                    column.setData(Key.SEARCH_TXT, null);
                 }
                 return true;
             }
 
             private void applyHeader() {
-                if (fHeaderState == HeaderState.SEARCH) {
-                    stopSearchThread();
-                    final TmfFilterAndNode filter = new TmfFilterAndNode(null);
-                    for (final TableColumn col : fTable.getColumns()) {
-                        final Object filterObj = col.getData(Key.SEARCH_OBJ);
-                        if (filterObj instanceof ITmfFilterTreeNode) {
-                            filter.addChild((ITmfFilterTreeNode) filterObj);
-                        }
-                    }
-                    if (filter.getChildrenCount() > 0) {
-                        fTable.setData(Key.SEARCH_OBJ, filter);
-                        fTable.refresh();
-                        searchNext();
-                        fireSearchApplied(filter);
-                    } else {
-                        fTable.setData(Key.SEARCH_OBJ, null);
-                        fTable.refresh();
-                        fireSearchApplied(null);
-                    }
-                } else if (fHeaderState == HeaderState.FILTER) {
-                    final TmfFilterAndNode filter = new TmfFilterAndNode(null);
-                    for (final TableColumn col : fTable.getColumns()) {
-                        final Object filterObj = col.getData(Key.FILTER_OBJ);
-                        if (filterObj instanceof ITmfFilterTreeNode) {
-                            filter.addChild((ITmfFilterTreeNode) filterObj);
-                        }
-                    }
-                    if (filter.getChildrenCount() > 0) {
-                        applyFilter(filter);
-                    } else {
-                        clearFilters();
+                stopSearchThread();
+                final TmfFilterRootNode filter = new TmfFilterRootNode();
+                for (final TableColumn col : fTable.getColumns()) {
+                    final Object filterObj = col.getData(Key.SEARCH_OBJ);
+                    if (filterObj instanceof ITmfFilterTreeNode) {
+                        filter.addChild((ITmfFilterTreeNode) filterObj);
                     }
                 }
+                if (filter.getChildrenCount() > 0) {
+                    fHeaderState = HeaderState.SEARCH;
+                    fTable.setData(Key.SEARCH_OBJ, filter);
+                    fTable.refresh();
+                    searchNext();
+                    fireSearchApplied(filter);
+                } else {
+                    fHeaderState = HeaderState.NO_SEARCH;
+                    fTable.setData(Key.SEARCH_OBJ, null);
+                    fTable.refresh();
+                    fireSearchApplied(null);
+                }
 
                 tableEditor.getEditor().dispose();
             }
@@ -1812,19 +1941,28 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     fTable.refresh();
                 } else if (e.character == SWT.DEL) {
                     if (fHeaderState == HeaderState.SEARCH) {
+                        fHeaderState = HeaderState.NO_SEARCH;
                         stopSearchThread();
                         for (final TableColumn column : fTable.getColumns()) {
                             column.setData(Key.SEARCH_OBJ, null);
                             column.setData(Key.SEARCH_TXT, null);
+                            column.setData(Key.FILTER_TXT, null);
                         }
                         fTable.setData(Key.SEARCH_OBJ, null);
                         fTable.refresh();
                         fireSearchApplied(null);
-                    } else if (fHeaderState == HeaderState.FILTER) {
-                        clearFilters();
+                    } else {
+                        for (final TableColumn column : fTable.getColumns()) {
+                            column.setData(Key.FILTER_TXT, null);
+                        }
+                        fTable.refresh();
                     }
                 } else if (e.character == SWT.CR) {
-                    if ((e.stateMask & SWT.SHIFT) == 0) {
+                    if ((e.stateMask & SWT.CTRL) != 0) {
+                        if (fHeaderState == HeaderState.SEARCH) {
+                            applySearchAsFilter();
+                        }
+                    } else if ((e.stateMask & SWT.SHIFT) == 0) {
                         searchNext();
                     } else {
                         searchPrevious();
@@ -1834,6 +1972,27 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         });
     }
 
+    /**
+     * Apply the current search condition as a new filter.
+     *
+     * @since 2.0
+     */
+    protected void applySearchAsFilter() {
+        Object searchObj = fTable.getData(Key.SEARCH_OBJ);
+        if (searchObj instanceof ITmfFilter) {
+            ITmfFilter filter = (ITmfFilter) searchObj;
+            fTable.setData(Key.SEARCH_OBJ, null);
+            fireSearchApplied(null);
+            fHeaderState = HeaderState.NO_SEARCH;
+            for (final TableColumn col : fTable.getColumns()) {
+                col.setData(Key.FILTER_TXT, col.getData(Key.SEARCH_TXT));
+                col.setData(Key.SEARCH_TXT, null);
+                col.setData(Key.SEARCH_OBJ, null);
+            }
+            applyFilter(filter);
+        }
+    }
+
     /**
      * Send an event indicating a filter has been applied.
      *
@@ -1887,7 +2046,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     }
 
     /**
-     * Apply a filter.
+     * Apply a filter. It is added to the existing filters.
      *
      * @param filter
      *            The filter to apply
@@ -1897,13 +2056,78 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         stopSearchThread();
         fFilterMatchCount = 0;
         fFilterCheckCount = 0;
-        fCache.applyFilter(filter);
+        ITmfFilterTreeNode rootFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
+        if (rootFilter == null) {
+            rootFilter = new TmfFilterRootNode();
+        }
+        if (filter instanceof TmfFilterRootNode) {
+            TmfFilterRootNode parentFilter = (TmfFilterRootNode) filter;
+            for (ITmfFilterTreeNode child : parentFilter.getChildren()) {
+                rootFilter.addChild(child);
+            }
+        } else if (filter instanceof TmfCollapseFilter) {
+            fCollapseFilterEnabled = true;
+        } else if (filter instanceof ITmfFilterTreeNode) {
+            rootFilter.addChild((ITmfFilterTreeNode) filter);
+        } else {
+            rootFilter.addChild(new TmfFilterObjectNode(filter));
+        }
+        fCache.applyFilter(rootFilter, fCollapseFilterEnabled);
+        fHeaderBar.addFilter(filter);
+        fTable.clearAll();
+        fTable.setData(Key.FILTER_OBJ, rootFilter);
+        /* +1 for header row, +2 for top and bottom filter status rows */
+        fTable.setItemCount(3);
+        startFilterThread();
+        fireFilterApplied(rootFilter);
+    }
+
+    /**
+     * Remove a filter. Any other existing filters remain applied.
+     *
+     * @param filter
+     *            The filter to remove
+     * @since 2.0
+     */
+    protected void removeFilter(ITmfFilter filter) {
+        ITmfFilterTreeNode rootFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
+        if (rootFilter == null) {
+            return;
+        }
+        stopFilterThread();
+        stopSearchThread();
+        fFilterMatchCount = 0;
+        fFilterCheckCount = 0;
+        if (filter instanceof TmfCollapseFilter) {
+            fCollapseFilterEnabled = false;
+        } else if (filter instanceof ITmfFilterTreeNode) {
+            rootFilter.removeChild((ITmfFilterTreeNode) filter);
+        } else {
+            for (ITmfFilterTreeNode child : rootFilter.getChildren()) {
+                if (child instanceof TmfFilterObjectNode) {
+                    if (((TmfFilterObjectNode) child).getFilter().equals(filter)) {
+                        rootFilter.removeChild(child);
+                        break;
+                    }
+                }
+            }
+        }
+        if (!rootFilter.hasChildren() && !fCollapseFilterEnabled) {
+            clearFilters();
+            return;
+        }
+        fCache.applyFilter(rootFilter, fCollapseFilterEnabled);
+        fHeaderBar.removeFilter(filter);
         fTable.clearAll();
-        fTable.setData(Key.FILTER_OBJ, filter);
+        fTable.setData(Key.FILTER_OBJ, rootFilter);
         /* +1 for header row, +2 for top and bottom filter status rows */
         fTable.setItemCount(3);
         startFilterThread();
-        fireFilterApplied(filter);
+        fireFilterApplied(rootFilter);
+
+        // Set original width
+        fTable.getColumns()[MARGIN_COLUMN_INDEX].setWidth(0);
+        packMarginColumn();
     }
 
     /**
@@ -1916,6 +2140,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         stopFilterThread();
         stopSearchThread();
         fCache.clearFilter();
+        fHeaderBar.clearFilters();
+        fCollapseFilterEnabled = false;
         fTable.clearAll();
         for (final TableColumn column : fTable.getColumns()) {
             column.setData(Key.FILTER_OBJ, null);
@@ -1950,6 +2176,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      */
     protected class FilterThread extends Thread {
         private final ITmfFilterTreeNode filter;
+        private TmfCollapseFilter collapseFilter = null;
         private TmfEventRequest request;
         private boolean refreshBusy = false;
         private boolean refreshPending = false;
@@ -1971,6 +2198,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             if (fTrace == null) {
                 return;
             }
+            if (fCollapseFilterEnabled) {
+                collapseFilter = new TmfCollapseFilter();
+            }
             final int nbRequested = (int) (fTrace.getNbEvents() - fFilterCheckCount);
             if (nbRequested <= 0) {
                 return;
@@ -1985,15 +2215,15 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     }
                     boolean refresh = false;
                     if (filter.matches(event)) {
-                        final long rank = fFilterCheckCount;
-                        final int index = (int) fFilterMatchCount;
-                        fFilterMatchCount++;
-                        fCache.storeEvent(event, rank, index);
-                        refresh = true;
-                    } else {
-                        if (filter instanceof TmfCollapseFilter) {
+                        if (collapseFilter == null || collapseFilter.matches(event)) {
+                            final long rank = fFilterCheckCount;
+                            final int index = (int) fFilterMatchCount;
+                            fFilterMatchCount++;
+                            fCache.storeEvent(event, rank, index);
+                        } else if (collapseFilter != null) {
                             fCache.updateCollapsedEvent((int) fFilterMatchCount - 1);
                         }
+                        refresh = true;
                     }
 
                     if (refresh || (fFilterCheckCount % 100) == 0) {
@@ -2304,6 +2534,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             final int selection = index + 1 + (eventFilter != null ? +1 : 0);
 
             display.asyncExec(new Runnable() {
+
                 @Override
                 public void run() {
                     if (monitor.isCanceled()) {
@@ -2327,15 +2558,19 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 }
             });
             return Status.OK_STATUS;
+
         }
 
         @Override
         protected void canceling() {
-            request.cancel();
+            if (request != null) {
+                request.cancel();
+            }
             synchronized (fSearchSyncObj) {
                 fSearchThread = null;
             }
         }
+
     }
 
     /**
@@ -2384,10 +2619,15 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
     /**
      * Pack the columns.
+     *
+     * @return Whether or not a pack was done in this call. Otherwise, it was
+     *         already done by a previous call
+     *
+     * @since 2.0
      */
-    protected void packColumns() {
+    protected boolean packColumns() {
         if (fPackDone) {
-            return;
+            return false;
         }
         fTable.setRedraw(false);
         try {
@@ -2402,6 +2642,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             fTable.setRedraw(true);
         }
         fPackDone = true;
+        return true;
     }
 
     private void packMarginColumn() {
@@ -2412,21 +2653,70 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     }
 
     private void packSingleColumn(int i, final TableColumn column) {
-        final int headerWidth = column.getWidth();
+        if (i != MARGIN_COLUMN_INDEX && !column.getResizable()) {
+            return;
+        }
+        Object data = column.getData(Key.WIDTH);
+        final int headerWidth = data instanceof Integer ? (int) data : -1;
         column.pack();
         /*
          * Workaround for Linux which doesn't consider the image width of
          * search/filter row in TableColumn.pack() after having executed
          * TableItem.setImage(null) for other rows than search/filter row.
          */
-        boolean isCollapseFilter = fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter;
-        if (IS_LINUX && (i == 0) && isCollapseFilter) {
+        if (IS_LINUX && (i == MARGIN_COLUMN_INDEX) && fCollapseFilterEnabled) {
             column.setWidth(column.getWidth() + SEARCH_IMAGE.getBounds().width);
         }
 
         if (column.getWidth() < headerWidth) {
             column.setWidth(headerWidth);
+        } else if (i != MARGIN_COLUMN_INDEX) {
+            column.setData(Key.WIDTH, column.getWidth());
+        }
+    }
+
+    /**
+     * Returns true if the column is a visible event column.
+     *
+     * @param column
+     *            the column
+     * @return false if the column is the margin column or hidden, true
+     *         otherwise
+     */
+    private static boolean isVisibleEventColumn(TableColumn column) {
+        if (column.getData(Key.ASPECT) == TmfMarginColumn.MARGIN_ASPECT) {
+            return false;
+        }
+        if (!column.getResizable() && column.getWidth() == 0) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns true if the column is expanded to take extra available space.
+     * This is the last non-zero-width visible column in the column order on
+     * Linux. This column's width should not be persisted.
+     *
+     * @param column
+     *            the column
+     * @return true if the column is expanded.
+     */
+    private static boolean isExpanded(TableColumn column) {
+        if (IS_LINUX) {
+            Table table = column.getParent();
+            int[] order = table.getColumnOrder();
+            for (int i = order.length - 1; i >= 0; i--) {
+                TableColumn col = table.getColumn(order[i]);
+                if (col == column) {
+                    return true;
+                }
+                if (col.getWidth() > 0) {
+                    return false;
+                }
+            }
         }
+        return false;
     }
 
     /**
@@ -2500,6 +2790,26 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         return fColumnOrder;
     }
 
+    /**
+     * Get column widths
+     *
+     * @return the current visual widths of the receiver's columns
+     * @since 2.1
+     */
+    public int[] getColumnWidth() {
+        return fColumnSize;
+    }
+
+    /**
+     * Get whether the columns are resizable
+     *
+     * @return an array stating if each column is resizable
+     * @since 2.1
+     */
+    public boolean[] getColumnResizable() {
+        return fColumnResizable;
+    }
+
     /**
      * Sets the order that the columns in the receiver should be displayed in to
      * the given argument which is described in terms of the zero-relative
@@ -2520,6 +2830,39 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fColumnOrder = fTable.getColumnOrder();
     }
 
+    /**
+     * Sets the column width and resizability
+     *
+     * @param width
+     *            an array of widths
+     * @param resizable
+     *            an array of bools saying if a column is resizable or not
+     * @since 2.1
+     */
+    public void setColumnWidth(int[] width, boolean[] resizable) {
+        int length = fTable.getColumns().length;
+        if (width == null || resizable == null || resizable.length != length || width.length != length) {
+            return;
+        }
+        int i = 0;
+        for (TableColumn column : fTable.getColumns()) {
+            if (i != MARGIN_COLUMN_INDEX) {
+                column.setData(Key.WIDTH, width[i]);
+                column.setResizable(resizable[i]);
+                if (column.getResizable()) {
+                    column.setWidth((int) column.getData(Key.WIDTH));
+                } else {
+                    column.setWidth(0);
+                }
+            }
+            i++;
+        }
+        fColumnSize = width;
+        fColumnResizable = resizable;
+        /* Don't pack, it would override these settings */
+        fPackDone = true;
+    }
+
     /**
      * Notify this table that is got the UI focus.
      */
@@ -2527,6 +2870,24 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fTable.setFocus();
     }
 
+    /**
+     * Registers context menus with a site for extension. This method can be
+     * called for part sites so that context menu contributions can be added.
+     *
+     * @param site
+     *            the site that the context menus will be registered for
+     *
+     * @since 1.2
+     */
+    public void registerContextMenus(IWorkbenchPartSite site) {
+        if (site instanceof IEditorSite) {
+            IEditorSite editorSite = (IEditorSite) site;
+            // Don't use the editor input when adding contributions, otherwise
+            // we get too many unwanted things.
+            editorSite.registerContextMenu(fTablePopupMenuManager, this, false);
+        }
+    }
+
     /**
      * Assign a new trace to this event table.
      *
@@ -2721,29 +3082,39 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             final TableItem tableItem = selection[0];
             if (tableItem.getData(Key.RANK) != null) {
                 final StringBuffer defaultMessage = new StringBuffer();
-                for (int i = 0; i < fTable.getColumns().length; i++) {
-                    if (i > 0) {
-                        defaultMessage.append(", "); //$NON-NLS-1$
+                for (int i : fTable.getColumnOrder()) {
+                    TableColumn column = fTable.getColumns()[i];
+                    // Omit the margin column and hidden columns
+                    if (isVisibleEventColumn(column)) {
+                        if (defaultMessage.length() > 0) {
+                            defaultMessage.append(", "); //$NON-NLS-1$
+                        }
+                        defaultMessage.append(tableItem.getText(i));
                     }
-                    defaultMessage.append(tableItem.getText(i));
                 }
-                final InputDialog dialog = new MultiLineInputDialog(
-                        PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
-                        Messages.TmfEventsTable_AddBookmarkDialogTitle,
-                        Messages.TmfEventsTable_AddBookmarkDialogMessage,
-                        defaultMessage.toString());
+                final AddBookmarkDialog dialog = new AddBookmarkDialog(
+                        PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), defaultMessage.toString());
                 if (dialog.open() == Window.OK) {
                     final String message = dialog.getValue();
                     try {
-                        final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
-                        if (bookmark.exists()) {
-                            bookmark.setAttribute(IMarker.MESSAGE, message.toString());
-                            final Long rank = (Long) tableItem.getData(Key.RANK);
-                            final int location = rank.intValue();
-                            bookmark.setAttribute(IMarker.LOCATION, Integer.valueOf(location));
-                            fBookmarksMap.put(rank, bookmark.getId());
-                            fTable.refresh();
-                        }
+                        final Long rank = (Long) tableItem.getData(Key.RANK);
+                        final String location = NLS.bind(Messages.TmfMarker_LocationRank, rank.toString());
+                        final ITmfTimestamp timestamp = (ITmfTimestamp) tableItem.getData(Key.TIMESTAMP);
+                        final long[] id = new long[1];
+                        ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
+                            @Override
+                            public void run(IProgressMonitor monitor) throws CoreException {
+                                final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
+                                bookmark.setAttribute(IMarker.MESSAGE, message.toString());
+                                bookmark.setAttribute(IMarker.LOCATION, location);
+                                bookmark.setAttribute(ITmfMarker.MARKER_RANK, rank.toString());
+                                bookmark.setAttribute(ITmfMarker.MARKER_TIME, Long.toString(timestamp.toNanos()));
+                                bookmark.setAttribute(ITmfMarker.MARKER_COLOR, dialog.getColorValue().toString());
+                                id[0] = bookmark.getId();
+                            }
+                        }, null);
+                        fBookmarksMap.put(rank, id[0]);
+                        fTable.refresh();
                     } catch (final CoreException e) {
                         displayException(e);
                     }
@@ -2754,19 +3125,50 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     }
 
     /**
-     * Remove a bookmark from this event table.
+     * Add one or more bookmarks to this event table.
      *
-     * @param bookmark
-     *            The bookmark to remove
+     * @param bookmarks
+     *            The bookmarks to add
+     * @since 2.0
      */
-    public void removeBookmark(final IMarker bookmark) {
-        for (final Entry<Long, Long> entry : fBookmarksMap.entries()) {
-            if (entry.getValue().equals(bookmark.getId())) {
-                fBookmarksMap.remove(entry.getKey(), entry.getValue());
-                fTable.refresh();
-                return;
+    public void addBookmark(final IMarker... bookmarks) {
+        for (IMarker bookmark : bookmarks) {
+            /* try location as an integer for backward compatibility */
+            long rank = bookmark.getAttribute(IMarker.LOCATION, -1);
+            if (rank == -1) {
+                String rankString = bookmark.getAttribute(ITmfMarker.MARKER_RANK, (String) null);
+                if (rankString != null) {
+                    try {
+                        rank = Long.parseLong(rankString);
+                    } catch (NumberFormatException e) {
+                        Activator.getDefault().logError("Invalid marker rank", e); //$NON-NLS-1$
+                    }
+                }
+            }
+            if (rank != -1) {
+                fBookmarksMap.put(rank, bookmark.getId());
+            }
+        }
+        fTable.refresh();
+    }
+
+    /**
+     * Remove one or more bookmarks from this event table.
+     *
+     * @param bookmarks
+     *            The bookmarks to remove
+     * @since 2.0
+     */
+    public void removeBookmark(final IMarker... bookmarks) {
+        for (IMarker bookmark : bookmarks) {
+            for (final Entry<Long, Long> entry : fBookmarksMap.entries()) {
+                if (entry.getValue().equals(bookmark.getId())) {
+                    fBookmarksMap.remove(entry.getKey(), entry.getValue());
+                    break;
+                }
             }
         }
+        fTable.refresh();
     }
 
     private void toggleBookmark(final Long rank) {
@@ -2807,14 +3209,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         }
         try {
             fBookmarksMap.clear();
-            for (final IMarker bookmark : bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) {
-                final int location = bookmark.getAttribute(IMarker.LOCATION, -1);
-                if (location != -1) {
-                    final long rank = location;
-                    fBookmarksMap.put(rank, bookmark.getId());
-                }
-            }
-            fTable.refresh();
+            IMarker[] bookmarks = bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO);
+            addBookmark(bookmarks);
         } catch (final CoreException e) {
             displayException(e);
         }
@@ -2822,9 +3218,35 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
     @Override
     public void gotoMarker(final IMarker marker) {
-        final int rank = marker.getAttribute(IMarker.LOCATION, -1);
+        ITmfTimestamp tsBegin = null;
+        ITmfTimestamp tsEnd = null;
+        /* try location as an integer for backward compatibility */
+        long rank = marker.getAttribute(IMarker.LOCATION, -1);
+        if (rank == -1) {
+            String rankString = marker.getAttribute(ITmfMarker.MARKER_RANK, (String) null);
+            try {
+                rank = Long.parseLong(rankString);
+            } catch (NumberFormatException e) {
+                /* ignored */
+            }
+        }
+        try {
+            String timeString = marker.getAttribute(ITmfMarker.MARKER_TIME, (String) null);
+            long time = Long.parseLong(timeString);
+            tsBegin = TmfTimestamp.fromNanos(time);
+            String durationString = marker.getAttribute(ITmfMarker.MARKER_DURATION, (String) null);
+            long duration = Long.parseLong(durationString);
+            tsEnd = TmfTimestamp.fromNanos(time + duration);
+        } catch (NumberFormatException e) {
+            /* ignored */
+        }
+        if (rank == -1 && tsBegin != null) {
+            final ITmfContext context = fTrace.seekEvent(tsBegin);
+            rank = context.getRank();
+            context.dispose();
+        }
         if (rank != -1) {
-            int index = rank;
+            int index = (int) rank;
             if (fTable.getData(Key.FILTER_OBJ) != null) {
                 // +1 for top filter status row
                 index = fCache.getFilteredEventIndex(rank) + 1;
@@ -2835,6 +3257,13 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             fSelectedBeginRank = fSelectedRank;
             fTable.setSelection(index + 1); // +1 for header row
             updateStatusLine(null);
+            if (tsBegin != null) {
+                if (tsEnd != null) {
+                    broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, tsBegin, tsEnd));
+                } else {
+                    broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, tsBegin));
+                }
+            }
         }
     }
 
@@ -2899,72 +3328,167 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     public void selectionRangeUpdated(final TmfSelectionRangeUpdatedSignal signal) {
         if ((signal.getSource() != this) && (fTrace != null) && (!fTable.isDisposed())) {
 
-            /*
-             * Create a request for one event that will be queued after other
-             * ongoing requests. When this request is completed do the work to
-             * select the actual event with the timestamp specified in the
-             * signal. This procedure prevents the method fTrace.getRank() from
-             * interfering and delaying ongoing requests.
-             */
-            final TmfEventRequest subRequest = new TmfEventRequest(ITmfEvent.class,
-                    TmfTimeRange.ETERNITY, 0, 1, ExecutionType.FOREGROUND) {
+            Job timeSelectJob;
+            synchronized (fTimeSelectMutexRule) {
+                timeSelectJob = fTimeSelectJob;
+                if (timeSelectJob != null) {
+                    timeSelectJob.cancel();
+                }
 
-                TmfTimestamp ts = new TmfTimestamp(signal.getBeginTime());
+                /*
+                 * Run in separate thread to not block UI thread for too long.
+                 */
+                timeSelectJob = new Job("Events table selection job") { //$NON-NLS-1$
+                    ITmfTimestamp ts = signal.getBeginTime();
+                    ITmfTimestamp tf = signal.getEndTime();
 
-                @Override
-                public void handleData(final ITmfEvent event) {
-                    super.handleData(event);
-                }
+                    @Override
+                    protected IStatus run(IProgressMonitor monitor) {
+                        if (fTrace == null) {
+                            return Status.OK_STATUS;
+                        }
 
-                @Override
-                public void handleSuccess() {
-                    super.handleSuccess();
-                    if (fTrace == null) {
-                        return;
-                    }
+                        final Pair<Long, Long> selection = getSelectedRanks(monitor);
 
-                    /*
-                     * Verify if the event is within the trace range and adjust
-                     * if necessary
-                     */
-                    ITmfTimestamp timestamp = ts;
-                    if (timestamp.compareTo(fTrace.getStartTime()) == -1) {
-                        timestamp = fTrace.getStartTime();
-                    }
-                    if (timestamp.compareTo(fTrace.getEndTime()) == 1) {
-                        timestamp = fTrace.getEndTime();
+                        if (monitor.isCanceled() || (selection == null)) {
+                            return Status.CANCEL_STATUS;
+                        }
+                        updateDisplayWithSelection(selection.getFirst().longValue(), selection.getSecond().longValue());
+                        return Status.OK_STATUS;
                     }
 
-                    // Get the rank of the selected event in the table
-                    final ITmfContext context = fTrace.seekEvent(timestamp);
-                    final long rank = context.getRank();
-                    context.dispose();
+                    /**
+                     * Verify if the event is within the trace range and adjust if
+                     * necessary.
+                     * @param monitor
+                     *                a progress monitor
+                     * @return A pair of rank representing the selected area
+                     **/
+                    @Nullable
+                    private Pair<Long, Long> getSelectedRanks(IProgressMonitor monitor) {
+
+                        /* Clamp the timestamp value to fit inside of the trace */
+                        ITmfTimestamp timestampBegin = ts;
+                        if (timestampBegin.compareTo(fTrace.getStartTime()) < 0) {
+                            timestampBegin = fTrace.getStartTime();
+                        }
+                        if (timestampBegin.compareTo(fTrace.getEndTime()) > 0) {
+                            timestampBegin = fTrace.getEndTime();
+                        }
 
-                    PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
-                        @Override
-                        public void run() {
-                            // Return if table is disposed
-                            if (fTable.isDisposed()) {
-                                return;
-                            }
+                        ITmfTimestamp timestampEnd = tf;
+                        if (timestampEnd.compareTo(fTrace.getStartTime()) < 0) {
+                            timestampEnd = fTrace.getStartTime();
+                        }
+                        if (timestampEnd.compareTo(fTrace.getEndTime()) > 0) {
+                            timestampEnd = fTrace.getEndTime();
+                        }
 
-                            fSelectedRank = rank;
-                            fSelectedBeginRank = fSelectedRank;
-                            int index = (int) rank;
-                            if (fTable.getData(Key.FILTER_OBJ) != null) {
-                                /* +1 for top filter status row */
-                                index = fCache.getFilteredEventIndex(rank) + 1;
+                        ITmfTimestamp tb;
+                        ITmfTimestamp te;
+                        long rankBegin;
+                        long rankEnd;
+                        ITmfContext contextBegin;
+                        ITmfContext contextEnd;
+                        if (monitor.isCanceled()) {
+                            return null;
+                        }
+
+                        /* Adjust the rank of the selection to the right range */
+                        if (timestampBegin.compareTo(timestampEnd) > 0) {
+                            te = timestampEnd;
+                            contextEnd = fTrace.seekEvent(te);
+                            rankEnd = contextEnd.getRank();
+                            contextEnd.dispose();
+
+                            if (monitor.isCanceled()) {
+                                return null;
                             }
-                            /* +1 for header row */
-                            fTable.setSelection(index + 1);
-                            fRawViewer.selectAndReveal(rank);
-                            updateStatusLine(null);
+                            /*
+                             * To include all events at the begin time, seek at the
+                             * next nanosecond and then use the previous rank
+                             */
+                            tb = timestampBegin.normalize(1, ITmfTimestamp.NANOSECOND_SCALE);
+                            if (tb.compareTo(fTrace.getEndTime()) <= 0) {
+                                contextBegin = fTrace.seekEvent(tb);
+                                rankBegin = contextBegin.getRank();
+                                contextBegin.dispose();
+                            } else {
+                                rankBegin = ITmfContext.UNKNOWN_RANK;
+                            }
+                            rankBegin = (rankBegin == ITmfContext.UNKNOWN_RANK ? fTrace.getNbEvents() : rankBegin) - 1;
+                            /*
+                             * If no events in selection range, select only the next
+                             * event
+                             */
+                            rankBegin = rankBegin >= rankEnd ? rankBegin : rankEnd;
+                        } else {
+                            tb = timestampBegin;
+                            contextBegin = fTrace.seekEvent(tb);
+                            rankBegin = contextBegin.getRank();
+                            contextBegin.dispose();
+                            if (monitor.isCanceled()) {
+                                return null;
+                            }
+                            /*
+                             * To include all events at the end time, seek at the
+                             * next nanosecond and then use the previous rank
+                             */
+                            te = timestampEnd.normalize(1, ITmfTimestamp.NANOSECOND_SCALE);
+                            if (te.compareTo(fTrace.getEndTime()) <= 0) {
+                                contextEnd = fTrace.seekEvent(te);
+                                rankEnd = contextEnd.getRank();
+                                contextEnd.dispose();
+                            } else {
+                                rankEnd = ITmfContext.UNKNOWN_RANK;
+                            }
+                            rankEnd = (rankEnd == ITmfContext.UNKNOWN_RANK ? fTrace.getNbEvents() : rankEnd) - 1;
+                            /*
+                             * If no events in selection range, select only the next
+                             * event
+                             */
+                            rankEnd = rankEnd >= rankBegin ? rankEnd : rankBegin;
                         }
-                    });
-                }
-            };
+                        return new Pair<>(Long.valueOf(rankBegin), Long.valueOf(rankEnd));
+                    }
+
+                    private void updateDisplayWithSelection(final long rankBegin, final long rankEnd) {
+                        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+                            @Override
+                            public void run() {
+                                // Return if table is disposed
+                                if (fTable.isDisposed()) {
+                                    return;
+                                }
 
-            ((ITmfEventProvider) fTrace).sendRequest(subRequest);
+                                fSelectedRank = rankEnd;
+                                long toReveal = fSelectedBeginRank != rankBegin ? rankBegin : rankEnd;
+                                fSelectedBeginRank = rankBegin;
+                                int indexBegin = (int) rankBegin;
+                                int indexEnd = (int) rankEnd;
+
+                                if (fTable.getData(Key.FILTER_OBJ) != null) {
+                                    /* +1 for top filter status row */
+                                    indexBegin = fCache.getFilteredEventIndex(rankBegin) + 1;
+                                    indexEnd = rankEnd == rankBegin ? indexBegin : fCache.getFilteredEventIndex(rankEnd) + 1;
+                                }
+                                /* +1 for header row */
+                                fTable.setSelectionRange(indexBegin + 1, indexEnd + 1);
+                                fRawViewer.selectAndReveal(toReveal);
+                                updateStatusLine(null);
+                            }
+                        });
+                    }
+                };
+                timeSelectJob.setSystem(true);
+                /*
+                 *  Make subsequent jobs not run concurrently so that they are
+                 *  executed in order.
+                 */
+                timeSelectJob.setRule(fTimeSelectMutexRule);
+                timeSelectJob.schedule();
+                fTimeSelectJob = timeSelectJob;
+            }
         }
     }
 
@@ -2999,7 +3523,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      */
     private static final class TmfMarginColumn extends TmfEventTableColumn {
 
-        private static final @NonNull ITmfEventAspect MARGIN_ASPECT = new ITmfEventAspect() {
+        private static final @NonNull ITmfEventAspect<String> MARGIN_ASPECT = new ITmfEventAspect<String>() {
 
             @Override
             public String getName() {
@@ -3027,5 +3551,4 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             super(MARGIN_ASPECT);
         }
     }
-
-}
+}
\ No newline at end of file
This page took 0.087977 seconds and 5 git commands to generate.