ss: Move plugins to Trace Compass namespace
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / viewers / events / TmfEventsTable.java
index ee7a7ec70867fb46797d62aa7c8e0e7e50465df9..6523a7cf4b294acd62b48f16a0cb9ac568960733 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2010, 2011, 2012 Ericsson\r
- *\r
- * All rights reserved. This program and the accompanying materials are\r
- * made available under the terms of the Eclipse Public License v1.0 which\r
- * accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *   Francois Chouinard - Initial API and implementation\r
- *   Patrick Tasse - Factored out from events view\r
- *   Francois Chouinard - Replaced Table by TmfVirtualTable\r
- *   Patrick Tasse - Filter implementation (inspired by www.eclipse.org/mat)\r
- *******************************************************************************/\r
-\r
-package org.eclipse.linuxtools.tmf.ui.viewers.events;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Map.Entry;\r
-import java.util.regex.Pattern;\r
-import java.util.regex.PatternSyntaxException;\r
-\r
-import org.eclipse.core.resources.IFile;\r
-import org.eclipse.core.resources.IMarker;\r
-import org.eclipse.core.resources.IResource;\r
-import org.eclipse.core.runtime.CoreException;\r
-import org.eclipse.core.runtime.IProgressMonitor;\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.eclipse.core.runtime.Status;\r
-import org.eclipse.core.runtime.jobs.Job;\r
-import org.eclipse.jface.action.Action;\r
-import org.eclipse.jface.action.IAction;\r
-import org.eclipse.jface.action.IMenuListener;\r
-import org.eclipse.jface.action.IMenuManager;\r
-import org.eclipse.jface.action.MenuManager;\r
-import org.eclipse.jface.action.Separator;\r
-import org.eclipse.jface.dialogs.InputDialog;\r
-import org.eclipse.jface.dialogs.MessageDialog;\r
-import org.eclipse.jface.resource.FontDescriptor;\r
-import org.eclipse.jface.resource.JFaceResources;\r
-import org.eclipse.jface.resource.LocalResourceManager;\r
-import org.eclipse.jface.window.Window;\r
-import org.eclipse.linuxtools.internal.tmf.ui.Activator;\r
-import org.eclipse.linuxtools.internal.tmf.ui.Messages;\r
-import org.eclipse.linuxtools.tmf.core.component.ITmfDataProvider;\r
-import org.eclipse.linuxtools.tmf.core.component.TmfComponent;\r
-import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;\r
-import org.eclipse.linuxtools.tmf.core.event.ITmfEventField;\r
-import org.eclipse.linuxtools.tmf.core.event.ITmfTimestamp;\r
-import org.eclipse.linuxtools.tmf.core.event.TmfEventField;\r
-import org.eclipse.linuxtools.tmf.core.event.TmfTimestamp;\r
-import org.eclipse.linuxtools.tmf.core.filter.ITmfFilter;\r
-import org.eclipse.linuxtools.tmf.core.filter.model.ITmfFilterTreeNode;\r
-import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterAndNode;\r
-import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterMatchesNode;\r
-import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterNode;\r
-import org.eclipse.linuxtools.tmf.core.request.ITmfDataRequest.ExecutionType;\r
-import org.eclipse.linuxtools.tmf.core.request.TmfDataRequest;\r
-import org.eclipse.linuxtools.tmf.core.signal.TmfExperimentUpdatedSignal;\r
-import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;\r
-import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;\r
-import org.eclipse.linuxtools.tmf.core.signal.TmfTraceUpdatedSignal;\r
-import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;\r
-import org.eclipse.linuxtools.tmf.core.trace.ITmfLocation;\r
-import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;\r
-import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;\r
-import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting;\r
-import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSettingsManager;\r
-import org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener;\r
-import org.eclipse.linuxtools.tmf.ui.views.filter.FilterManager;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.rawviewer.TmfRawEventViewer;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.TmfVirtualTable;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.custom.SashForm;\r
-import org.eclipse.swt.custom.TableEditor;\r
-import org.eclipse.swt.events.FocusAdapter;\r
-import org.eclipse.swt.events.FocusEvent;\r
-import org.eclipse.swt.events.KeyAdapter;\r
-import org.eclipse.swt.events.KeyEvent;\r
-import org.eclipse.swt.events.MouseAdapter;\r
-import org.eclipse.swt.events.MouseEvent;\r
-import org.eclipse.swt.events.SelectionAdapter;\r
-import org.eclipse.swt.events.SelectionEvent;\r
-import org.eclipse.swt.graphics.Color;\r
-import org.eclipse.swt.graphics.Font;\r
-import org.eclipse.swt.graphics.Image;\r
-import org.eclipse.swt.graphics.Point;\r
-import org.eclipse.swt.graphics.Rectangle;\r
-import org.eclipse.swt.layout.FillLayout;\r
-import org.eclipse.swt.layout.GridData;\r
-import org.eclipse.swt.layout.GridLayout;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Display;\r
-import org.eclipse.swt.widgets.Event;\r
-import org.eclipse.swt.widgets.Label;\r
-import org.eclipse.swt.widgets.Listener;\r
-import org.eclipse.swt.widgets.Menu;\r
-import org.eclipse.swt.widgets.MessageBox;\r
-import org.eclipse.swt.widgets.Shell;\r
-import org.eclipse.swt.widgets.TableColumn;\r
-import org.eclipse.swt.widgets.TableItem;\r
-import org.eclipse.swt.widgets.Text;\r
-import org.eclipse.ui.PlatformUI;\r
-import org.eclipse.ui.ide.IGotoMarker;\r
-import org.eclipse.ui.themes.ColorUtil;\r
-\r
-/**\r
- * The generic TMF Events table\r
- *\r
- * This is a view that will list events that are read from a trace.\r
- *\r
- * @version 1.0\r
- * @author Francois Chouinard\r
- * @author Patrick Tasse\r
- */\r
-public class TmfEventsTable extends TmfComponent implements IGotoMarker,\r
-        IColorSettingsListener, ITmfEventsFilterProvider {\r
-\r
-    private static final Image BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(\r
-            "icons/elcl16/bookmark_obj.gif"); //$NON-NLS-1$\r
-    private static final Image SEARCH_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/search.gif"); //$NON-NLS-1$\r
-    private static final Image SEARCH_MATCH_IMAGE = Activator.getDefault().getImageFromPath(\r
-            "icons/elcl16/search_match.gif"); //$NON-NLS-1$\r
-    private static final Image SEARCH_MATCH_BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(\r
-            "icons/elcl16/search_match_bookmark.gif"); //$NON-NLS-1$\r
-    private static final Image FILTER_IMAGE = Activator.getDefault()\r
-            .getImageFromPath("icons/elcl16/filter_items.gif"); //$NON-NLS-1$\r
-    private static final Image STOP_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/stop.gif"); //$NON-NLS-1$\r
-    private static final String SEARCH_HINT = Messages.TmfEventsTable_SearchHint;\r
-    private static final String FILTER_HINT = Messages.TmfEventsTable_FilterHint;\r
-    private static final int MAX_CACHE_SIZE = 1000;\r
-\r
-    /**\r
-     * The events table search/filter keys\r
-     *\r
-     * @version 1.0\r
-     * @author Patrick Tasse\r
-     */\r
-    public interface Key {\r
-        /** Search text */\r
-        String SEARCH_TXT = "$srch_txt"; //$NON-NLS-1$\r
-\r
-        /** Search object */\r
-        String SEARCH_OBJ = "$srch_obj"; //$NON-NLS-1$\r
-\r
-        /** Filter text */\r
-        String FILTER_TXT = "$fltr_txt"; //$NON-NLS-1$\r
-\r
-        /** Filter object */\r
-        String FILTER_OBJ = "$fltr_obj"; //$NON-NLS-1$\r
-\r
-        /** Timestamp*/\r
-        String TIMESTAMP = "$time"; //$NON-NLS-1$\r
-\r
-        /** Rank */\r
-        String RANK = "$rank"; //$NON-NLS-1$\r
-\r
-        /** Field ID */\r
-        String FIELD_ID = "$field_id"; //$NON-NLS-1$\r
-\r
-        /** Bookmark indicator */\r
-        String BOOKMARK = "$bookmark"; //$NON-NLS-1$\r
-    }\r
-\r
-    /**\r
-     * The events table search/filter state\r
-     *\r
-     * @version 1.0\r
-     * @author Patrick Tasse\r
-     */\r
-    public static enum HeaderState {\r
-        /** A search is being run */\r
-        SEARCH,\r
-\r
-        /** A filter is applied */\r
-        FILTER\r
-    }\r
-\r
-    interface Direction {\r
-        int FORWARD = +1;\r
-        int BACKWARD = -1;\r
-    }\r
-\r
-    // ------------------------------------------------------------------------\r
-    // Table data\r
-    // ------------------------------------------------------------------------\r
-\r
-    protected Composite fComposite;\r
-    protected SashForm fSashForm;\r
-    protected TmfVirtualTable fTable;\r
-    protected TmfRawEventViewer fRawViewer;\r
-    protected ITmfTrace fTrace;\r
-    protected boolean fPackDone = false;\r
-    protected HeaderState fHeaderState = HeaderState.SEARCH;\r
-    protected long fSelectedRank = 0;\r
-\r
-    // Filter data\r
-    protected long fFilterMatchCount;\r
-    protected long fFilterCheckCount;\r
-    protected FilterThread fFilterThread;\r
-    protected boolean fFilterThreadResume = false;\r
-    protected final Object fFilterSyncObj = new Object();\r
-    protected SearchThread fSearchThread;\r
-    protected final Object fSearchSyncObj = new Object();\r
-    protected List<ITmfEventsFilterListener> fEventsFilterListeners = new ArrayList<ITmfEventsFilterListener>();\r
-\r
-    // Bookmark map <Rank, MarkerId>\r
-    protected Map<Long, Long> fBookmarksMap = new HashMap<Long, Long>();\r
-    protected IFile fBookmarksFile;\r
-    protected long fPendingGotoRank = -1;\r
-\r
-    // SWT resources\r
-    protected LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());\r
-    protected Color fGrayColor;\r
-    protected Color fGreenColor;\r
-    protected Font fBoldFont;\r
-\r
-    // Table column names\r
-    static private final String[] COLUMN_NAMES = new String[] { Messages.TmfEventsTable_TimestampColumnHeader,\r
-        Messages.TmfEventsTable_SourceColumnHeader, Messages.TmfEventsTable_TypeColumnHeader,\r
-        Messages.TmfEventsTable_ReferenceColumnHeader, Messages.TmfEventsTable_ContentColumnHeader };\r
-\r
-    static private final ColumnData[] COLUMN_DATA = new ColumnData[] { new ColumnData(COLUMN_NAMES[0], 100, SWT.LEFT),\r
-        new ColumnData(COLUMN_NAMES[1], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[2], 100, SWT.LEFT),\r
-        new ColumnData(COLUMN_NAMES[3], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[4], 100, SWT.LEFT) };\r
-\r
-    // Event cache\r
-    private final TmfEventsCache fCache;\r
-    private boolean fCacheUpdateBusy = false;\r
-    private boolean fCacheUpdatePending = false;\r
-    private boolean fCacheUpdateCompleted = false;\r
-    private final Object fCacheUpdateSyncObj = new Object();\r
-\r
-    private boolean fDisposeOnClose;\r
-\r
-    // ------------------------------------------------------------------------\r
-    // Constructor\r
-    // ------------------------------------------------------------------------\r
-\r
-    /**\r
-     * Basic constructor, will use default column data.\r
-     *\r
-     * @param parent\r
-     *            The parent composite UI object\r
-     * @param cacheSize\r
-     *            The size of the event table cache\r
-     */\r
-    public TmfEventsTable(final Composite parent, final int cacheSize) {\r
-        this(parent, cacheSize, COLUMN_DATA);\r
-    }\r
-\r
-    /**\r
-     * Advanced constructor, where we also define which column data to use.\r
-     *\r
-     * @param parent\r
-     *            The parent composite UI object\r
-     * @param cacheSize\r
-     *            The size of the event table cache\r
-     * @param columnData\r
-     *            The column data to use for this table\r
-     */\r
-    public TmfEventsTable(final Composite parent, int cacheSize, final ColumnData[] columnData) {\r
-        super("TmfEventsTable"); //$NON-NLS-1$\r
-\r
-        fComposite = new Composite(parent, SWT.NONE);\r
-        final GridLayout gl = new GridLayout(1, false);\r
-        gl.marginHeight = 0;\r
-        gl.marginWidth = 0;\r
-        gl.verticalSpacing = 0;\r
-        fComposite.setLayout(gl);\r
-\r
-        fSashForm = new SashForm(fComposite, SWT.HORIZONTAL);\r
-        fSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\r
-\r
-        // Create a virtual table\r
-        final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION;\r
-        fTable = new TmfVirtualTable(fSashForm, style);\r
-\r
-        // Set the table layout\r
-        final GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);\r
-        fTable.setLayoutData(layoutData);\r
-\r
-        // Some cosmetic enhancements\r
-        fTable.setHeaderVisible(true);\r
-        fTable.setLinesVisible(true);\r
-\r
-        // Set the columns\r
-        setColumnHeaders(columnData);\r
-\r
-        // Set the default column field ids if this is not a subclass\r
-        if (Arrays.equals(columnData, COLUMN_DATA)) {\r
-            fTable.getColumns()[0].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TIMESTAMP);\r
-            fTable.getColumns()[1].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_SOURCE);\r
-            fTable.getColumns()[2].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TYPE);\r
-            fTable.getColumns()[3].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_REFERENCE);\r
-            fTable.getColumns()[4].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_CONTENT);\r
-        }\r
-\r
-        // Set the frozen row for header row\r
-        fTable.setFrozenRowCount(1);\r
-\r
-        // Create the header row cell editor\r
-        createHeaderEditor();\r
-\r
-        // Handle the table item selection\r
-        fTable.addSelectionListener(new SelectionAdapter() {\r
-            @Override\r
-            public void widgetSelected(final SelectionEvent e) {\r
-                final TableItem[] selection = fTable.getSelection();\r
-                if (selection.length > 0) {\r
-                    final TableItem selectedTableItem = selection[0];\r
-                    if (selectedTableItem != null) {\r
-                        if (selectedTableItem.getData(Key.RANK) instanceof Long) {\r
-                            fSelectedRank = (Long) selectedTableItem.getData(Key.RANK);\r
-                            fRawViewer.selectAndReveal((Long) selectedTableItem.getData(Key.RANK));\r
-                        }\r
-                        if (selectedTableItem.getData(Key.TIMESTAMP) instanceof TmfTimestamp) {\r
-                            final TmfTimestamp ts = (TmfTimestamp) selectedTableItem.getData(Key.TIMESTAMP);\r
-                            broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, ts));\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-        });\r
-\r
-        cacheSize = Math.max(cacheSize, Display.getDefault().getBounds().height / fTable.getItemHeight());\r
-        cacheSize = Math.min(cacheSize, MAX_CACHE_SIZE);\r
-        fCache = new TmfEventsCache(cacheSize, this);\r
-\r
-        // Handle the table item requests\r
-        fTable.addListener(SWT.SetData, new Listener() {\r
-\r
-            @Override\r
-            public void handleEvent(final Event event) {\r
-\r
-                final TableItem item = (TableItem) event.item;\r
-                int index = event.index - 1; // -1 for the header row\r
-\r
-                if (event.index == 0) {\r
-                    setHeaderRowItemData(item);\r
-                    return;\r
-                }\r
-\r
-                if (fTable.getData(Key.FILTER_OBJ) != null) {\r
-                    if ((event.index == 1) || (event.index == (fTable.getItemCount() - 1))) {\r
-                        setFilterStatusRowItemData(item);\r
-                        return;\r
-                    }\r
-                    index = index - 1; // -1 for top filter status row\r
-                }\r
-\r
-                final CachedEvent cachedEvent = fCache.getEvent(index);\r
-                if (cachedEvent != null) {\r
-                    setItemData(item, cachedEvent.event, cachedEvent.rank);\r
-                    return;\r
-                }\r
-\r
-                // Else, fill the cache asynchronously (and off the UI thread)\r
-                event.doit = false;\r
-            }\r
-        });\r
-\r
-        fTable.addMouseListener(new MouseAdapter() {\r
-            @Override\r
-            public void mouseDoubleClick(final MouseEvent event) {\r
-                if (event.button != 1) {\r
-                    return;\r
-                }\r
-                // Identify the selected row\r
-                final Point point = new Point(event.x, event.y);\r
-                final TableItem item = fTable.getItem(point);\r
-                if (item != null) {\r
-                    final Rectangle imageBounds = item.getImageBounds(0);\r
-                    imageBounds.width = BOOKMARK_IMAGE.getBounds().width;\r
-                    if (imageBounds.contains(point)) {\r
-                        final Long rank = (Long) item.getData(Key.RANK);\r
-                        if (rank != null) {\r
-                            toggleBookmark(rank);\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-        });\r
-\r
-        final Listener tooltipListener = new Listener () {\r
-            Shell tooltipShell = null;\r
-            @Override\r
-            public void handleEvent(final Event event) {\r
-                switch (event.type) {\r
-                    case SWT.MouseHover:\r
-                        final TableItem item = fTable.getItem(new Point(event.x, event.y));\r
-                        if (item == null) {\r
-                            return;\r
-                        }\r
-                        final Long rank = (Long) item.getData(Key.RANK);\r
-                        if (rank == null) {\r
-                            return;\r
-                        }\r
-                        final String tooltipText = (String) item.getData(Key.BOOKMARK);\r
-                        final Rectangle bounds = item.getImageBounds(0);\r
-                        bounds.width = BOOKMARK_IMAGE.getBounds().width;\r
-                        if (!bounds.contains(event.x,event.y)) {\r
-                            return;\r
-                        }\r
-                        if ((tooltipShell != null) && !tooltipShell.isDisposed()) {\r
-                            tooltipShell.dispose();\r
-                        }\r
-                        tooltipShell = new Shell(fTable.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);\r
-                        tooltipShell.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));\r
-                        final FillLayout layout = new FillLayout();\r
-                        layout.marginWidth = 2;\r
-                        tooltipShell.setLayout(layout);\r
-                        final Label label = new Label(tooltipShell, SWT.WRAP);\r
-                        String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : ""); //$NON-NLS-1$ //$NON-NLS-2$\r
-                        label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));\r
-                        label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));\r
-                        label.setText(text);\r
-                        label.addListener(SWT.MouseExit, this);\r
-                        label.addListener(SWT.MouseDown, this);\r
-                        label.addListener(SWT.MouseWheel, this);\r
-                        final Point size = tooltipShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);\r
-                        /*\r
-                         * Bug in Linux.  The coordinates of the event have an origin that excludes the table header but\r
-                         * the method toDisplay() expects coordinates relative to an origin that includes the table header.\r
-                         */\r
-                        int y = event.y;\r
-                        if (System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$\r
-                            y += fTable.getHeaderHeight();\r
-                        }\r
-                        Point pt = fTable.toDisplay(event.x, y);\r
-                        pt.x += BOOKMARK_IMAGE.getBounds().width;\r
-                        pt.y += size.y;\r
-                        tooltipShell.setBounds(pt.x, pt.y, size.x, size.y);\r
-                        tooltipShell.setVisible(true);\r
-                        break;\r
-                    case SWT.Dispose:\r
-                    case SWT.KeyDown:\r
-                    case SWT.MouseMove:\r
-                    case SWT.MouseExit:\r
-                    case SWT.MouseDown:\r
-                    case SWT.MouseWheel:\r
-                        if (tooltipShell != null) {\r
-                            tooltipShell.dispose();\r
-                            tooltipShell = null;\r
-                        }\r
-                        break;\r
-                    default:\r
-                        break;\r
-                }\r
-            }\r
-        };\r
-\r
-        fTable.addListener(SWT.MouseHover, tooltipListener);\r
-        fTable.addListener(SWT.Dispose, tooltipListener);\r
-        fTable.addListener(SWT.KeyDown, tooltipListener);\r
-        fTable.addListener(SWT.MouseMove, tooltipListener);\r
-        fTable.addListener(SWT.MouseExit, tooltipListener);\r
-        fTable.addListener(SWT.MouseDown, tooltipListener);\r
-        fTable.addListener(SWT.MouseWheel, tooltipListener);\r
-\r
-        // Create resources\r
-        createResources();\r
-\r
-        ColorSettingsManager.addColorSettingsListener(this);\r
-\r
-        fTable.setItemCount(1); // +1 for header row\r
-\r
-        fRawViewer = new TmfRawEventViewer(fSashForm, SWT.H_SCROLL | SWT.V_SCROLL);\r
-\r
-        fRawViewer.addSelectionListener(new Listener() {\r
-            @Override\r
-            public void handleEvent(final Event e) {\r
-                if (e.data instanceof Long) {\r
-                    final long rank = (Long) e.data;\r
-                    int index = (int) rank;\r
-                    if (fTable.getData(Key.FILTER_OBJ) != null) {\r
-                        index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row\r
-                    }\r
-                    fTable.setSelection(index + 1); // +1 for header row\r
-                    fSelectedRank = rank;\r
-                } else if (e.data instanceof ITmfLocation<?>) {\r
-                    // DOES NOT WORK: rank undefined in context from seekLocation()\r
-                    // ITmfLocation<?> location = (ITmfLocation<?>) e.data;\r
-                    // TmfContext context = fTrace.seekLocation(location);\r
-                    // fTable.setSelection((int) context.getRank());\r
-                    return;\r
-                } else {\r
-                    return;\r
-                }\r
-                final TableItem[] selection = fTable.getSelection();\r
-                if ((selection != null) && (selection.length > 0)) {\r
-                    final TmfTimestamp ts = (TmfTimestamp) fTable.getSelection()[0].getData(Key.TIMESTAMP);\r
-                    if (ts != null) {\r
-                        broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, ts));\r
-                    }\r
-                }\r
-            }\r
-        });\r
-\r
-        fSashForm.setWeights(new int[] { 1, 1 });\r
-        fRawViewer.setVisible(false);\r
-\r
-        createPopupMenu();\r
-    }\r
-\r
-    protected void createPopupMenu() {\r
-        final IAction showTableAction = new Action(Messages.TmfEventsTable_ShowTableActionText) {\r
-            @Override\r
-            public void run() {\r
-                fTable.setVisible(true);\r
-                fSashForm.layout();\r
-            }\r
-        };\r
-\r
-        final IAction hideTableAction = new Action(Messages.TmfEventsTable_HideTableActionText) {\r
-            @Override\r
-            public void run() {\r
-                fTable.setVisible(false);\r
-                fSashForm.layout();\r
-            }\r
-        };\r
-\r
-        final IAction showRawAction = new Action(Messages.TmfEventsTable_ShowRawActionText) {\r
-            @Override\r
-            public void run() {\r
-                fRawViewer.setVisible(true);\r
-                fSashForm.layout();\r
-                final int index = fTable.getSelectionIndex();\r
-                if (index >= +1) {\r
-                    fRawViewer.selectAndReveal(index - 1);\r
-                }\r
-            }\r
-        };\r
-\r
-        final IAction hideRawAction = new Action(Messages.TmfEventsTable_HideRawActionText) {\r
-            @Override\r
-            public void run() {\r
-                fRawViewer.setVisible(false);\r
-                fSashForm.layout();\r
-            }\r
-        };\r
-\r
-        final IAction showSearchBarAction = new Action(Messages.TmfEventsTable_ShowSearchBarActionText) {\r
-            @Override\r
-            public void run() {\r
-                fHeaderState = HeaderState.SEARCH;\r
-                fTable.refresh();\r
-            }\r
-        };\r
-\r
-        final IAction showFilterBarAction = new Action(Messages.TmfEventsTable_ShowFilterBarActionText) {\r
-            @Override\r
-            public void run() {\r
-                fHeaderState = HeaderState.FILTER;\r
-                fTable.refresh();\r
-            }\r
-        };\r
-\r
-        final IAction clearFiltersAction = new Action(Messages.TmfEventsTable_ClearFiltersActionText) {\r
-            @Override\r
-            public void run() {\r
-                clearFilters();\r
-            }\r
-        };\r
-\r
-        class ToggleBookmarkAction extends Action {\r
-            long fRank;\r
-\r
-            public ToggleBookmarkAction(final String text, final long rank) {\r
-                super(text);\r
-                fRank = rank;\r
-            }\r
-\r
-            @Override\r
-            public void run() {\r
-                toggleBookmark(fRank);\r
-            }\r
-        }\r
-\r
-        final MenuManager tablePopupMenu = new MenuManager();\r
-        tablePopupMenu.setRemoveAllWhenShown(true);\r
-        tablePopupMenu.addMenuListener(new IMenuListener() {\r
-            @Override\r
-            public void menuAboutToShow(final IMenuManager manager) {\r
-                if (fTable.getSelectionIndex() == 0) {\r
-                    // Right-click on header row\r
-                    if (fHeaderState == HeaderState.FILTER) {\r
-                        tablePopupMenu.add(showSearchBarAction);\r
-                    } else {\r
-                        tablePopupMenu.add(showFilterBarAction);\r
-                    }\r
-                    return;\r
-                }\r
-                final Point point = fTable.toControl(Display.getDefault().getCursorLocation());\r
-                final TableItem item = fTable.getSelection().length > 0 ? fTable.getSelection()[0] : null;\r
-                if (item != null) {\r
-                    final Rectangle imageBounds = item.getImageBounds(0);\r
-                    imageBounds.width = BOOKMARK_IMAGE.getBounds().width;\r
-                    if (point.x <= (imageBounds.x + imageBounds.width)) {\r
-                        // Right-click on left margin\r
-                        final Long rank = (Long) item.getData(Key.RANK);\r
-                        if ((rank != null) && (fBookmarksFile != null)) {\r
-                            if (fBookmarksMap.containsKey(rank)) {\r
-                                tablePopupMenu.add(new ToggleBookmarkAction(\r
-                                        Messages.TmfEventsTable_RemoveBookmarkActionText, rank));\r
-                            } else {\r
-                                tablePopupMenu.add(new ToggleBookmarkAction(\r
-                                        Messages.TmfEventsTable_AddBookmarkActionText, rank));\r
-                            }\r
-                        }\r
-                        return;\r
-                    }\r
-                }\r
-                // Right-click on table\r
-                if (fTable.isVisible() && fRawViewer.isVisible()) {\r
-                    tablePopupMenu.add(hideTableAction);\r
-                    tablePopupMenu.add(hideRawAction);\r
-                } else if (!fTable.isVisible()) {\r
-                    tablePopupMenu.add(showTableAction);\r
-                } else if (!fRawViewer.isVisible()) {\r
-                    tablePopupMenu.add(showRawAction);\r
-                }\r
-                tablePopupMenu.add(new Separator());\r
-                tablePopupMenu.add(clearFiltersAction);\r
-                final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();\r
-                if (savedFilters.length > 0) {\r
-                    final MenuManager subMenu = new MenuManager(Messages.TmfEventsTable_ApplyPresetFilterMenuName);\r
-                    for (final ITmfFilterTreeNode node : savedFilters) {\r
-                        if (node instanceof TmfFilterNode) {\r
-                            final TmfFilterNode filter = (TmfFilterNode) node;\r
-                            subMenu.add(new Action(filter.getFilterName()) {\r
-                                @Override\r
-                                public void run() {\r
-                                    applyFilter(filter);\r
-                                }\r
-                            });\r
-                        }\r
-                    }\r
-                    tablePopupMenu.add(subMenu);\r
-                }\r
-                appendToTablePopupMenu(tablePopupMenu, item);\r
-            }\r
-        });\r
-\r
-        final MenuManager rawViewerPopupMenu = new MenuManager();\r
-        rawViewerPopupMenu.setRemoveAllWhenShown(true);\r
-        rawViewerPopupMenu.addMenuListener(new IMenuListener() {\r
-            @Override\r
-            public void menuAboutToShow(final IMenuManager manager) {\r
-                if (fTable.isVisible() && fRawViewer.isVisible()) {\r
-                    rawViewerPopupMenu.add(hideTableAction);\r
-                    rawViewerPopupMenu.add(hideRawAction);\r
-                } else if (!fTable.isVisible()) {\r
-                    rawViewerPopupMenu.add(showTableAction);\r
-                } else if (!fRawViewer.isVisible()) {\r
-                    rawViewerPopupMenu.add(showRawAction);\r
-                }\r
-                appendToRawPopupMenu(tablePopupMenu);\r
-            }\r
-        });\r
-\r
-        Menu menu = tablePopupMenu.createContextMenu(fTable);\r
-        fTable.setMenu(menu);\r
-\r
-        menu = rawViewerPopupMenu.createContextMenu(fRawViewer);\r
-        fRawViewer.setMenu(menu);\r
-    }\r
-\r
-    protected void appendToTablePopupMenu(final MenuManager tablePopupMenu, final TableItem selectedItem) {\r
-        // override to append more actions\r
-    }\r
-\r
-    protected void appendToRawPopupMenu(final MenuManager rawViewerPopupMenu) {\r
-        // override to append more actions\r
-    }\r
-\r
-    @Override\r
-    public void dispose() {\r
-        stopSearchThread();\r
-        stopFilterThread();\r
-        ColorSettingsManager.removeColorSettingsListener(this);\r
-        fComposite.dispose();\r
-        if ((fTrace != null) && fDisposeOnClose) {\r
-            fTrace.dispose();\r
-        }\r
-        fResourceManager.dispose();\r
-        super.dispose();\r
-    }\r
-\r
-    /**\r
-     * Assign a layout data object to this view.\r
-     *\r
-     * @param layoutData\r
-     *            The layout data to assign\r
-     */\r
-    public void setLayoutData(final Object layoutData) {\r
-        fComposite.setLayoutData(layoutData);\r
-    }\r
-\r
-    /**\r
-     * Get the virtual table contained in this event table.\r
-     *\r
-     * @return The TMF virtual table\r
-     */\r
-    public TmfVirtualTable getTable() {\r
-        return fTable;\r
-    }\r
-\r
-    /**\r
-     * @param columnData\r
-     *\r
-     * FIXME: Add support for column selection\r
-     */\r
-    protected void setColumnHeaders(final ColumnData[] columnData) {\r
-        fTable.setColumnHeaders(columnData);\r
-    }\r
-\r
-    protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {\r
-        final ITmfEventField[] fields = extractItemFields(event);\r
-        final String[] content = new String[fields.length];\r
-        for (int i = 0; i < fields.length; i++) {\r
-            content[i] = fields[i].getValue() != null ? fields[i].getValue().toString() : ""; //$NON-NLS-1$\r
-        }\r
-        item.setText(content);\r
-        item.setData(Key.TIMESTAMP, new TmfTimestamp(event.getTimestamp()));\r
-        item.setData(Key.RANK, rank);\r
-\r
-        boolean bookmark = false;\r
-        final Long markerId = fBookmarksMap.get(rank);\r
-        if (markerId != null) {\r
-            bookmark = true;\r
-            try {\r
-                final IMarker marker = fBookmarksFile.findMarker(markerId);\r
-                item.setData(Key.BOOKMARK, marker.getAttribute(IMarker.MESSAGE));\r
-            } catch (final CoreException e) {\r
-                displayException(e);\r
-            }\r
-        } else {\r
-            item.setData(Key.BOOKMARK, null);\r
-        }\r
-\r
-        boolean searchMatch = false;\r
-        boolean searchNoMatch = false;\r
-        final ITmfFilter searchFilter = (ITmfFilter) fTable.getData(Key.SEARCH_OBJ);\r
-        if (searchFilter != null) {\r
-            if (searchFilter.matches(event)) {\r
-                searchMatch = true;\r
-            } else {\r
-                searchNoMatch = true;\r
-            }\r
-        }\r
-\r
-        final ColorSetting colorSetting = ColorSettingsManager.getColorSetting(event);\r
-        if (searchNoMatch) {\r
-            item.setForeground(colorSetting.getDimmedForegroundColor());\r
-            item.setBackground(colorSetting.getDimmedBackgroundColor());\r
-        } else {\r
-            item.setForeground(colorSetting.getForegroundColor());\r
-            item.setBackground(colorSetting.getBackgroundColor());\r
-        }\r
-\r
-        if (searchMatch) {\r
-            if (bookmark) {\r
-                item.setImage(SEARCH_MATCH_BOOKMARK_IMAGE);\r
-            } else {\r
-                item.setImage(SEARCH_MATCH_IMAGE);\r
-            }\r
-        } else if (bookmark) {\r
-            item.setImage(BOOKMARK_IMAGE);\r
-        } else {\r
-            item.setImage((Image) null);\r
-        }\r
-    }\r
-\r
-    protected void setHeaderRowItemData(final TableItem item) {\r
-        String txtKey = null;\r
-        if (fHeaderState == HeaderState.SEARCH) {\r
-            item.setImage(SEARCH_IMAGE);\r
-            txtKey = Key.SEARCH_TXT;\r
-        } else if (fHeaderState == HeaderState.FILTER) {\r
-            item.setImage(FILTER_IMAGE);\r
-            txtKey = Key.FILTER_TXT;\r
-        }\r
-        item.setForeground(fGrayColor);\r
-        for (int i = 0; i < fTable.getColumns().length; i++) {\r
-            final TableColumn column = fTable.getColumns()[i];\r
-            final String filter = (String) column.getData(txtKey);\r
-            if (filter == null) {\r
-                if (fHeaderState == HeaderState.SEARCH) {\r
-                    item.setText(i, SEARCH_HINT);\r
-                } else if (fHeaderState == HeaderState.FILTER) {\r
-                    item.setText(i, FILTER_HINT);\r
-                }\r
-                item.setForeground(i, fGrayColor);\r
-                item.setFont(i, fTable.getFont());\r
-            } else {\r
-                item.setText(i, filter);\r
-                item.setForeground(i, fGreenColor);\r
-                item.setFont(i, fBoldFont);\r
-            }\r
-        }\r
-    }\r
-\r
-    protected void setFilterStatusRowItemData(final TableItem item) {\r
-        for (int i = 0; i < fTable.getColumns().length; i++) {\r
-            if (i == 0) {\r
-                if ((fTrace == null) || (fFilterCheckCount == fTrace.getNbEvents())) {\r
-                    item.setImage(FILTER_IMAGE);\r
-                } else {\r
-                    item.setImage(STOP_IMAGE);\r
-                }\r
-                item.setText(0, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$\r
-            } else {\r
-                item.setText(i, ""); //$NON-NLS-1$\r
-            }\r
-        }\r
-        item.setData(Key.TIMESTAMP, null);\r
-        item.setData(Key.RANK, null);\r
-        item.setForeground(null);\r
-        item.setBackground(null);\r
-    }\r
-\r
-    protected void createHeaderEditor() {\r
-        final TableEditor tableEditor = fTable.createTableEditor();\r
-        tableEditor.horizontalAlignment = SWT.LEFT;\r
-        tableEditor.verticalAlignment = SWT.CENTER;\r
-        tableEditor.grabHorizontal = true;\r
-        tableEditor.minimumWidth = 50;\r
-\r
-        // Handle the header row selection\r
-        fTable.addMouseListener(new MouseAdapter() {\r
-            int columnIndex;\r
-            TableColumn column;\r
-            TableItem item;\r
-\r
-            @Override\r
-            public void mouseDown(final MouseEvent event) {\r
-                if (event.button != 1) {\r
-                    return;\r
-                }\r
-                // Identify the selected row\r
-                final Point point = new Point(event.x, event.y);\r
-                item = fTable.getItem(point);\r
-\r
-                // Header row selected\r
-                if ((item != null) && (fTable.indexOf(item) == 0)) {\r
-\r
-                    // Icon selected\r
-                    if (item.getImageBounds(0).contains(point)) {\r
-                        if (fHeaderState == HeaderState.SEARCH) {\r
-                            fHeaderState = HeaderState.FILTER;\r
-                        } else if (fHeaderState == HeaderState.FILTER) {\r
-                            fHeaderState = HeaderState.SEARCH;\r
-                        }\r
-                        fTable.refresh();\r
-                        return;\r
-                    }\r
-\r
-                    // Identify the selected column\r
-                    columnIndex = -1;\r
-                    for (int i = 0; i < fTable.getColumns().length; i++) {\r
-                        final Rectangle rect = item.getBounds(i);\r
-                        if (rect.contains(point)) {\r
-                            columnIndex = i;\r
-                            break;\r
-                        }\r
-                    }\r
-\r
-                    if (columnIndex == -1) {\r
-                        return;\r
-                    }\r
-\r
-                    column = fTable.getColumns()[columnIndex];\r
-\r
-                    String txtKey = null;\r
-                    if (fHeaderState == HeaderState.SEARCH) {\r
-                        txtKey = Key.SEARCH_TXT;\r
-                    } else if (fHeaderState == HeaderState.FILTER) {\r
-                        txtKey = Key.FILTER_TXT;\r
-                    }\r
-\r
-                    // The control that will be the editor must be a child of the Table\r
-                    final Text newEditor = (Text) fTable.createTableEditorControl(Text.class);\r
-                    final String headerString = (String) column.getData(txtKey);\r
-                    if (headerString != null) {\r
-                        newEditor.setText(headerString);\r
-                    }\r
-                    newEditor.addFocusListener(new FocusAdapter() {\r
-                        @Override\r
-                        public void focusLost(final FocusEvent e) {\r
-                            final boolean changed = updateHeader(newEditor.getText());\r
-                            if (changed) {\r
-                                applyHeader();\r
-                            }\r
-                        }\r
-                    });\r
-                    newEditor.addKeyListener(new KeyAdapter() {\r
-                        @Override\r
-                        public void keyPressed(final KeyEvent e) {\r
-                            if (e.character == SWT.CR) {\r
-                                updateHeader(newEditor.getText());\r
-                                applyHeader();\r
-                            } else if (e.character == SWT.ESC) {\r
-                                tableEditor.getEditor().dispose();\r
-                            }\r
-                        }\r
-                    });\r
-                    newEditor.selectAll();\r
-                    newEditor.setFocus();\r
-                    tableEditor.setEditor(newEditor, item, columnIndex);\r
-                }\r
-            }\r
-\r
-            /*\r
-             * returns true is value was changed\r
-             */\r
-            private boolean updateHeader(final String text) {\r
-                String objKey = null;\r
-                String txtKey = null;\r
-                if (fHeaderState == HeaderState.SEARCH) {\r
-                    objKey = Key.SEARCH_OBJ;\r
-                    txtKey = Key.SEARCH_TXT;\r
-                } else if (fHeaderState == HeaderState.FILTER) {\r
-                    objKey = Key.FILTER_OBJ;\r
-                    txtKey = Key.FILTER_TXT;\r
-                }\r
-                if (text.trim().length() > 0) {\r
-                    try {\r
-                        final String regex = TmfFilterMatchesNode.regexFix(text);\r
-                        Pattern.compile(regex);\r
-                        if (regex.equals(column.getData(txtKey))) {\r
-                            tableEditor.getEditor().dispose();\r
-                            return false;\r
-                        }\r
-                        final TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null);\r
-                        String fieldId = (String) column.getData(Key.FIELD_ID);\r
-                        if (fieldId == null) {\r
-                            fieldId = column.getText();\r
-                        }\r
-                        filter.setField(fieldId);\r
-                        filter.setRegex(regex);\r
-                        column.setData(objKey, filter);\r
-                        column.setData(txtKey, regex);\r
-                    } catch (final PatternSyntaxException ex) {\r
-                        tableEditor.getEditor().dispose();\r
-                        MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),\r
-                                ex.getDescription(), ex.getMessage());\r
-                        return false;\r
-                    }\r
-                } else {\r
-                    if (column.getData(txtKey) == null) {\r
-                        tableEditor.getEditor().dispose();\r
-                        return false;\r
-                    }\r
-                    column.setData(objKey, null);\r
-                    column.setData(txtKey, null);\r
-                }\r
-                return true;\r
-            }\r
-\r
-            private void applyHeader() {\r
-                if (fHeaderState == HeaderState.SEARCH) {\r
-                    stopSearchThread();\r
-                    final TmfFilterAndNode filter = new TmfFilterAndNode(null);\r
-                    for (final TableColumn column : fTable.getColumns()) {\r
-                        final Object filterObj = column.getData(Key.SEARCH_OBJ);\r
-                        if (filterObj instanceof ITmfFilterTreeNode) {\r
-                            filter.addChild((ITmfFilterTreeNode) filterObj);\r
-                        }\r
-                    }\r
-                    if (filter.getChildrenCount() > 0) {\r
-                        fTable.setData(Key.SEARCH_OBJ, filter);\r
-                        fTable.refresh();\r
-                        searchNext();\r
-                        fireSearchApplied(filter);\r
-                    } else {\r
-                        fTable.setData(Key.SEARCH_OBJ, null);\r
-                        fTable.refresh();\r
-                        fireSearchApplied(null);\r
-                    }\r
-                } else if (fHeaderState == HeaderState.FILTER) {\r
-                    final TmfFilterAndNode filter = new TmfFilterAndNode(null);\r
-                    for (final TableColumn column : fTable.getColumns()) {\r
-                        final Object filterObj = column.getData(Key.FILTER_OBJ);\r
-                        if (filterObj instanceof ITmfFilterTreeNode) {\r
-                            filter.addChild((ITmfFilterTreeNode) filterObj);\r
-                        }\r
-                    }\r
-                    if (filter.getChildrenCount() > 0) {\r
-                        applyFilter(filter);\r
-                    } else {\r
-                        clearFilters();\r
-                    }\r
-                }\r
-\r
-                tableEditor.getEditor().dispose();\r
-            }\r
-        });\r
-\r
-        fTable.addKeyListener(new KeyAdapter() {\r
-            @Override\r
-            public void keyPressed(final KeyEvent e) {\r
-                e.doit = false;\r
-                if (e.character == SWT.ESC) {\r
-                    stopFilterThread();\r
-                    stopSearchThread();\r
-                    fTable.refresh();\r
-                } else if (e.character == SWT.DEL) {\r
-                    if (fHeaderState == HeaderState.SEARCH) {\r
-                        stopSearchThread();\r
-                        for (final TableColumn column : fTable.getColumns()) {\r
-                            column.setData(Key.SEARCH_OBJ, null);\r
-                            column.setData(Key.SEARCH_TXT, null);\r
-                        }\r
-                        fTable.setData(Key.SEARCH_OBJ, null);\r
-                        fTable.refresh();\r
-                        fireSearchApplied(null);\r
-                    } else if (fHeaderState == HeaderState.FILTER) {\r
-                        clearFilters();\r
-                    }\r
-                } else if (e.character == SWT.CR) {\r
-                    if ((e.stateMask & SWT.SHIFT) == 0) {\r
-                        searchNext();\r
-                    } else {\r
-                        searchPrevious();\r
-                    }\r
-                }\r
-            }\r
-        });\r
-    }\r
-\r
-    protected void fireFilterApplied(final ITmfFilter filter) {\r
-        for (final ITmfEventsFilterListener listener : fEventsFilterListeners) {\r
-            listener.filterApplied(filter, fTrace);\r
-        }\r
-    }\r
-\r
-    protected void fireSearchApplied(final ITmfFilter filter) {\r
-        for (final ITmfEventsFilterListener listener : fEventsFilterListeners) {\r
-            listener.searchApplied(filter, fTrace);\r
-        }\r
-    }\r
-\r
-    protected void startFilterThread() {\r
-        synchronized (fFilterSyncObj) {\r
-            final ITmfFilterTreeNode filter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);\r
-            if (fFilterThread == null || fFilterThread.filter != filter) {\r
-                if (fFilterThread != null) {\r
-                    fFilterThread.cancel();\r
-                    fFilterThreadResume = false;\r
-                }\r
-                fFilterThread = new FilterThread(filter);\r
-                fFilterThread.start();\r
-            } else {\r
-                fFilterThreadResume = true;\r
-            }\r
-        }\r
-    }\r
-\r
-    protected void stopFilterThread() {\r
-        synchronized (fFilterSyncObj) {\r
-            if (fFilterThread != null) {\r
-                fFilterThread.cancel();\r
-                fFilterThread = null;\r
-                fFilterThreadResume = false;\r
-            }\r
-        }\r
-    }\r
-\r
-    /**\r
-     * @since 1.1\r
-     */\r
-    protected void applyFilter(ITmfFilter filter) {\r
-       stopFilterThread();\r
-       stopSearchThread();\r
-        fFilterMatchCount = 0;\r
-        fFilterCheckCount = 0;\r
-        fCache.applyFilter(filter);\r
-        fTable.clearAll();\r
-        fTable.setData(Key.FILTER_OBJ, filter);\r
-        fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows\r
-        startFilterThread();\r
-        fireFilterApplied(filter);\r
-    }\r
-\r
-    protected void clearFilters() {\r
-        if (fTable.getData(Key.FILTER_OBJ) == null) {\r
-            return;\r
-        }\r
-        stopFilterThread();\r
-        stopSearchThread();\r
-        fCache.clearFilter();\r
-        fTable.clearAll();\r
-        for (final TableColumn column : fTable.getColumns()) {\r
-            column.setData(Key.FILTER_OBJ, null);\r
-            column.setData(Key.FILTER_TXT, null);\r
-        }\r
-        fTable.setData(Key.FILTER_OBJ, null);\r
-        if (fTrace != null) {\r
-            fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row\r
-        } else {\r
-            fTable.setItemCount(1); // +1 for header row\r
-        }\r
-        fFilterMatchCount = 0;\r
-        fFilterCheckCount = 0;\r
-        if (fSelectedRank >= 0) {\r
-            fTable.setSelection((int) fSelectedRank + 1); // +1 for header row\r
-        } else {\r
-            fTable.setSelection(0);\r
-        }\r
-        fireFilterApplied(null);\r
-    }\r
-\r
-    protected class FilterThread extends Thread {\r
-        private final ITmfFilterTreeNode filter;\r
-        private TmfDataRequest request;\r
-        private boolean refreshBusy = false;\r
-        private boolean refreshPending = false;\r
-        private final Object syncObj = new Object();\r
-\r
-        public FilterThread(final ITmfFilterTreeNode filter) {\r
-            super("Filter Thread"); //$NON-NLS-1$\r
-            this.filter = filter;\r
-        }\r
-\r
-        @Override\r
-        public void run() {\r
-            if (fTrace == null) {\r
-                return;\r
-            }\r
-            final int nbRequested = (int) (fTrace.getNbEvents() - fFilterCheckCount);\r
-            if (nbRequested <= 0) {\r
-                return;\r
-            }\r
-            request = new TmfDataRequest(ITmfEvent.class, (int) fFilterCheckCount,\r
-                    nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {\r
-                @Override\r
-                public void handleData(final ITmfEvent event) {\r
-                    super.handleData(event);\r
-                    if (request.isCancelled()) {\r
-                        return;\r
-                    }\r
-                    if (filter.matches(event)) {\r
-                        final long rank = fFilterCheckCount;\r
-                        final int index = (int) fFilterMatchCount;\r
-                        fFilterMatchCount++;\r
-                        fCache.storeEvent(event.clone(), rank, index);\r
-                        refreshTable();\r
-                    } else if ((fFilterCheckCount % 100) == 0) {\r
-                        refreshTable();\r
-                    }\r
-                    fFilterCheckCount++;\r
-                }\r
-            };\r
-            ((ITmfDataProvider) fTrace).sendRequest(request);\r
-            try {\r
-                request.waitForCompletion();\r
-            } catch (final InterruptedException e) {\r
-            }\r
-            refreshTable();\r
-            synchronized (fFilterSyncObj) {\r
-                fFilterThread = null;\r
-                if (fFilterThreadResume) {\r
-                    fFilterThreadResume = false;\r
-                    fFilterThread = new FilterThread(filter);\r
-                    fFilterThread.start();\r
-                }\r
-            }\r
-        }\r
-\r
-        public void refreshTable() {\r
-            synchronized (syncObj) {\r
-                if (refreshBusy) {\r
-                    refreshPending = true;\r
-                    return;\r
-                }\r
-                refreshBusy = true;\r
-            }\r
-            Display.getDefault().asyncExec(new Runnable() {\r
-                @Override\r
-                public void run() {\r
-                    if (request.isCancelled()) {\r
-                        return;\r
-                    }\r
-                    if (fTable.isDisposed()) {\r
-                        return;\r
-                    }\r
-                    fTable.setItemCount((int) fFilterMatchCount + 3); // +1 for header row, +2 for top and bottom filter status rows\r
-                    fTable.refresh();\r
-                    synchronized (syncObj) {\r
-                        refreshBusy = false;\r
-                        if (refreshPending) {\r
-                            refreshPending = false;\r
-                            refreshTable();\r
-                        }\r
-                    }\r
-                }\r
-            });\r
-        }\r
-\r
-        public void cancel() {\r
-            if (request != null) {\r
-                request.cancel();\r
-            }\r
-        }\r
-    }\r
-\r
-    protected void searchNext() {\r
-        synchronized (fSearchSyncObj) {\r
-            if (fSearchThread != null) {\r
-                return;\r
-            }\r
-            final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);\r
-            if (searchFilter == null) {\r
-                return;\r
-            }\r
-            final int selectionIndex = fTable.getSelectionIndex();\r
-            int startIndex;\r
-            if (selectionIndex > 0) {\r
-                startIndex = selectionIndex; // -1 for header row, +1 for next event\r
-            } else {\r
-                // header row is selected, start at top event\r
-                startIndex = Math.max(0, fTable.getTopIndex() - 1); // -1 for header row\r
-            }\r
-            final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);\r
-            if (eventFilter != null)\r
-             {\r
-                startIndex = Math.max(0, startIndex - 1); // -1 for top filter status row\r
-            }\r
-            fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.FORWARD);\r
-            fSearchThread.schedule();\r
-        }\r
-    }\r
-\r
-    protected void searchPrevious() {\r
-        synchronized (fSearchSyncObj) {\r
-            if (fSearchThread != null) {\r
-                return;\r
-            }\r
-            final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);\r
-            if (searchFilter == null) {\r
-                return;\r
-            }\r
-            final int selectionIndex = fTable.getSelectionIndex();\r
-            int startIndex;\r
-            if (selectionIndex > 0) {\r
-                startIndex = selectionIndex - 2; // -1 for header row, -1 for previous event\r
-            } else {\r
-                // header row is selected, start at precedent of top event\r
-                startIndex = fTable.getTopIndex() - 2; // -1 for header row, -1 for previous event\r
-            }\r
-            final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);\r
-            if (eventFilter != null)\r
-             {\r
-                startIndex = startIndex - 1; // -1 for top filter status row\r
-            }\r
-            fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.BACKWARD);\r
-            fSearchThread.schedule();\r
-        }\r
-    }\r
-\r
-    protected void stopSearchThread() {\r
-        fPendingGotoRank = -1;\r
-        synchronized (fSearchSyncObj) {\r
-            if (fSearchThread != null) {\r
-                fSearchThread.cancel();\r
-                fSearchThread = null;\r
-            }\r
-        }\r
-    }\r
-\r
-    protected class SearchThread extends Job {\r
-        protected ITmfFilterTreeNode searchFilter;\r
-        protected ITmfFilterTreeNode eventFilter;\r
-        protected int startIndex;\r
-        protected int direction;\r
-        protected long rank;\r
-        protected long foundRank = -1;\r
-        protected TmfDataRequest request;\r
-\r
-        public SearchThread(final ITmfFilterTreeNode searchFilter, final ITmfFilterTreeNode eventFilter, final int startIndex,\r
-                final long currentRank, final int direction) {\r
-            super(Messages.TmfEventsTable_SearchingJobName);\r
-            this.searchFilter = searchFilter;\r
-            this.eventFilter = eventFilter;\r
-            this.startIndex = startIndex;\r
-            this.rank = currentRank;\r
-            this.direction = direction;\r
-        }\r
-\r
-        @Override\r
-        protected IStatus run(final IProgressMonitor monitor) {\r
-            if (fTrace == null) {\r
-                return Status.OK_STATUS;\r
-            }\r
-            final Display display = Display.getDefault();\r
-            if (startIndex < 0) {\r
-                rank = (int) fTrace.getNbEvents() - 1;\r
-            } else if (startIndex >= (fTable.getItemCount() - (eventFilter == null ? 1 : 3))) { // -1 for header row, -2 for top and bottom filter status rows\r
-                rank = 0;\r
-            } else {\r
-                int idx = startIndex;\r
-                while (foundRank == -1) {\r
-                    final CachedEvent event = fCache.peekEvent(idx);\r
-                    if (event == null) {\r
-                        break;\r
-                    }\r
-                    rank = event.rank;\r
-                    if (searchFilter.matches(event.event) && ((eventFilter == null) || eventFilter.matches(event.event))) {\r
-                        foundRank = event.rank;\r
-                        break;\r
-                    }\r
-                    if (direction == Direction.FORWARD) {\r
-                        idx++;\r
-                    } else {\r
-                        idx--;\r
-                    }\r
-                }\r
-                if (foundRank == -1) {\r
-                    if (direction == Direction.FORWARD) {\r
-                        rank++;\r
-                        if (rank > (fTrace.getNbEvents() - 1)) {\r
-                            rank = 0;\r
-                        }\r
-                    } else {\r
-                        rank--;\r
-                        if (rank < 0) {\r
-                            rank = (int) fTrace.getNbEvents() - 1;\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-            final int startRank = (int) rank;\r
-            boolean wrapped = false;\r
-            while (!monitor.isCanceled() && (foundRank == -1) && (fTrace != null)) {\r
-                int nbRequested = (direction == Direction.FORWARD ? Integer.MAX_VALUE : Math.min((int) rank + 1, fTrace.getCacheSize()));\r
-                if (direction == Direction.BACKWARD) {\r
-                    rank = Math.max(0, rank - fTrace.getCacheSize() + 1);\r
-                }\r
-                request = new TmfDataRequest(ITmfEvent.class, (int) rank, nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {\r
-                    long currentRank = rank;\r
-\r
-                    @Override\r
-                    public void handleData(final ITmfEvent event) {\r
-                        super.handleData(event);\r
-                        if (searchFilter.matches(event) && ((eventFilter == null) || eventFilter.matches(event))) {\r
-                            foundRank = currentRank;\r
-                            if (direction == Direction.FORWARD) {\r
-                                done();\r
-                                return;\r
-                            }\r
-                        }\r
-                        currentRank++;\r
-                    }\r
-                };\r
-                ((ITmfDataProvider) fTrace).sendRequest(request);\r
-                try {\r
-                    request.waitForCompletion();\r
-                    if (request.isCancelled()) {\r
-                        return Status.OK_STATUS;\r
-                    }\r
-                } catch (final InterruptedException e) {\r
-                    synchronized (fSearchSyncObj) {\r
-                        fSearchThread = null;\r
-                    }\r
-                    return Status.OK_STATUS;\r
-                }\r
-                if (foundRank == -1) {\r
-                    if (direction == Direction.FORWARD) {\r
-                        if (rank == 0) {\r
-                            synchronized (fSearchSyncObj) {\r
-                                fSearchThread = null;\r
-                            }\r
-                            return Status.OK_STATUS;\r
-                        }\r
-                        nbRequested = (int) rank;\r
-                        rank = 0;\r
-                        wrapped = true;\r
-                    } else {\r
-                        rank--;\r
-                        if (rank < 0) {\r
-                            rank = (int) fTrace.getNbEvents() - 1;\r
-                            wrapped = true;\r
-                        }\r
-                        if ((rank <= startRank) && wrapped) {\r
-                            synchronized (fSearchSyncObj) {\r
-                                fSearchThread = null;\r
-                            }\r
-                            return Status.OK_STATUS;\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-            int index = (int) foundRank;\r
-            if (eventFilter != null) {\r
-                index = fCache.getFilteredEventIndex(foundRank);\r
-            }\r
-            final int selection = index + 1 + (eventFilter != null ? +1 : 0); // +1 for header row, +1 for top filter status row\r
-\r
-            display.asyncExec(new Runnable() {\r
-                @Override\r
-                public void run() {\r
-                    if (monitor.isCanceled()) {\r
-                        return;\r
-                    }\r
-                    if (fTable.isDisposed()) {\r
-                        return;\r
-                    }\r
-                    fTable.setSelection(selection);\r
-                    fSelectedRank = foundRank;\r
-                    synchronized (fSearchSyncObj) {\r
-                        fSearchThread = null;\r
-                    }\r
-                }\r
-            });\r
-            return Status.OK_STATUS;\r
-        }\r
-\r
-        @Override\r
-        protected void canceling() {\r
-            request.cancel();\r
-            synchronized (fSearchSyncObj) {\r
-                fSearchThread = null;\r
-            }\r
-        }\r
-    }\r
-\r
-    protected void createResources() {\r
-        fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable\r
-                .getForeground().getRGB()));\r
-        fGreenColor = fTable.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);\r
-        fBoldFont = fResourceManager.createFont(FontDescriptor.createFrom(fTable.getFont()).setStyle(SWT.BOLD));\r
-    }\r
-\r
-    protected void packColumns() {\r
-        if (fPackDone) {\r
-            return;\r
-        }\r
-        for (final TableColumn column : fTable.getColumns()) {\r
-            final int headerWidth = column.getWidth();\r
-            column.pack();\r
-            if (column.getWidth() < headerWidth) {\r
-                column.setWidth(headerWidth);\r
-            }\r
-        }\r
-        fPackDone = true;\r
-    }\r
-\r
-    /**\r
-     * @param event\r
-     * @return\r
-     *\r
-     *         FIXME: Add support for column selection\r
-     */\r
-    protected ITmfEventField[] extractItemFields(final ITmfEvent event) {\r
-        ITmfEventField[] fields = new TmfEventField[0];\r
-        if (event != null) {\r
-            final String timestamp = event.getTimestamp().toString();\r
-            final String source = event.getSource();\r
-            final String type = event.getType().getName();\r
-            final String reference = event.getReference();\r
-            final ITmfEventField content = event.getContent();\r
-            final String value = (content.getValue() != null) ? content.getValue().toString() : content.toString();\r
-            fields = new TmfEventField[] {\r
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_TIMESTAMP, timestamp),\r
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_SOURCE, source),\r
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_TYPE, type),\r
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_REFERENCE, reference),\r
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_CONTENT, value)\r
-            };\r
-        }\r
-        return fields;\r
-    }\r
-\r
-    /**\r
-     * Notify this table that is got the UI focus.\r
-     */\r
-    public void setFocus() {\r
-        fTable.setFocus();\r
-    }\r
-\r
-    /**\r
-     * Assign a new trace to this event table.\r
-     *\r
-     * @param trace\r
-     *            The trace to assign to this event table\r
-     * @param disposeOnClose\r
-     *            true if the trace should be disposed when the table is\r
-     *            disposed\r
-     */\r
-    public void setTrace(final ITmfTrace trace, final boolean disposeOnClose) {\r
-        if ((fTrace != null) && fDisposeOnClose) {\r
-            fTrace.dispose();\r
-        }\r
-        fTrace = trace;\r
-        fPackDone = false;\r
-        fSelectedRank = 0;\r
-        fDisposeOnClose = disposeOnClose;\r
-\r
-        // Perform the updates on the UI thread\r
-        fTable.getDisplay().syncExec(new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                fTable.removeAll();\r
-                fCache.setTrace(fTrace); // Clear the cache\r
-                if (fTrace != null) {\r
-                    if (!fTable.isDisposed() && (fTrace != null)) {\r
-                        if (fTable.getData(Key.FILTER_OBJ) == null) {\r
-                            fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row\r
-                        } else {\r
-                            stopFilterThread();\r
-                            fFilterMatchCount = 0;\r
-                            fFilterCheckCount = 0;\r
-                            fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows\r
-                            startFilterThread();\r
-                        }\r
-                    }\r
-                    fRawViewer.setTrace(fTrace);\r
-                }\r
-            }\r
-        });\r
-    }\r
-\r
-    // ------------------------------------------------------------------------\r
-    // Event cache\r
-    // ------------------------------------------------------------------------\r
-\r
-    /**\r
-     * Notify that the event cache has been updated\r
-     *\r
-     * @param completed\r
-     *            Also notify if the populating of the cache is complete, or\r
-     *            not.\r
-     */\r
-    public void cacheUpdated(final boolean completed) {\r
-        synchronized (fCacheUpdateSyncObj) {\r
-            if (fCacheUpdateBusy) {\r
-                fCacheUpdatePending = true;\r
-                fCacheUpdateCompleted = completed;\r
-                return;\r
-            }\r
-            fCacheUpdateBusy = true;\r
-        }\r
-        // Event cache is now updated. Perform update on the UI thread\r
-        if (!fTable.isDisposed()) {\r
-            fTable.getDisplay().asyncExec(new Runnable() {\r
-                @Override\r
-                public void run() {\r
-                    if (!fTable.isDisposed()) {\r
-                        fTable.refresh();\r
-                        packColumns();\r
-                    }\r
-                    if (completed) {\r
-                        populateCompleted();\r
-                    }\r
-                    synchronized (fCacheUpdateSyncObj) {\r
-                        fCacheUpdateBusy = false;\r
-                        if (fCacheUpdatePending) {\r
-                            fCacheUpdatePending = false;\r
-                            cacheUpdated(fCacheUpdateCompleted);\r
-                        }\r
-                    }\r
-                }\r
-            });\r
-        }\r
-    }\r
-\r
-    protected void populateCompleted() {\r
-        // Nothing by default;\r
-    }\r
-\r
-    // ------------------------------------------------------------------------\r
-    // Bookmark handling\r
-    // ------------------------------------------------------------------------\r
-\r
-    /**\r
-     * Add a bookmark to this event table.\r
-     *\r
-     * @param bookmarksFile\r
-     *            The file to use for the bookmarks\r
-     */\r
-    public void addBookmark(final IFile bookmarksFile) {\r
-        fBookmarksFile = bookmarksFile;\r
-        final TableItem[] selection = fTable.getSelection();\r
-        if (selection.length > 0) {\r
-            final TableItem tableItem = selection[0];\r
-            if (tableItem.getData(Key.RANK) != null) {\r
-                final StringBuffer defaultMessage = new StringBuffer();\r
-                for (int i = 0; i < fTable.getColumns().length; i++) {\r
-                    if (i > 0)\r
-                     {\r
-                        defaultMessage.append(", "); //$NON-NLS-1$\r
-                    }\r
-                    defaultMessage.append(tableItem.getText(i));\r
-                }\r
-                final InputDialog dialog = new InputDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),\r
-                        Messages.TmfEventsTable_AddBookmarkDialogTitle, Messages.TmfEventsTable_AddBookmarkDialogText,\r
-                        defaultMessage.toString(), null);\r
-                if (dialog.open() == Window.OK) {\r
-                    final String message = dialog.getValue();\r
-                    try {\r
-                        final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);\r
-                        if (bookmark.exists()) {\r
-                            bookmark.setAttribute(IMarker.MESSAGE, message.toString());\r
-                            final long rank = (Long) tableItem.getData(Key.RANK);\r
-                            final int location = (int) rank;\r
-                            bookmark.setAttribute(IMarker.LOCATION, (Integer) location);\r
-                            fBookmarksMap.put(rank, bookmark.getId());\r
-                            fTable.refresh();\r
-                        }\r
-                    } catch (final CoreException e) {\r
-                        displayException(e);\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-    }\r
-\r
-    /**\r
-     * Remove a bookmark from this event table.\r
-     *\r
-     * @param bookmark\r
-     *            The bookmark to remove\r
-     */\r
-    public void removeBookmark(final IMarker bookmark) {\r
-        for (final Entry<Long, Long> entry : fBookmarksMap.entrySet()) {\r
-            if (entry.getValue().equals(bookmark.getId())) {\r
-                fBookmarksMap.remove(entry.getKey());\r
-                fTable.refresh();\r
-                return;\r
-            }\r
-        }\r
-    }\r
-\r
-    private void toggleBookmark(final long rank) {\r
-        if (fBookmarksFile == null) {\r
-            return;\r
-        }\r
-        if (fBookmarksMap.containsKey(rank)) {\r
-            final Long markerId = fBookmarksMap.remove(rank);\r
-            fTable.refresh();\r
-            try {\r
-                final IMarker bookmark = fBookmarksFile.findMarker(markerId);\r
-                if (bookmark != null) {\r
-                    bookmark.delete();\r
-                }\r
-            } catch (final CoreException e) {\r
-                displayException(e);\r
-            }\r
-        } else {\r
-            addBookmark(fBookmarksFile);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Refresh the bookmarks assigned to this trace, from the contents of a\r
-     * bookmark file.\r
-     *\r
-     * @param bookmarksFile\r
-     *            The bookmark file to use\r
-     */\r
-    public void refreshBookmarks(final IFile bookmarksFile) {\r
-        fBookmarksFile = bookmarksFile;\r
-        if (bookmarksFile == null) {\r
-            fBookmarksMap.clear();\r
-            fTable.refresh();\r
-            return;\r
-        }\r
-        try {\r
-            fBookmarksMap.clear();\r
-            for (final IMarker bookmark : bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) {\r
-                final int location = bookmark.getAttribute(IMarker.LOCATION, -1);\r
-                if (location != -1) {\r
-                    final long rank = location;\r
-                    fBookmarksMap.put(rank, bookmark.getId());\r
-                }\r
-            }\r
-            fTable.refresh();\r
-        } catch (final CoreException e) {\r
-            displayException(e);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void gotoMarker(final IMarker marker) {\r
-        final int rank = marker.getAttribute(IMarker.LOCATION, -1);\r
-        if (rank != -1) {\r
-            int index = rank;\r
-            if (fTable.getData(Key.FILTER_OBJ) != null) {\r
-                index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row\r
-            } else if (rank >= fTable.getItemCount()) {\r
-                fPendingGotoRank = rank;\r
-            }\r
-            fTable.setSelection(index + 1); // +1 for header row\r
-        }\r
-    }\r
-\r
-    // ------------------------------------------------------------------------\r
-    // Listeners\r
-    // ------------------------------------------------------------------------\r
-\r
-    /*\r
-     * (non-Javadoc)\r
-     *\r
-     * @see org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener#colorSettingsChanged(org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting[])\r
-     */\r
-    @Override\r
-    public void colorSettingsChanged(final ColorSetting[] colorSettings) {\r
-        fTable.refresh();\r
-    }\r
-\r
-    @Override\r
-    public void addEventsFilterListener(final ITmfEventsFilterListener listener) {\r
-        if (!fEventsFilterListeners.contains(listener)) {\r
-            fEventsFilterListeners.add(listener);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void removeEventsFilterListener(final ITmfEventsFilterListener listener) {\r
-        fEventsFilterListeners.remove(listener);\r
-    }\r
-\r
-    // ------------------------------------------------------------------------\r
-    // Signal handlers\r
-    // ------------------------------------------------------------------------\r
-\r
-    /**\r
-     * Handler for the experiment updated signal.\r
-     *\r
-     * @param signal\r
-     *            The incoming signal\r
-     */\r
-    @TmfSignalHandler\r
-    public void experimentUpdated(final TmfExperimentUpdatedSignal signal) {\r
-        if ((signal.getExperiment() != fTrace) || fTable.isDisposed()) {\r
-            return;\r
-        }\r
-        // Perform the refresh on the UI thread\r
-        Display.getDefault().asyncExec(new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                if (!fTable.isDisposed() && (fTrace != null)) {\r
-                    if (fTable.getData(Key.FILTER_OBJ) == null) {\r
-                        fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row\r
-                        if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row\r
-                            fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row\r
-                            fPendingGotoRank = -1;\r
-                        }\r
-                    } else {\r
-                        startFilterThread();\r
-                    }\r
-                }\r
-                if (!fRawViewer.isDisposed() && (fTrace != null)) {\r
-                    fRawViewer.refreshEventCount();\r
-                }\r
-            }\r
-        });\r
-    }\r
-\r
-    /**\r
-     * Handler for the trace updated signal\r
-     *\r
-     * @param signal\r
-     *            The incoming signal\r
-     */\r
-    @TmfSignalHandler\r
-    public void traceUpdated(final TmfTraceUpdatedSignal signal) {\r
-        if ((signal.getTrace() != fTrace) || fTable.isDisposed()) {\r
-            return;\r
-        }\r
-        // Perform the refresh on the UI thread\r
-        Display.getDefault().asyncExec(new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                if (!fTable.isDisposed() && (fTrace != null)) {\r
-                    if (fTable.getData(Key.FILTER_OBJ) == null) {\r
-                        fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row\r
-                        if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row\r
-                            fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row\r
-                            fPendingGotoRank = -1;\r
-                        }\r
-                    } else {\r
-                        startFilterThread();\r
-                    }\r
-                }\r
-                if (!fRawViewer.isDisposed() && (fTrace != null)) {\r
-                    fRawViewer.refreshEventCount();\r
-                }\r
-            }\r
-        });\r
-    }\r
-\r
-    /**\r
-     * Handler for the time synch signal.\r
-     *\r
-     * @param signal\r
-     *            The incoming signal\r
-     */\r
-    @TmfSignalHandler\r
-    public void currentTimeUpdated(final TmfTimeSynchSignal signal) {\r
-        if ((signal.getSource() != this) && (fTrace != null) && (!fTable.isDisposed())) {\r
-\r
-            // Create a request for one event that will be queued after other ongoing requests. When this request is completed\r
-            // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents\r
-            // the method fTrace.getRank() from interfering and delaying ongoing requests.\r
-            final TmfDataRequest subRequest = new TmfDataRequest(ITmfEvent.class, 0, 1, ExecutionType.FOREGROUND) {\r
-\r
-                TmfTimestamp ts = new TmfTimestamp(signal.getCurrentTime());\r
-\r
-                @Override\r
-                public void handleData(final ITmfEvent event) {\r
-                    super.handleData(event);\r
-                }\r
-\r
-                @Override\r
-                public void handleCompleted() {\r
-                    super.handleCompleted();\r
-                    if (fTrace == null) {\r
-                        return;\r
-                    }\r
-\r
-                    // Verify if the event is within the trace range and adjust if necessary\r
-                    ITmfTimestamp timestamp = ts;\r
-                    if (timestamp.compareTo(fTrace.getStartTime(), true) == -1) {\r
-                        timestamp = fTrace.getStartTime();\r
-                    }\r
-                    if (timestamp.compareTo(fTrace.getEndTime(), true) == 1) {\r
-                        timestamp = fTrace.getEndTime();\r
-                    }\r
-\r
-                    // Get the rank of the selected event in the table\r
-                    final ITmfContext context = fTrace.seekEvent(timestamp);\r
-                    final long rank = context.getRank();\r
-                    fSelectedRank = rank;\r
-\r
-                    fTable.getDisplay().asyncExec(new Runnable() {\r
-                        @Override\r
-                        public void run() {\r
-                            // Return if table is disposed\r
-                            if (fTable.isDisposed()) {\r
-                                return;\r
-                            }\r
-\r
-                            int index = (int) rank;\r
-                            if (fTable.isDisposed()) {\r
-                                return;\r
-                            }\r
-                            if (fTable.getData(Key.FILTER_OBJ) != null)\r
-                             {\r
-                                index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row\r
-                            }\r
-                            fTable.setSelection(index + 1); // +1 for header row\r
-                            fRawViewer.selectAndReveal(rank);\r
-                        }\r
-                    });\r
-                }\r
-            };\r
-\r
-            ((ITmfDataProvider) fTrace).sendRequest(subRequest);\r
-        }\r
-    }\r
-\r
-    // ------------------------------------------------------------------------\r
-    // Error handling\r
-    // ------------------------------------------------------------------------\r
-\r
-    /**\r
-     * Display an exception in a message box\r
-     *\r
-     * @param e the exception\r
-     */\r
-    private static void displayException(final Exception e) {\r
-        final MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());\r
-        mb.setText(e.getClass().getName());\r
-        mb.setMessage(e.getMessage());\r
-        mb.open();\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2010, 2014 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Francois Chouinard - Initial API and implementation, replaced Table by TmfVirtualTable
+ *   Patrick Tasse - Factored out from events view,
+ *                   Filter implementation (inspired by www.eclipse.org/mat)
+ *   Ansgar Radermacher - Support navigation to model URIs (Bug 396956)
+ *   Bernd Hufmann - Updated call site and model URI implementation
+ *   Alexandre Montplaisir - Update to new column API
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.viewers.events;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.NotEnabledException;
+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.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.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+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.Job;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EValidator;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.FontDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.util.OpenStrategy;
+import org.eclipse.jface.util.SafeRunnable;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+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.linuxtools.internal.tmf.core.filter.TmfCollapseFilter;
+import org.eclipse.linuxtools.internal.tmf.ui.Activator;
+import org.eclipse.linuxtools.internal.tmf.ui.Messages;
+import org.eclipse.linuxtools.internal.tmf.ui.commands.ExportToTextCommandHandler;
+import org.eclipse.linuxtools.internal.tmf.ui.dialogs.MultiLineInputDialog;
+import org.eclipse.linuxtools.tmf.core.component.ITmfEventProvider;
+import org.eclipse.linuxtools.tmf.core.component.TmfComponent;
+import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
+import org.eclipse.linuxtools.tmf.core.event.collapse.ITmfCollapsibleEvent;
+import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfCallsite;
+import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfModelLookup;
+import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfSourceLookup;
+import org.eclipse.linuxtools.tmf.core.filter.ITmfFilter;
+import org.eclipse.linuxtools.tmf.core.filter.model.ITmfFilterTreeNode;
+import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterAndNode;
+import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterMatchesNode;
+import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterNode;
+import org.eclipse.linuxtools.tmf.core.request.ITmfEventRequest.ExecutionType;
+import org.eclipse.linuxtools.tmf.core.request.TmfEventRequest;
+import org.eclipse.linuxtools.tmf.core.signal.TmfEventFilterAppliedSignal;
+import org.eclipse.linuxtools.tmf.core.signal.TmfEventSearchAppliedSignal;
+import org.eclipse.linuxtools.tmf.core.signal.TmfEventSelectedSignal;
+import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
+import org.eclipse.linuxtools.tmf.core.signal.TmfTraceUpdatedSignal;
+import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;
+import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
+import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager;
+import org.eclipse.linuxtools.tmf.core.trace.location.ITmfLocation;
+import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
+import org.eclipse.linuxtools.tmf.ui.viewers.events.columns.TmfEventTableColumn;
+import org.eclipse.linuxtools.tmf.ui.viewers.events.columns.TmfEventTableFieldColumn;
+import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting;
+import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSettingsManager;
+import org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener;
+import org.eclipse.linuxtools.tmf.ui.views.filter.FilterManager;
+import org.eclipse.linuxtools.tmf.ui.widgets.rawviewer.TmfRawEventViewer;
+import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.TmfVirtualTable;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.custom.TableEditor;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+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.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbenchPage;
+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.themes.ColorUtil;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+
+/**
+ * The generic TMF Events table
+ *
+ * This is a view that will list events that are read from a trace.
+ *
+ * @author Francois Chouinard
+ * @author Patrick Tasse
+ * @since 2.0
+ */
+public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorSettingsListener, ISelectionProvider {
+
+    /**
+     * Empty string array, used by {@link #getItemStrings}.
+     * @since 3.0
+     */
+    protected static final @NonNull String[] EMPTY_STRING_ARRAY = new String[0];
+
+    /**
+     * Empty string
+     * @since 3.1
+     */
+    protected static final @NonNull String EMPTY_STRING = ""; //$NON-NLS-1$
+
+    private static final boolean IS_LINUX = System.getProperty("os.name").contains("Linux") ? true : false; //$NON-NLS-1$ //$NON-NLS-2$
+
+    private static final Image BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(
+            "icons/elcl16/bookmark_obj.gif"); //$NON-NLS-1$
+    private static final Image SEARCH_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/search.gif"); //$NON-NLS-1$
+    private static final Image SEARCH_MATCH_IMAGE = Activator.getDefault().getImageFromPath(
+            "icons/elcl16/search_match.gif"); //$NON-NLS-1$
+    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 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;
+
+    /**
+     * Default set of columns to use for trace types that do not specify
+     * anything
+     * @since 3.2
+     */
+    public static final Collection<TmfEventTableColumn> DEFAULT_COLUMNS = ImmutableList.of(
+            TmfEventTableColumn.BaseColumns.TIMESTAMP,
+            TmfEventTableColumn.BaseColumns.SOURCE,
+            TmfEventTableColumn.BaseColumns.EVENT_TYPE,
+            TmfEventTableColumn.BaseColumns.REFERENCE,
+            TmfEventTableColumn.BaseColumns.CONTENTS
+            );
+
+    /**
+     * The events table search/filter keys
+     *
+     * @version 1.0
+     * @author Patrick Tasse
+     */
+    public interface Key {
+        /** Search text */
+        String SEARCH_TXT = "$srch_txt"; //$NON-NLS-1$
+
+        /** Search object */
+        String SEARCH_OBJ = "$srch_obj"; //$NON-NLS-1$
+
+        /** Filter text */
+        String FILTER_TXT = "$fltr_txt"; //$NON-NLS-1$
+
+        /** Filter object */
+        String FILTER_OBJ = "$fltr_obj"; //$NON-NLS-1$
+
+        /** Timestamp */
+        String TIMESTAMP = "$time"; //$NON-NLS-1$
+
+        /** Rank */
+        String RANK = "$rank"; //$NON-NLS-1$
+
+        /** Field ID */
+        String FIELD_ID = "$field_id"; //$NON-NLS-1$
+
+        /** Bookmark indicator */
+        String BOOKMARK = "$bookmark"; //$NON-NLS-1$
+    }
+
+    /**
+     * The events table search/filter state
+     *
+     * @version 1.0
+     * @author Patrick Tasse
+     */
+    public static enum HeaderState {
+        /** A search is being run */
+        SEARCH,
+
+        /** A filter is applied */
+        FILTER
+    }
+
+    interface Direction {
+        int FORWARD = +1;
+        int BACKWARD = -1;
+    }
+
+    // ------------------------------------------------------------------------
+    // Table data
+    // ------------------------------------------------------------------------
+
+    /** The virtual event table */
+    protected TmfVirtualTable fTable;
+
+    private Composite fComposite;
+    private SashForm fSashForm;
+    private TmfRawEventViewer fRawViewer;
+    private ITmfTrace fTrace;
+    volatile private boolean fPackDone = false;
+    private HeaderState fHeaderState = HeaderState.SEARCH;
+    private long fSelectedRank = 0;
+    private ITmfTimestamp fSelectedBeginTimestamp = null;
+    private IStatusLineManager fStatusLineManager = null;
+
+    // Filter data
+    private long fFilterMatchCount;
+    private long fFilterCheckCount;
+    private FilterThread fFilterThread;
+    private boolean fFilterThreadResume = false;
+    private final Object fFilterSyncObj = new Object();
+    private SearchThread fSearchThread;
+    private final Object fSearchSyncObj = new Object();
+
+    /**
+     * List of selection change listeners (element type: <code>ISelectionChangedListener</code>).
+     *
+     * @see #fireSelectionChanged
+     */
+    private ListenerList selectionChangedListeners = new ListenerList();
+
+    // Bookmark map <Rank, MarkerId>
+    private Multimap<Long, Long> fBookmarksMap = HashMultimap.create();
+    private IFile fBookmarksFile;
+    private long fPendingGotoRank = -1;
+
+    // SWT resources
+    private LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());
+    private Color fGrayColor;
+    private Color fGreenColor;
+    private Font fBoldFont;
+
+    private final List<TmfEventTableColumn> fColumns = new LinkedList<>();
+
+    // Event cache
+    private final TmfEventsCache fCache;
+    private boolean fCacheUpdateBusy = false;
+    private boolean fCacheUpdatePending = false;
+    private boolean fCacheUpdateCompleted = false;
+    private final Object fCacheUpdateSyncObj = new Object();
+
+    private boolean fDisposeOnClose;
+
+    // ------------------------------------------------------------------------
+    // Constructors
+    // ------------------------------------------------------------------------
+
+    /**
+     * Basic constructor, using the default set of columns
+     *
+     * @param parent
+     *            The parent composite UI object
+     * @param cacheSize
+     *            The size of the event table cache
+     */
+    public TmfEventsTable(final Composite parent, final int cacheSize) {
+        this(parent, cacheSize, DEFAULT_COLUMNS);
+    }
+
+    /**
+     * Legacy constructor, using ColumnData to define columns
+     *
+     * @param parent
+     *            The parent composite UI object
+     * @param cacheSize
+     *            The size of the event table cache
+     * @param columnData
+     *            Unused
+     * @deprecated Deprecated constructor, use
+     *             {@link #TmfEventsTable(Composite, int, Collection)}
+     */
+    @Deprecated
+    public TmfEventsTable(final Composite parent, int cacheSize,
+            final org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData[] columnData) {
+        /*
+         * We'll do a "best-effort" to keep trace types still using this API to
+         * keep working, by defining a TmfEventTableFieldColumn for each
+         * ColumnData they passed.
+         */
+        this(parent, cacheSize, convertFromColumnData(columnData));
+    }
+
+    @Deprecated
+    private static Collection<TmfEventTableColumn> convertFromColumnData(
+            org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData[] columnData) {
+
+        ImmutableList.Builder<TmfEventTableColumn> builder = new ImmutableList.Builder<>();
+        for (org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData col : columnData) {
+            String header = col.header;
+            if (header != null) {
+                builder.add(new TmfEventTableFieldColumn(header));
+            }
+        }
+        return builder.build();
+    }
+
+    /**
+     * Standard constructor, where we define which columns to use.
+     *
+     * @param parent
+     *            The parent composite UI object
+     * @param cacheSize
+     *            The size of the event table cache
+     * @param columns
+     *            The columns to use in this table.
+     *            <p>
+     *            The iteration order of this collection will correspond to the
+     *            initial ordering of this series of columns in the table.
+     *            </p>
+     * @since 3.1
+     */
+    public TmfEventsTable(final Composite parent, int cacheSize,
+            Collection<? extends TmfEventTableColumn> columns) {
+        super("TmfEventsTable"); //$NON-NLS-1$
+
+        fComposite = new Composite(parent, SWT.NONE);
+        final GridLayout gl = new GridLayout(1, false);
+        gl.marginHeight = 0;
+        gl.marginWidth = 0;
+        gl.verticalSpacing = 0;
+        fComposite.setLayout(gl);
+
+        fSashForm = new SashForm(fComposite, SWT.HORIZONTAL);
+        fSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+        // Create a virtual table
+        final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION;
+        fTable = new TmfVirtualTable(fSashForm, style);
+
+        // Set the table layout
+        final GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+        fTable.setLayoutData(layoutData);
+
+        // Some cosmetic enhancements
+        fTable.setHeaderVisible(true);
+        fTable.setLinesVisible(true);
+
+        // Setup the columns
+        if (columns != null) {
+            fColumns.addAll(columns);
+        }
+
+        TmfMarginColumn collapseCol = new TmfMarginColumn();
+        fColumns.add(MARGIN_COLUMN_INDEX, collapseCol);
+
+        // 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.FIELD_ID, col.getFilterFieldId());
+            column.pack();
+            if (col instanceof TmfMarginColumn) {
+                column.setResizable(false);
+            }
+        }
+
+        // Set the frozen row for header row
+        fTable.setFrozenRowCount(1);
+
+        // Create the header row cell editor
+        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));
+                    }
+                    if (e.item.getData(Key.TIMESTAMP) instanceof ITmfTimestamp) {
+                        final ITmfTimestamp ts = (ITmfTimestamp) e.item.getData(Key.TIMESTAMP);
+                        if (fTable.getSelectionIndices().length == 1) {
+                            fSelectedBeginTimestamp = ts;
+                        }
+                        if (fSelectedBeginTimestamp != null) {
+                            if (fSelectedBeginTimestamp.compareTo(ts) <= 0) {
+                                broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, fSelectedBeginTimestamp, ts));
+                                if (fTable.getSelectionIndices().length == 2) {
+                                    updateStatusLine(ts.getDelta(fSelectedBeginTimestamp));
+                                }
+                            } else {
+                                broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, ts, fSelectedBeginTimestamp));
+                                updateStatusLine(fSelectedBeginTimestamp.getDelta(ts));
+                            }
+                        }
+                    } 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));
+                }
+            }
+        });
+
+        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;
+                    }
+                    index = index - 1; // -1 for top filter status row
+                }
+
+                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.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;
+                }
+            }
+        };
+
+        fTable.addListener(SWT.MouseHover, tooltipListener);
+        fTable.addListener(SWT.Dispose, tooltipListener);
+        fTable.addListener(SWT.KeyDown, tooltipListener);
+        fTable.addListener(SWT.MouseMove, tooltipListener);
+        fTable.addListener(SWT.MouseExit, tooltipListener);
+        fTable.addListener(SWT.MouseDown, tooltipListener);
+        fTable.addListener(SWT.MouseWheel, tooltipListener);
+
+        // Create resources
+        createResources();
+
+        ColorSettingsManager.addColorSettingsListener(this);
+
+        fTable.setItemCount(1); // +1 for header row
+
+        fRawViewer = new TmfRawEventViewer(fSashForm, SWT.H_SCROLL | SWT.V_SCROLL);
+
+        fRawViewer.addSelectionListener(new Listener() {
+            @Override
+            public void handleEvent(final Event e) {
+                if (e.data instanceof Long) {
+                    final long rank = (Long) e.data;
+                    int index = (int) rank;
+                    if (fTable.getData(Key.FILTER_OBJ) != null) {
+                        index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
+                    }
+                    fTable.setSelection(index + 1); // +1 for header row
+                    fSelectedRank = rank;
+                    updateStatusLine(null);
+                } else if (e.data instanceof ITmfLocation) {
+                    // DOES NOT WORK: rank undefined in context from seekLocation()
+                    // ITmfLocation<?> location = (ITmfLocation<?>) e.data;
+                    // TmfContext context = fTrace.seekLocation(location);
+                    // fTable.setSelection((int) context.getRank());
+                    return;
+                } else {
+                    return;
+                }
+                final TableItem[] selection = fTable.getSelection();
+                if ((selection != null) && (selection.length > 0)) {
+                    final TmfTimestamp ts = (TmfTimestamp) fTable.getSelection()[0].getData(Key.TIMESTAMP);
+                    if (ts != null) {
+                        broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, ts));
+                    }
+                }
+            }
+        });
+
+        fSashForm.setWeights(new int[] { 1, 1 });
+        fRawViewer.setVisible(false);
+
+        createPopupMenu();
+    }
+
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
+
+    /**
+     * Create a pop-up menu.
+     */
+    protected void createPopupMenu() {
+        final IAction showTableAction = new Action(Messages.TmfEventsTable_ShowTableActionText) {
+            @Override
+            public void run() {
+                fTable.setVisible(true);
+                fSashForm.layout();
+            }
+        };
+
+        final IAction hideTableAction = new Action(Messages.TmfEventsTable_HideTableActionText) {
+            @Override
+            public void run() {
+                fTable.setVisible(false);
+                fSashForm.layout();
+            }
+        };
+
+        final IAction showRawAction = new Action(Messages.TmfEventsTable_ShowRawActionText) {
+            @Override
+            public void run() {
+                fRawViewer.setVisible(true);
+                fSashForm.layout();
+                final int index = fTable.getSelectionIndex();
+                if (index >= 1) {
+                    fRawViewer.selectAndReveal(index - 1);
+                }
+            }
+        };
+
+        final IAction hideRawAction = new Action(Messages.TmfEventsTable_HideRawActionText) {
+            @Override
+            public void run() {
+                fRawViewer.setVisible(false);
+                fSashForm.layout();
+            }
+        };
+
+        final IAction openCallsiteAction = new Action(Messages.TmfEventsTable_OpenSourceCodeActionText) {
+            @Override
+            public void run() {
+                final TableItem items[] = fTable.getSelection();
+                if (items.length != 1) {
+                    return;
+                }
+                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$
+                        final ArrayList<IFile> files = new ArrayList<>();
+                        ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
+                            @Override
+                            public boolean visit(IResource resource) throws CoreException {
+                                if (resource instanceof IFile && resource.getFullPath().toString().endsWith(trimmedPath)) {
+                                    files.add((IFile) resource);
+                                }
+                                return true;
+                            }
+                        });
+                        IFile file = null;
+                        if (files.size() > 1) {
+                            ListDialog dialog = new ListDialog(getTable().getShell());
+                            dialog.setContentProvider(ArrayContentProvider.getInstance());
+                            dialog.setLabelProvider(new LabelProvider() {
+                                @Override
+                                public String getText(Object element) {
+                                    return ((IFile) element).getFullPath().toString();
+                                }
+                            });
+                            dialog.setInput(files);
+                            dialog.setTitle(Messages.TmfEventsTable_OpenSourceCodeSelectFileDialogTitle);
+                            dialog.setMessage(Messages.TmfEventsTable_OpenSourceCodeSelectFileDialogTitle + '\n' + cs.toString());
+                            dialog.open();
+                            Object[] result = dialog.getResult();
+                            if (result != null && result.length > 0) {
+                                file = (IFile) result[0];
+                            }
+                        } else if (files.size() == 1) {
+                            file = files.get(0);
+                        }
+                        if (file != null) {
+                            marker = file.createMarker(IMarker.MARKER);
+                            marker.setAttribute(IMarker.LINE_NUMBER, Long.valueOf(cs.getLineNumber()).intValue());
+                            IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), marker);
+                            marker.delete();
+                        } else if (files.size() == 0){
+                            displayException(new FileNotFoundException('\'' + cs.toString() + '\'' + '\n' + Messages.TmfEventsTable_OpenSourceCodeNotFound));
+                        }
+                    } catch (CoreException e) {
+                        displayException(e);
+                    }
+                }
+            }
+        };
+
+        final IAction openModelAction = new Action(Messages.TmfEventsTable_OpenModelActionText) {
+            @Override
+            public void run() {
+
+                final TableItem items[] = fTable.getSelection();
+                if (items.length != 1) {
+                    return;
+                }
+                final TableItem item = items[0];
+
+                final Object eventData = item.getData();
+                if (eventData instanceof ITmfModelLookup) {
+                    String modelURI = ((ITmfModelLookup) eventData).getModelUri();
+
+                    if (modelURI != null) {
+                        IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+
+                        IFile file = null;
+                        final URI uri = URI.createURI(modelURI);
+                        if (uri.isPlatformResource()) {
+                            IPath path = new Path(uri.toPlatformString(true));
+                            file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
+                        } else if (uri.isFile() && !uri.isRelative()) {
+                            file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(
+                                    new Path(uri.toFileString()));
+                        }
+
+                        if (file != null) {
+                            try {
+                                /*
+                                 * create a temporary validation marker on the
+                                 * model file, remove it afterwards thus,
+                                 * navigation works with all model editors
+                                 * supporting the navigation to a marker
+                                 */
+                                IMarker marker = file.createMarker(EValidator.MARKER);
+                                marker.setAttribute(EValidator.URI_ATTRIBUTE, modelURI);
+                                marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
+
+                                IDE.openEditor(activePage, marker, OpenStrategy.activateOnOpen());
+                                marker.delete();
+                            }
+                            catch (CoreException e) {
+                                displayException(e);
+                            }
+                        } else {
+                            displayException(new FileNotFoundException('\'' + modelURI + '\'' + '\n' + Messages.TmfEventsTable_OpenModelUnsupportedURI));
+                        }
+                    }
+                }
+            }
+        };
+
+        final IAction exportToTextAction = new Action(Messages.TmfEventsTable_Export_to_text) {
+            @Override
+            public void run() {
+                IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+                IHandlerService handlerService = (IHandlerService) activePage.getActiveEditor().getSite().getService(IHandlerService.class);
+                ICommandService cmdService = (ICommandService) activePage.getActiveEditor().getSite().getService(ICommandService.class);
+                try {
+                    HashMap<String, Object> parameters = new HashMap<>();
+                    Command command = cmdService.getCommand(ExportToTextCommandHandler.COMMAND_ID);
+                    ParameterizedCommand cmd = ParameterizedCommand.generateCommand(command, parameters);
+
+                    IEvaluationContext context = handlerService.getCurrentState();
+                    // Omit the margin column
+                    List<TmfEventTableColumn> exportColumns = fColumns.subList(EVENT_COLUMNS_START_INDEX, fColumns.size());
+                    context.addVariable(ExportToTextCommandHandler.TMF_EVENT_TABLE_COLUMNS_ID, exportColumns);
+
+                    handlerService.executeCommandInContext(cmd, null, context);
+                } catch (ExecutionException e) {
+                    displayException(e);
+                } catch (NotDefinedException e) {
+                    displayException(e);
+                } catch (NotEnabledException e) {
+                    displayException(e);
+                } catch (NotHandledException e) {
+                    displayException(e);
+                }
+            }
+        };
+
+        final IAction showSearchBarAction = new Action(Messages.TmfEventsTable_ShowSearchBarActionText) {
+            @Override
+            public void run() {
+                fHeaderState = HeaderState.SEARCH;
+                fTable.refresh();
+            }
+        };
+
+        final IAction showFilterBarAction = new Action(Messages.TmfEventsTable_ShowFilterBarActionText) {
+            @Override
+            public void run() {
+                fHeaderState = HeaderState.FILTER;
+                fTable.refresh();
+            }
+        };
+
+        final IAction clearFiltersAction = new Action(Messages.TmfEventsTable_ClearFiltersActionText) {
+            @Override
+            public void run() {
+                clearFilters();
+            }
+        };
+
+        final IAction collapseAction = new Action(Messages.TmfEventsTable_CollapseFilterMenuName) {
+            @Override
+            public void run() {
+                applyFilter(new TmfCollapseFilter());
+            }
+        };
+
+        class ToggleBookmarkAction extends Action {
+            Long fRank;
+
+            public ToggleBookmarkAction(final String text, final Long rank) {
+                super(text);
+                fRank = rank;
+            }
+
+            @Override
+            public void run() {
+                toggleBookmark(fRank);
+            }
+        }
+
+        final MenuManager tablePopupMenu = new MenuManager();
+        tablePopupMenu.setRemoveAllWhenShown(true);
+        tablePopupMenu.addMenuListener(new IMenuListener() {
+            @Override
+            public void menuAboutToShow(final IMenuManager manager) {
+                if (fTable.getSelectionIndex() == 0) {
+                    // Right-click on header row
+                    if (fHeaderState == HeaderState.FILTER) {
+                        tablePopupMenu.add(showSearchBarAction);
+                    } else {
+                        tablePopupMenu.add(showFilterBarAction);
+                    }
+                    return;
+                }
+                final Point point = fTable.toControl(Display.getDefault().getCursorLocation());
+                final TableItem item = fTable.getSelection().length > 0 ? fTable.getSelection()[0] : null;
+                if (item != null) {
+                    final Rectangle imageBounds = item.getImageBounds(0);
+                    imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
+                    if (point.x <= (imageBounds.x + imageBounds.width)) {
+                        // Right-click on left margin
+                        final Long rank = (Long) item.getData(Key.RANK);
+                        if ((rank != null) && (fBookmarksFile != null)) {
+                            if (fBookmarksMap.containsKey(rank)) {
+                                tablePopupMenu.add(new ToggleBookmarkAction(
+                                        Messages.TmfEventsTable_RemoveBookmarkActionText, rank));
+                            } else {
+                                tablePopupMenu.add(new ToggleBookmarkAction(
+                                        Messages.TmfEventsTable_AddBookmarkActionText, rank));
+                            }
+                        }
+                        return;
+                    }
+                }
+
+                // Right-click on table
+                if (fTable.isVisible() && fRawViewer.isVisible()) {
+                    tablePopupMenu.add(hideTableAction);
+                    tablePopupMenu.add(hideRawAction);
+                } else if (!fTable.isVisible()) {
+                    tablePopupMenu.add(showTableAction);
+                } else if (!fRawViewer.isVisible()) {
+                    tablePopupMenu.add(showRawAction);
+                }
+                tablePopupMenu.add(exportToTextAction);
+                tablePopupMenu.add(new Separator());
+
+                if (item != null) {
+                    final Object data = item.getData();
+                    Separator separator = null;
+                    if (data instanceof ITmfSourceLookup) {
+                        ITmfSourceLookup event = (ITmfSourceLookup) data;
+                        if (event.getCallsite() != null) {
+                            tablePopupMenu.add(openCallsiteAction);
+                            separator = new Separator();
+                        }
+                    }
+
+                    if (data instanceof ITmfModelLookup) {
+                        ITmfModelLookup event = (ITmfModelLookup) data;
+                        if (event.getModelUri() != null) {
+                            tablePopupMenu.add(openModelAction);
+                            separator = new Separator();
+                        }
+
+                        if (separator != null) {
+                            tablePopupMenu.add(separator);
+                        }
+                    }
+                }
+
+                // only show collapse filter if at least one trace can be collapsed
+                boolean isCollapsible = false;
+                if (fTrace != null) {
+                    ITmfTrace traces[] = TmfTraceManager.getTraceSet(fTrace);
+                    for (ITmfTrace trace : traces) {
+                        Class <? extends ITmfEvent> eventClass = trace.getEventType();
+                        isCollapsible = ITmfCollapsibleEvent.class.isAssignableFrom(eventClass);
+                        if (isCollapsible) {
+                            break;
+                        }
+                    }
+                }
+
+                if (isCollapsible && !(fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter)) {
+                    tablePopupMenu.add(collapseAction);
+                    tablePopupMenu.add(new Separator());
+                }
+
+                tablePopupMenu.add(clearFiltersAction);
+                final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();
+                if (savedFilters.length > 0) {
+                    final MenuManager subMenu = new MenuManager(Messages.TmfEventsTable_ApplyPresetFilterMenuName);
+                    for (final ITmfFilterTreeNode node : savedFilters) {
+                        if (node instanceof TmfFilterNode) {
+                            final TmfFilterNode filter = (TmfFilterNode) node;
+                            subMenu.add(new Action(filter.getFilterName()) {
+                                @Override
+                                public void run() {
+                                    applyFilter(filter);
+                                }
+                            });
+                        }
+                    }
+                    tablePopupMenu.add(subMenu);
+                }
+                appendToTablePopupMenu(tablePopupMenu, item);
+            }
+        });
+
+        final MenuManager rawViewerPopupMenu = new MenuManager();
+        rawViewerPopupMenu.setRemoveAllWhenShown(true);
+        rawViewerPopupMenu.addMenuListener(new IMenuListener() {
+            @Override
+            public void menuAboutToShow(final IMenuManager manager) {
+                if (fTable.isVisible() && fRawViewer.isVisible()) {
+                    rawViewerPopupMenu.add(hideTableAction);
+                    rawViewerPopupMenu.add(hideRawAction);
+                } else if (!fTable.isVisible()) {
+                    rawViewerPopupMenu.add(showTableAction);
+                } else if (!fRawViewer.isVisible()) {
+                    rawViewerPopupMenu.add(showRawAction);
+                }
+                appendToRawPopupMenu(tablePopupMenu);
+            }
+        });
+
+        Menu menu = tablePopupMenu.createContextMenu(fTable);
+        fTable.setMenu(menu);
+
+        menu = rawViewerPopupMenu.createContextMenu(fRawViewer);
+        fRawViewer.setMenu(menu);
+    }
+
+
+    /**
+     * Append an item to the event table's pop-up menu.
+     *
+     * @param tablePopupMenu
+     *            The menu manager
+     * @param selectedItem
+     *            The item to append
+     */
+    protected void appendToTablePopupMenu(final MenuManager tablePopupMenu, final TableItem selectedItem) {
+        // override to append more actions
+    }
+
+    /**
+     * Append an item to the raw viewer's pop-up menu.
+     *
+     * @param rawViewerPopupMenu
+     *            The menu manager
+     */
+    protected void appendToRawPopupMenu(final MenuManager rawViewerPopupMenu) {
+        // override to append more actions
+    }
+
+    @Override
+    public void dispose() {
+        stopSearchThread();
+        stopFilterThread();
+        ColorSettingsManager.removeColorSettingsListener(this);
+        fComposite.dispose();
+        if ((fTrace != null) && fDisposeOnClose) {
+            fTrace.dispose();
+        }
+        fResourceManager.dispose();
+        fRawViewer.dispose();
+        super.dispose();
+    }
+
+    /**
+     * Assign a layout data object to this view.
+     *
+     * @param layoutData
+     *            The layout data to assign
+     */
+    public void setLayoutData(final Object layoutData) {
+        fComposite.setLayoutData(layoutData);
+    }
+
+    /**
+     * Get the virtual table contained in this event table.
+     *
+     * @return The TMF virtual table
+     */
+    public TmfVirtualTable getTable() {
+        return fTable;
+    }
+
+    /**
+     * @param columnData
+     *            columnData
+     * @deprecated The column headers are now set at the constructor, this
+     *             shouldn't be called anymore.
+     */
+    @Deprecated
+    protected void setColumnHeaders(final org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData [] columnData) {
+        /* No-op */
+    }
+
+    /**
+     * Set a table item's data.
+     *
+     * @param item
+     *            The item to set
+     * @param event
+     *            Which trace event to link with this entry
+     * @param rank
+     *            Which rank this event has in the trace/experiment
+     */
+    protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {
+        String[] itemStrings = getItemStrings(fColumns, event);
+        item.setText(itemStrings);
+        item.setData(event);
+        item.setData(Key.TIMESTAMP, new TmfTimestamp(event.getTimestamp()));
+        item.setData(Key.RANK, rank);
+
+        final Collection<Long> markerIds = fBookmarksMap.get(rank);
+        if (!markerIds.isEmpty()) {
+            Joiner joiner = Joiner.on("\n -").skipNulls(); //$NON-NLS-1$
+            List<Object> parts = new ArrayList<>();
+            if (markerIds.size() > 1) {
+                parts.add(Messages.TmfEventsTable_MultipleBookmarksToolTip);
+            }
+            try {
+                for (long markerId : markerIds) {
+                    final IMarker marker = fBookmarksFile.findMarker(markerId);
+                    parts.add(marker.getAttribute(IMarker.MESSAGE));
+                }
+            } catch (CoreException e) {
+                displayException(e);
+            }
+            item.setData(Key.BOOKMARK, joiner.join(parts));
+        } else {
+            item.setData(Key.BOOKMARK, null);
+        }
+
+        boolean searchMatch = false;
+        boolean searchNoMatch = false;
+        final ITmfFilter searchFilter = (ITmfFilter) fTable.getData(Key.SEARCH_OBJ);
+        if (searchFilter != null) {
+            if (searchFilter.matches(event)) {
+                searchMatch = true;
+            } else {
+                searchNoMatch = true;
+            }
+        }
+
+        final ColorSetting colorSetting = ColorSettingsManager.getColorSetting(event);
+        if (searchNoMatch) {
+            item.setForeground(colorSetting.getDimmedForegroundColor());
+            item.setBackground(colorSetting.getDimmedBackgroundColor());
+        } else {
+            item.setForeground(colorSetting.getForegroundColor());
+            item.setBackground(colorSetting.getBackgroundColor());
+        }
+
+        if (searchMatch) {
+            if (!markerIds.isEmpty()) {
+                item.setImage(SEARCH_MATCH_BOOKMARK_IMAGE);
+            } else {
+                item.setImage(SEARCH_MATCH_IMAGE);
+            }
+        } else if (!markerIds.isEmpty()) {
+            item.setImage(BOOKMARK_IMAGE);
+        } else {
+            item.setImage((Image) null);
+        }
+
+        if ((itemStrings[MARGIN_COLUMN_INDEX] != null) && !itemStrings[MARGIN_COLUMN_INDEX].isEmpty()) {
+            packMarginColumn();
+        }
+    }
+
+    /**
+     * Set the item data of the header row.
+     *
+     * @param item
+     *            The item to use as table header
+     */
+    protected void setHeaderRowItemData(final TableItem item) {
+        String txtKey = null;
+        if (fHeaderState == HeaderState.SEARCH) {
+            item.setImage(SEARCH_IMAGE);
+            txtKey = Key.SEARCH_TXT;
+        } else if (fHeaderState == HeaderState.FILTER) {
+            item.setImage(FILTER_IMAGE);
+            txtKey = Key.FILTER_TXT;
+        }
+        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);
+            if (filter == null) {
+                if (fHeaderState == HeaderState.SEARCH) {
+                    item.setText(i, SEARCH_HINT);
+                } else if (fHeaderState == HeaderState.FILTER) {
+                    item.setText(i, FILTER_HINT);
+                }
+                item.setForeground(i, fGrayColor);
+                item.setFont(i, fTable.getFont());
+            } else {
+                item.setText(i, filter);
+                item.setForeground(i, fGreenColor);
+                item.setFont(i, fBoldFont);
+            }
+        }
+    }
+
+    /**
+     * Set the item data of the "filter status" row.
+     *
+     * @param item
+     *            The item to use as filter status row
+     */
+    protected void setFilterStatusRowItemData(final TableItem item) {
+        for (int i = 0; i < fTable.getColumns().length; i++) {
+            if (i == MARGIN_COLUMN_INDEX) {
+                if ((fTrace == null) || (fFilterCheckCount == fTrace.getNbEvents())) {
+                    item.setImage(FILTER_IMAGE);
+                } else {
+                    item.setImage(STOP_IMAGE);
+                }
+            }
+
+            if (i == FILTER_SUMMARY_INDEX) {
+                item.setText(FILTER_SUMMARY_INDEX, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
+            } else {
+                item.setText(i, EMPTY_STRING);
+            }
+        }
+        item.setData(null);
+        item.setData(Key.TIMESTAMP, null);
+        item.setData(Key.RANK, null);
+        item.setForeground(null);
+        item.setBackground(null);
+    }
+
+    /**
+     * Create an editor for the header.
+     */
+    protected void createHeaderEditor() {
+        final TableEditor tableEditor = fTable.createTableEditor();
+        tableEditor.horizontalAlignment = SWT.LEFT;
+        tableEditor.verticalAlignment = SWT.CENTER;
+        tableEditor.grabHorizontal = true;
+        tableEditor.minimumWidth = 50;
+
+        // Handle the header row selection
+        fTable.addMouseListener(new MouseAdapter() {
+            int columnIndex;
+            TableColumn column;
+            TableItem item;
+
+            @Override
+            public void mouseDown(final MouseEvent event) {
+                if (event.button != 1) {
+                    return;
+                }
+                // Identify the selected row
+                final Point point = new Point(event.x, event.y);
+                item = fTable.getItem(point);
+
+                // Header row selected
+                if ((item != null) && (fTable.indexOf(item) == 0)) {
+
+                    // Icon selected
+                    if (item.getImageBounds(0).contains(point)) {
+                        if (fHeaderState == HeaderState.SEARCH) {
+                            fHeaderState = HeaderState.FILTER;
+                        } else if (fHeaderState == HeaderState.FILTER) {
+                            fHeaderState = HeaderState.SEARCH;
+                        }
+                        fTable.setSelection(0);
+                        fTable.refresh();
+                        return;
+                    }
+
+                    // Identify the selected column
+                    columnIndex = -1;
+                    for (int i = 0; i < fTable.getColumns().length; i++) {
+                        final Rectangle rect = item.getBounds(i);
+                        if (rect.contains(point)) {
+                            columnIndex = i;
+                            break;
+                        }
+                    }
+
+                    if (columnIndex == -1) {
+                        return;
+                    }
+
+                    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);
+                    if (headerString != null) {
+                        newEditor.setText(headerString);
+                    }
+                    newEditor.addFocusListener(new FocusAdapter() {
+                        @Override
+                        public void focusLost(final FocusEvent e) {
+                            final boolean changed = updateHeader(newEditor.getText());
+                            if (changed) {
+                                applyHeader();
+                            }
+                        }
+                    });
+                    newEditor.addKeyListener(new KeyAdapter() {
+                        @Override
+                        public void keyPressed(final KeyEvent e) {
+                            if (e.character == SWT.CR) {
+                                updateHeader(newEditor.getText());
+                                applyHeader();
+
+                                // Set focus on the table so that the next carriage return goes to the next result
+                                TmfEventsTable.this.getTable().setFocus();
+                            } else if (e.character == SWT.ESC) {
+                                tableEditor.getEditor().dispose();
+                            }
+                        }
+                    });
+                    newEditor.selectAll();
+                    newEditor.setFocus();
+                    tableEditor.setEditor(newEditor, item, columnIndex);
+                }
+            }
+
+            /*
+             * returns true is value was changed
+             */
+            private boolean updateHeader(final String text) {
+                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 (text.trim().length() > 0) {
+                    try {
+                        final String regex = TmfFilterMatchesNode.regexFix(text);
+                        Pattern.compile(regex);
+                        if (regex.equals(column.getData(txtKey))) {
+                            tableEditor.getEditor().dispose();
+                            return false;
+                        }
+                        final TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null);
+                        String fieldId = (String) column.getData(Key.FIELD_ID);
+                        if (fieldId == null) {
+                            fieldId = column.getText();
+                        }
+                        filter.setField(fieldId);
+                        filter.setRegex(regex);
+                        column.setData(objKey, filter);
+                        column.setData(txtKey, regex);
+                    } catch (final PatternSyntaxException ex) {
+                        tableEditor.getEditor().dispose();
+                        MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
+                                ex.getDescription(), ex.getMessage());
+                        return false;
+                    }
+                } else {
+                    if (column.getData(txtKey) == null) {
+                        tableEditor.getEditor().dispose();
+                        return false;
+                    }
+                    column.setData(objKey, null);
+                    column.setData(txtKey, 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();
+                    }
+                }
+
+                tableEditor.getEditor().dispose();
+            }
+        });
+
+        fTable.addKeyListener(new KeyAdapter() {
+            @Override
+            public void keyPressed(final KeyEvent e) {
+                e.doit = false;
+                if (e.character == SWT.ESC) {
+                    stopFilterThread();
+                    stopSearchThread();
+                    fTable.refresh();
+                } else if (e.character == SWT.DEL) {
+                    if (fHeaderState == HeaderState.SEARCH) {
+                        stopSearchThread();
+                        for (final TableColumn column : fTable.getColumns()) {
+                            column.setData(Key.SEARCH_OBJ, null);
+                            column.setData(Key.SEARCH_TXT, null);
+                        }
+                        fTable.setData(Key.SEARCH_OBJ, null);
+                        fTable.refresh();
+                        fireSearchApplied(null);
+                    } else if (fHeaderState == HeaderState.FILTER) {
+                        clearFilters();
+                    }
+                } else if (e.character == SWT.CR) {
+                    if ((e.stateMask & SWT.SHIFT) == 0) {
+                        searchNext();
+                    } else {
+                        searchPrevious();
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Send an event indicating a filter has been applied.
+     *
+     * @param filter
+     *            The filter that was just applied
+     */
+    protected void fireFilterApplied(final ITmfFilter filter) {
+        broadcast(new TmfEventFilterAppliedSignal(this, fTrace, filter));
+    }
+
+    /**
+     * Send an event indicating that a search has been applied.
+     *
+     * @param filter
+     *            The search filter that was just applied
+     */
+    protected void fireSearchApplied(final ITmfFilter filter) {
+        broadcast(new TmfEventSearchAppliedSignal(this, fTrace, filter));
+    }
+
+    /**
+     * Start the filtering thread.
+     */
+    protected void startFilterThread() {
+        synchronized (fFilterSyncObj) {
+            final ITmfFilterTreeNode filter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
+            if (fFilterThread == null || fFilterThread.filter != filter) {
+                if (fFilterThread != null) {
+                    fFilterThread.cancel();
+                    fFilterThreadResume = false;
+                }
+                fFilterThread = new FilterThread(filter);
+                fFilterThread.start();
+            } else {
+                fFilterThreadResume = true;
+            }
+        }
+    }
+
+    /**
+     * Stop the filtering thread.
+     */
+    protected void stopFilterThread() {
+        synchronized (fFilterSyncObj) {
+            if (fFilterThread != null) {
+                fFilterThread.cancel();
+                fFilterThread = null;
+                fFilterThreadResume = false;
+            }
+        }
+    }
+
+    /**
+     * Apply a filter.
+     *
+     * @param filter
+     *            The filter to apply
+     * @since 1.1
+     */
+    protected void applyFilter(ITmfFilter filter) {
+        stopFilterThread();
+        stopSearchThread();
+        fFilterMatchCount = 0;
+        fFilterCheckCount = 0;
+        fCache.applyFilter(filter);
+        fTable.clearAll();
+        fTable.setData(Key.FILTER_OBJ, filter);
+        fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
+        startFilterThread();
+        fireFilterApplied(filter);
+    }
+
+    /**
+     * Clear all currently active filters.
+     */
+    protected void clearFilters() {
+        if (fTable.getData(Key.FILTER_OBJ) == null) {
+            return;
+        }
+        stopFilterThread();
+        stopSearchThread();
+        fCache.clearFilter();
+        fTable.clearAll();
+        for (final TableColumn column : fTable.getColumns()) {
+            column.setData(Key.FILTER_OBJ, null);
+            column.setData(Key.FILTER_TXT, null);
+        }
+        fTable.setData(Key.FILTER_OBJ, null);
+        if (fTrace != null) {
+            fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
+        } else {
+            fTable.setItemCount(1); // +1 for header row
+        }
+        fFilterMatchCount = 0;
+        fFilterCheckCount = 0;
+        if (fSelectedRank >= 0) {
+            fTable.setSelection((int) fSelectedRank + 1); // +1 for header row
+        } else {
+            fTable.setSelection(0);
+        }
+        fireFilterApplied(null);
+        updateStatusLine(null);
+
+        // Set original width
+        fTable.getColumns()[MARGIN_COLUMN_INDEX].setWidth(0);
+        packMarginColumn();
+    }
+
+    /**
+     * Wrapper Thread object for the filtering thread.
+     */
+    protected class FilterThread extends Thread {
+        private final ITmfFilterTreeNode filter;
+        private TmfEventRequest request;
+        private boolean refreshBusy = false;
+        private boolean refreshPending = false;
+        private final Object syncObj = new Object();
+
+        /**
+         * Constructor.
+         *
+         * @param filter
+         *            The filter this thread will be processing
+         */
+        public FilterThread(final ITmfFilterTreeNode filter) {
+            super("Filter Thread"); //$NON-NLS-1$
+            this.filter = filter;
+        }
+
+        @Override
+        public void run() {
+            if (fTrace == null) {
+                return;
+            }
+            final int nbRequested = (int) (fTrace.getNbEvents() - fFilterCheckCount);
+            if (nbRequested <= 0) {
+                return;
+            }
+            request = new TmfEventRequest(ITmfEvent.class, TmfTimeRange.ETERNITY,
+                    (int) fFilterCheckCount, nbRequested, ExecutionType.BACKGROUND) {
+                @Override
+                public void handleData(final ITmfEvent event) {
+                    super.handleData(event);
+                    if (request.isCancelled()) {
+                        return;
+                    }
+                    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) {
+                            fCache.updateCollapsedEvent((int) fFilterMatchCount - 1);
+                        }
+                    }
+
+                    if (refresh || (fFilterCheckCount % 100) == 0) {
+                        refreshTable();
+                    }
+                    fFilterCheckCount++;
+                }
+            };
+            ((ITmfEventProvider) fTrace).sendRequest(request);
+            try {
+                request.waitForCompletion();
+            } catch (final InterruptedException e) {
+            }
+            refreshTable();
+            synchronized (fFilterSyncObj) {
+                fFilterThread = null;
+                if (fFilterThreadResume) {
+                    fFilterThreadResume = false;
+                    fFilterThread = new FilterThread(filter);
+                    fFilterThread.start();
+                }
+            }
+        }
+
+        /**
+         * Refresh the filter.
+         */
+        public void refreshTable() {
+            synchronized (syncObj) {
+                if (refreshBusy) {
+                    refreshPending = true;
+                    return;
+                }
+                refreshBusy = true;
+            }
+            Display.getDefault().asyncExec(new Runnable() {
+                @Override
+                public void run() {
+                    if (request.isCancelled()) {
+                        return;
+                    }
+                    if (fTable.isDisposed()) {
+                        return;
+                    }
+                    fTable.setItemCount((int) fFilterMatchCount + 3); // +1 for header row, +2 for top and bottom filter status rows
+                    fTable.refresh();
+                    synchronized (syncObj) {
+                        refreshBusy = false;
+                        if (refreshPending) {
+                            refreshPending = false;
+                            refreshTable();
+                        }
+                    }
+                }
+            });
+        }
+
+        /**
+         * Cancel this filtering thread.
+         */
+        public void cancel() {
+            if (request != null) {
+                request.cancel();
+            }
+        }
+    }
+
+    /**
+     * Go to the next item of a search.
+     */
+    protected void searchNext() {
+        synchronized (fSearchSyncObj) {
+            if (fSearchThread != null) {
+                return;
+            }
+            final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
+            if (searchFilter == null) {
+                return;
+            }
+            final int selectionIndex = fTable.getSelectionIndex();
+            int startIndex;
+            if (selectionIndex > 0) {
+                startIndex = selectionIndex; // -1 for header row, +1 for next event
+            } else {
+                // header row is selected, start at top event
+                startIndex = Math.max(0, fTable.getTopIndex() - 1); // -1 for header row
+            }
+            final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
+            if (eventFilter != null) {
+                startIndex = Math.max(0, startIndex - 1); // -1 for top filter status row
+            }
+            fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.FORWARD);
+            fSearchThread.schedule();
+        }
+    }
+
+    /**
+     * Go to the previous item of a search.
+     */
+    protected void searchPrevious() {
+        synchronized (fSearchSyncObj) {
+            if (fSearchThread != null) {
+                return;
+            }
+            final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
+            if (searchFilter == null) {
+                return;
+            }
+            final int selectionIndex = fTable.getSelectionIndex();
+            int startIndex;
+            if (selectionIndex > 0) {
+                startIndex = selectionIndex - 2; // -1 for header row, -1 for previous event
+            } else {
+                // header row is selected, start at precedent of top event
+                startIndex = fTable.getTopIndex() - 2; // -1 for header row, -1 for previous event
+            }
+            final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
+            if (eventFilter != null) {
+                startIndex = startIndex - 1; // -1 for top filter status row
+            }
+            fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.BACKWARD);
+            fSearchThread.schedule();
+        }
+    }
+
+    /**
+     * Stop the search thread.
+     */
+    protected void stopSearchThread() {
+        fPendingGotoRank = -1;
+        synchronized (fSearchSyncObj) {
+            if (fSearchThread != null) {
+                fSearchThread.cancel();
+                fSearchThread = null;
+            }
+        }
+    }
+
+    /**
+     * Wrapper for the search thread.
+     */
+    protected class SearchThread extends Job {
+
+        private ITmfFilterTreeNode searchFilter;
+        private ITmfFilterTreeNode eventFilter;
+        private int startIndex;
+        private int direction;
+        private long rank;
+        private long foundRank = -1;
+        private TmfEventRequest request;
+        private ITmfTimestamp foundTimestamp = null;
+
+        /**
+         * Constructor.
+         *
+         * @param searchFilter
+         *            The search filter
+         * @param eventFilter
+         *            The event filter
+         * @param startIndex
+         *            The index at which we should start searching
+         * @param currentRank
+         *            The current rank
+         * @param direction
+         *            In which direction should we search, forward or backwards
+         */
+        public SearchThread(final ITmfFilterTreeNode searchFilter,
+                final ITmfFilterTreeNode eventFilter, final int startIndex,
+                final long currentRank, final int direction) {
+            super(Messages.TmfEventsTable_SearchingJobName);
+            this.searchFilter = searchFilter;
+            this.eventFilter = eventFilter;
+            this.startIndex = startIndex;
+            this.rank = currentRank;
+            this.direction = direction;
+        }
+
+        @Override
+        protected IStatus run(final IProgressMonitor monitor) {
+            if (fTrace == null) {
+                return Status.OK_STATUS;
+            }
+            final Display display = Display.getDefault();
+            if (startIndex < 0) {
+                rank = (int) fTrace.getNbEvents() - 1;
+            } else if (startIndex >= (fTable.getItemCount() - (eventFilter == null ? 1 : 3))) { // -1 for header row, -2 for top and bottom filter status rows
+                rank = 0;
+            } else {
+                int idx = startIndex;
+                while (foundRank == -1) {
+                    final CachedEvent event = fCache.peekEvent(idx);
+                    if (event == null) {
+                        break;
+                    }
+                    rank = event.rank;
+                    if (searchFilter.matches(event.event) && ((eventFilter == null) || eventFilter.matches(event.event))) {
+                        foundRank = event.rank;
+                        foundTimestamp = event.event.getTimestamp();
+                        break;
+                    }
+                    if (direction == Direction.FORWARD) {
+                        idx++;
+                    } else {
+                        idx--;
+                    }
+                }
+                if (foundRank == -1) {
+                    if (direction == Direction.FORWARD) {
+                        rank++;
+                        if (rank > (fTrace.getNbEvents() - 1)) {
+                            rank = 0;
+                        }
+                    } else {
+                        rank--;
+                        if (rank < 0) {
+                            rank = (int) fTrace.getNbEvents() - 1;
+                        }
+                    }
+                }
+            }
+            final int startRank = (int) rank;
+            boolean wrapped = false;
+            while (!monitor.isCanceled() && (foundRank == -1) && (fTrace != null)) {
+                int nbRequested = (direction == Direction.FORWARD ? Integer.MAX_VALUE : Math.min((int) rank + 1, fTrace.getCacheSize()));
+                if (direction == Direction.BACKWARD) {
+                    rank = Math.max(0, rank - fTrace.getCacheSize() + 1);
+                }
+                request = new TmfEventRequest(ITmfEvent.class, TmfTimeRange.ETERNITY,
+                        (int) rank, nbRequested, ExecutionType.BACKGROUND) {
+                    long currentRank = rank;
+
+                    @Override
+                    public void handleData(final ITmfEvent event) {
+                        super.handleData(event);
+                        if (searchFilter.matches(event) && ((eventFilter == null) || eventFilter.matches(event))) {
+                            foundRank = currentRank;
+                            foundTimestamp = event.getTimestamp();
+                            if (direction == Direction.FORWARD) {
+                                done();
+                                return;
+                            }
+                        }
+                        currentRank++;
+                    }
+                };
+                ((ITmfEventProvider) fTrace).sendRequest(request);
+                try {
+                    request.waitForCompletion();
+                    if (request.isCancelled()) {
+                        return Status.OK_STATUS;
+                    }
+                } catch (final InterruptedException e) {
+                    synchronized (fSearchSyncObj) {
+                        fSearchThread = null;
+                    }
+                    return Status.OK_STATUS;
+                }
+                if (foundRank == -1) {
+                    if (direction == Direction.FORWARD) {
+                        if (rank == 0) {
+                            synchronized (fSearchSyncObj) {
+                                fSearchThread = null;
+                            }
+                            return Status.OK_STATUS;
+                        }
+                        nbRequested = (int) rank;
+                        rank = 0;
+                        wrapped = true;
+                    } else {
+                        rank--;
+                        if (rank < 0) {
+                            rank = (int) fTrace.getNbEvents() - 1;
+                            wrapped = true;
+                        }
+                        if ((rank <= startRank) && wrapped) {
+                            synchronized (fSearchSyncObj) {
+                                fSearchThread = null;
+                            }
+                            return Status.OK_STATUS;
+                        }
+                    }
+                }
+            }
+            int index = (int) foundRank;
+            if (eventFilter != null) {
+                index = fCache.getFilteredEventIndex(foundRank);
+            }
+            final int selection = index + 1 + (eventFilter != null ? +1 : 0); // +1 for header row, +1 for top filter status row
+
+            display.asyncExec(new Runnable() {
+                @Override
+                public void run() {
+                    if (monitor.isCanceled()) {
+                        return;
+                    }
+                    if (fTable.isDisposed()) {
+                        return;
+                    }
+                    fTable.setSelection(selection);
+                    fSelectedRank = foundRank;
+                    fRawViewer.selectAndReveal(fSelectedRank);
+                    if (foundTimestamp != null) {
+                        broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, foundTimestamp));
+                    }
+                    fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, getSelection()));
+                    synchronized (fSearchSyncObj) {
+                        fSearchThread = null;
+                    }
+                    updateStatusLine(null);
+                }
+            });
+            return Status.OK_STATUS;
+        }
+
+        @Override
+        protected void canceling() {
+            request.cancel();
+            synchronized (fSearchSyncObj) {
+                fSearchThread = null;
+            }
+        }
+    }
+
+    /**
+     * Create the resources.
+     */
+    protected void createResources() {
+        fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable
+                .getForeground().getRGB()));
+        fGreenColor = fTable.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);
+        fBoldFont = fResourceManager.createFont(FontDescriptor.createFrom(fTable.getFont()).setStyle(SWT.BOLD));
+    }
+
+    /**
+     * Pack the columns.
+     */
+    protected void packColumns() {
+        if (fPackDone) {
+            return;
+        }
+        fTable.setRedraw(false);
+        try {
+            TableColumn tableColumns[] = fTable.getColumns();
+            for (int i = 0; i < tableColumns.length; i++) {
+                final TableColumn column = tableColumns[i];
+                packSingleColumn(i, column);
+            }
+        } finally {
+            // Make sure that redraw is always enabled.
+            fTable.setRedraw(true);
+        }
+        fPackDone = true;
+    }
+
+
+    private void packMarginColumn() {
+        TableColumn[] columns = fTable.getColumns();
+        if (columns.length > 0) {
+            packSingleColumn(0, columns[0]);
+        }
+    }
+
+    private void packSingleColumn(int i, final TableColumn column) {
+        final int headerWidth = column.getWidth();
+        column.pack();
+        // Workaround for Linux which doesn't consider the image width of
+        // search/filter row in TableColumn.pack() after having executed
+        // TableItem.setImage((Image)null) for other rows than search/filter row.
+        boolean isCollapseFilter = fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter;
+        if (IS_LINUX && (i == 0) && isCollapseFilter) {
+            column.setWidth(column.getWidth() + SEARCH_IMAGE.getBounds().width);
+        }
+
+        if (column.getWidth() < headerWidth) {
+            column.setWidth(headerWidth);
+        }
+    }
+
+    /**
+     * Get the array of item strings (e.g., what to display in each cell of the
+     * table row) corresponding to the columns and trace event passed in
+     * parameter. The order of the Strings in the returned array will correspond
+     * to the iteration order of 'columns'.
+     *
+     * <p>
+     * To ensure consistent results, make sure only call this within a scope
+     * synchronized on 'columns'! If the order of 'columns' changes right after
+     * this method is called, the returned value won't be ordered correctly
+     * anymore.
+     */
+    private static String[] getItemStrings(List<TmfEventTableColumn> columns, ITmfEvent event) {
+        if (event == null) {
+            return EMPTY_STRING_ARRAY;
+        }
+        synchronized (columns) {
+            List<String> itemStrings = new ArrayList<>(columns.size());
+            for (TmfEventTableColumn column : columns) {
+                ITmfEvent passedEvent = event;
+                if (!(column instanceof TmfMarginColumn) && (event instanceof CachedEvent)) {
+                    // Make sure that the event object from the trace is passed
+                    // to all columns but the TmfMarginColumn
+                    passedEvent = ((CachedEvent) event).event;
+                }
+                if (passedEvent == null) {
+                    itemStrings.add(EMPTY_STRING);
+                } else {
+                    itemStrings.add(column.getItemString(passedEvent));
+                }
+
+            }
+            return itemStrings.toArray(new String[0]);
+        }
+    }
+
+    /**
+     * Get the contents of the row in the events table corresponding to an
+     * event. The order of the elements corresponds to the current order of the
+     * columns.
+     *
+     * @param event
+     *            The event printed in this row
+     * @return The event row entries
+     * @since 3.0
+     */
+    public String[] getItemStrings(ITmfEvent event) {
+        return getItemStrings(fColumns, event);
+    }
+
+    /**
+     * Notify this table that is got the UI focus.
+     */
+    public void setFocus() {
+        fTable.setFocus();
+    }
+
+    /**
+     * Assign a new trace to this event table.
+     *
+     * @param trace
+     *            The trace to assign to this event table
+     * @param disposeOnClose
+     *            true if the trace should be disposed when the table is
+     *            disposed
+     */
+    public void setTrace(final ITmfTrace trace, final boolean disposeOnClose) {
+        if ((fTrace != null) && fDisposeOnClose) {
+            fTrace.dispose();
+        }
+        fTrace = trace;
+        fPackDone = false;
+        fSelectedRank = 0;
+        fDisposeOnClose = disposeOnClose;
+
+        // Perform the updates on the UI thread
+        fTable.getDisplay().syncExec(new Runnable() {
+            @Override
+            public void run() {
+                fTable.removeAll();
+                fCache.setTrace(fTrace); // Clear the cache
+                if (fTrace != null) {
+                    if (!fTable.isDisposed() && (fTrace != null)) {
+                        if (fTable.getData(Key.FILTER_OBJ) == null) {
+                            fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
+                        } else {
+                            stopFilterThread();
+                            fFilterMatchCount = 0;
+                            fFilterCheckCount = 0;
+                            fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
+                            startFilterThread();
+                        }
+                    }
+                }
+                fRawViewer.setTrace(fTrace);
+            }
+        });
+    }
+
+    /**
+     * Assign the status line manager
+     *
+     * @param statusLineManager
+     *            The status line manager, or null to disable status line messages
+     * @since 2.1
+     */
+    public void setStatusLineManager(IStatusLineManager statusLineManager) {
+        if (fStatusLineManager != null && statusLineManager == null) {
+            fStatusLineManager.setMessage(EMPTY_STRING);
+        }
+        fStatusLineManager = statusLineManager;
+    }
+
+    private void updateStatusLine(ITmfTimestamp delta) {
+        if (fStatusLineManager != null) {
+            if (delta != null) {
+                fStatusLineManager.setMessage("\u0394: " + delta); //$NON-NLS-1$
+            } else {
+                fStatusLineManager.setMessage(null);
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Event cache
+    // ------------------------------------------------------------------------
+
+    /**
+     * Notify that the event cache has been updated
+     *
+     * @param completed
+     *            Also notify if the populating of the cache is complete, or
+     *            not.
+     */
+    public void cacheUpdated(final boolean completed) {
+        synchronized (fCacheUpdateSyncObj) {
+            if (fCacheUpdateBusy) {
+                fCacheUpdatePending = true;
+                fCacheUpdateCompleted = completed;
+                return;
+            }
+            fCacheUpdateBusy = true;
+        }
+        // Event cache is now updated. Perform update on the UI thread
+        if (!fTable.isDisposed()) {
+            fTable.getDisplay().asyncExec(new Runnable() {
+                @Override
+                public void run() {
+                    if (!fTable.isDisposed()) {
+                        fTable.refresh();
+                        packColumns();
+                    }
+                    if (completed) {
+                        populateCompleted();
+                    }
+                    synchronized (fCacheUpdateSyncObj) {
+                        fCacheUpdateBusy = false;
+                        if (fCacheUpdatePending) {
+                            fCacheUpdatePending = false;
+                            cacheUpdated(fCacheUpdateCompleted);
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * Callback for when populating the table is complete.
+     */
+    protected void populateCompleted() {
+        // Nothing by default;
+    }
+
+    // ------------------------------------------------------------------------
+    // ISelectionProvider
+    // ------------------------------------------------------------------------
+
+    /**
+     * @since 2.0
+     */
+    @Override
+    public void addSelectionChangedListener(ISelectionChangedListener listener) {
+        selectionChangedListeners.add(listener);
+    }
+
+    /**
+     * @since 2.0
+     */
+    @Override
+    public ISelection getSelection() {
+        if (fTable == null || fTable.isDisposed()) {
+            return StructuredSelection.EMPTY;
+        }
+        List<Object> list = new ArrayList<>(fTable.getSelection().length);
+        for (TableItem item : fTable.getSelection()) {
+            if (item.getData() != null) {
+                list.add(item.getData());
+            }
+        }
+        return new StructuredSelection(list);
+    }
+
+    /**
+     * @since 2.0
+     */
+    @Override
+    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+        selectionChangedListeners.remove(listener);
+    }
+
+    /**
+     * @since 2.0
+     */
+    @Override
+    public void setSelection(ISelection selection) {
+        // not implemented
+    }
+
+    /**
+     * Notifies any selection changed listeners that the viewer's selection has changed.
+     * Only listeners registered at the time this method is called are notified.
+     *
+     * @param event a selection changed event
+     *
+     * @see ISelectionChangedListener#selectionChanged
+     * @since 2.0
+     */
+    protected void fireSelectionChanged(final SelectionChangedEvent event) {
+        Object[] listeners = selectionChangedListeners.getListeners();
+        for (int i = 0; i < listeners.length; ++i) {
+            final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
+            SafeRunnable.run(new SafeRunnable() {
+                @Override
+                public void run() {
+                    l.selectionChanged(event);
+                }
+            });
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Bookmark handling
+    // ------------------------------------------------------------------------
+
+    /**
+     * Add a bookmark to this event table.
+     *
+     * @param bookmarksFile
+     *            The file to use for the bookmarks
+     */
+    public void addBookmark(final IFile bookmarksFile) {
+        fBookmarksFile = bookmarksFile;
+        final TableItem[] selection = fTable.getSelection();
+        if (selection.length > 0) {
+            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$
+                    }
+                    defaultMessage.append(tableItem.getText(i));
+                }
+                final InputDialog dialog = new MultiLineInputDialog(
+                        PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
+                        Messages.TmfEventsTable_AddBookmarkDialogTitle,
+                        Messages.TmfEventsTable_AddBookmarkDialogMessage,
+                        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();
+                        }
+                    } catch (final CoreException e) {
+                        displayException(e);
+                    }
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Remove a bookmark from this event table.
+     *
+     * @param bookmark
+     *            The bookmark to remove
+     */
+    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;
+            }
+        }
+    }
+
+    private void toggleBookmark(final Long rank) {
+        if (fBookmarksFile == null) {
+            return;
+        }
+        if (fBookmarksMap.containsKey(rank)) {
+            final Collection<Long> markerIds = fBookmarksMap.removeAll(rank);
+            fTable.refresh();
+            try {
+                for (long markerId : markerIds) {
+                    final IMarker bookmark = fBookmarksFile.findMarker(markerId);
+                    if (bookmark != null) {
+                        bookmark.delete();
+                    }
+                }
+            } catch (final CoreException e) {
+                displayException(e);
+            }
+        } else {
+            addBookmark(fBookmarksFile);
+        }
+    }
+
+    /**
+     * Refresh the bookmarks assigned to this trace, from the contents of a
+     * bookmark file.
+     *
+     * @param bookmarksFile
+     *            The bookmark file to use
+     */
+    public void refreshBookmarks(final IFile bookmarksFile) {
+        fBookmarksFile = bookmarksFile;
+        if (bookmarksFile == null) {
+            fBookmarksMap.clear();
+            fTable.refresh();
+            return;
+        }
+        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();
+        } catch (final CoreException e) {
+            displayException(e);
+        }
+    }
+
+    @Override
+    public void gotoMarker(final IMarker marker) {
+        final int rank = marker.getAttribute(IMarker.LOCATION, -1);
+        if (rank != -1) {
+            int index = rank;
+            if (fTable.getData(Key.FILTER_OBJ) != null) {
+                index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
+            } else if (rank >= fTable.getItemCount()) {
+                fPendingGotoRank = rank;
+            }
+            fSelectedRank = rank;
+            fTable.setSelection(index + 1); // +1 for header row
+            updateStatusLine(null);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Listeners
+    // ------------------------------------------------------------------------
+
+    @Override
+    public void colorSettingsChanged(final ColorSetting[] colorSettings) {
+        fTable.refresh();
+    }
+
+    // ------------------------------------------------------------------------
+    // Signal handlers
+    // ------------------------------------------------------------------------
+
+    /**
+     * Handler for the trace updated signal
+     *
+     * @param signal
+     *            The incoming signal
+     */
+    @TmfSignalHandler
+    public void traceUpdated(final TmfTraceUpdatedSignal signal) {
+        if ((signal.getTrace() != fTrace) || fTable.isDisposed()) {
+            return;
+        }
+        // Perform the refresh on the UI thread
+        Display.getDefault().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (!fTable.isDisposed() && (fTrace != null)) {
+                    if (fTable.getData(Key.FILTER_OBJ) == null) {
+                        fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
+                        if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row
+                            fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row
+                            fPendingGotoRank = -1;
+                            updateStatusLine(null);
+                        }
+                    } else {
+                        startFilterThread();
+                    }
+                }
+                if (!fRawViewer.isDisposed() && (fTrace != null)) {
+                    fRawViewer.refreshEventCount();
+                }
+            }
+        });
+    }
+
+    /**
+     * Handler for the time synch signal.
+     *
+     * @param signal
+     *            The incoming signal
+     */
+    @TmfSignalHandler
+    public void currentTimeUpdated(final TmfTimeSynchSignal 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) {
+
+                TmfTimestamp ts = new TmfTimestamp(signal.getBeginTime());
+
+                @Override
+                public void handleData(final ITmfEvent event) {
+                    super.handleData(event);
+                }
+
+                @Override
+                public void handleCompleted() {
+                    super.handleCompleted();
+                    if (fTrace == null) {
+                        return;
+                    }
+
+                    // Verify if the event is within the trace range and adjust if necessary
+                    ITmfTimestamp timestamp = ts;
+                    if (timestamp.compareTo(fTrace.getStartTime(), true) == -1) {
+                        timestamp = fTrace.getStartTime();
+                    }
+                    if (timestamp.compareTo(fTrace.getEndTime(), true) == 1) {
+                        timestamp = fTrace.getEndTime();
+                    }
+
+                    // Get the rank of the selected event in the table
+                    final ITmfContext context = fTrace.seekEvent(timestamp);
+                    final long rank = context.getRank();
+                    context.dispose();
+                    fSelectedRank = rank;
+
+                    fTable.getDisplay().asyncExec(new Runnable() {
+                        @Override
+                        public void run() {
+                            // Return if table is disposed
+                            if (fTable.isDisposed()) {
+                                return;
+                            }
+
+                            int index = (int) rank;
+                            if (fTable.isDisposed()) {
+                                return;
+                            }
+                            if (fTable.getData(Key.FILTER_OBJ) != null) {
+                                index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
+                            }
+                            fTable.setSelection(index + 1); // +1 for header row
+                            fRawViewer.selectAndReveal(rank);
+                            updateStatusLine(null);
+                        }
+                    });
+                }
+            };
+
+            ((ITmfEventProvider) fTrace).sendRequest(subRequest);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Error handling
+    // ------------------------------------------------------------------------
+
+    /**
+     * Display an exception in a message box
+     *
+     * @param e the exception
+     */
+    private static void displayException(final Exception e) {
+        final MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
+        mb.setText(e.getClass().getSimpleName());
+        mb.setMessage(e.getMessage());
+        mb.open();
+    }
+
+    /**
+     * @since 2.0
+     */
+    public void refresh() {
+        fCache.clear();
+        fTable.refresh();
+        fTable.redraw();
+    }
+
+    /**
+    * Margin column for images and special text (e.g. collapse count)
+    */
+   private static final class TmfMarginColumn extends TmfEventTableColumn {
+
+       private static final @NonNull String HEADER = EMPTY_STRING;
+
+       /**
+        * Constructor
+        */
+       public TmfMarginColumn() {
+           super(HEADER);
+       }
+
+       @Override
+       public String getItemString(ITmfEvent event) {
+           if (!(event instanceof CachedEvent) || ((CachedEvent) event).repeatCount == 0) {
+               return EMPTY_STRING;
+           }
+           return "+" + ((CachedEvent) event).repeatCount; //$NON-NLS-1$
+       }
+
+       @Override
+       public String getFilterFieldId() {
+           return null;
+       }
+   }
+
+}
This page took 0.100369 seconds and 5 git commands to generate.