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 6ee25c6ee091bac32e0545689607a278f6c73aa6..6523a7cf4b294acd62b48f16a0cb9ac568960733 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010, 2011, 2012 Ericsson
+ * 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
@@ -7,25 +7,33 @@
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
- *   Francois Chouinard - Initial API and implementation
- *   Patrick Tasse - Factored out from events view
- *   Francois Chouinard - Replaced Table by TmfVirtualTable
- *   Patrick Tasse - Filter implementation (inspired by www.eclipse.org/mat)
+ *   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.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 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;
@@ -41,10 +49,12 @@ 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;
@@ -62,41 +72,46 @@ 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.ITmfDataProvider;
+import org.eclipse.linuxtools.tmf.core.component.ITmfEventProvider;
 import org.eclipse.linuxtools.tmf.core.component.TmfComponent;
-import org.eclipse.linuxtools.tmf.core.ctfadaptor.CtfConstants;
-import org.eclipse.linuxtools.tmf.core.ctfadaptor.CtfTmfCallsite;
-import org.eclipse.linuxtools.tmf.core.ctfadaptor.CtfTmfEvent;
 import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
-import org.eclipse.linuxtools.tmf.core.event.ITmfEventField;
-import org.eclipse.linuxtools.tmf.core.event.TmfEventField;
+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.ITmfDataRequest.ExecutionType;
-import org.eclipse.linuxtools.tmf.core.request.TmfDataRequest;
+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.ITmfLocation;
 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.ColumnData;
 import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.TmfVirtualTable;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.SashForm;
@@ -129,25 +144,44 @@ 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.PartInitException;
 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.
  *
- * @version 1.0
  * @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$
@@ -162,6 +196,23 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     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
      *
@@ -181,7 +232,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         /** Filter object */
         String FILTER_OBJ = "$fltr_obj"; //$NON-NLS-1$
 
-        /** Timestamp*/
+        /** Timestamp */
         String TIMESTAMP = "$time"; //$NON-NLS-1$
 
         /** Rank */
@@ -224,9 +275,11 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private SashForm fSashForm;
     private TmfRawEventViewer fRawViewer;
     private ITmfTrace fTrace;
-    private boolean fPackDone = false;
+    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;
@@ -245,7 +298,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private ListenerList selectionChangedListeners = new ListenerList();
 
     // Bookmark map <Rank, MarkerId>
-    private Map<Long, Long> fBookmarksMap = new HashMap<Long, Long>();
+    private Multimap<Long, Long> fBookmarksMap = HashMultimap.create();
     private IFile fBookmarksFile;
     private long fPendingGotoRank = -1;
 
@@ -255,14 +308,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private Color fGreenColor;
     private Font fBoldFont;
 
-    // Table column names
-    private static final String[] COLUMN_NAMES = new String[] { Messages.TmfEventsTable_TimestampColumnHeader,
-        Messages.TmfEventsTable_SourceColumnHeader, Messages.TmfEventsTable_TypeColumnHeader,
-        Messages.TmfEventsTable_ReferenceColumnHeader, Messages.TmfEventsTable_ContentColumnHeader };
-
-    private static final ColumnData[] COLUMN_DATA = new ColumnData[] { new ColumnData(COLUMN_NAMES[0], 100, SWT.LEFT),
-        new ColumnData(COLUMN_NAMES[1], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[2], 100, SWT.LEFT),
-        new ColumnData(COLUMN_NAMES[3], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[4], 100, SWT.LEFT) };
+    private final List<TmfEventTableColumn> fColumns = new LinkedList<>();
 
     // Event cache
     private final TmfEventsCache fCache;
@@ -278,7 +324,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     // ------------------------------------------------------------------------
 
     /**
-     * Basic constructor, will use default column data.
+     * Basic constructor, using the default set of columns
      *
      * @param parent
      *            The parent composite UI object
@@ -286,20 +332,63 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      *            The size of the event table cache
      */
     public TmfEventsTable(final Composite parent, final int cacheSize) {
-        this(parent, cacheSize, COLUMN_DATA);
+        this(parent, cacheSize, DEFAULT_COLUMNS);
     }
 
     /**
-     * Advanced constructor, where we also define which column data to use.
+     * 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
-     *            The column data to use for this table
+     *            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, final ColumnData[] columnData) {
+    public TmfEventsTable(final Composite parent, int cacheSize,
+            Collection<? extends TmfEventTableColumn> columns) {
         super("TmfEventsTable"); //$NON-NLS-1$
 
         fComposite = new Composite(parent, SWT.NONE);
@@ -313,7 +402,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
 
         // Create a virtual table
-        final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION;
+        final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION;
         fTable = new TmfVirtualTable(fSashForm, style);
 
         // Set the table layout
@@ -324,16 +413,24 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fTable.setHeaderVisible(true);
         fTable.setLinesVisible(true);
 
-        // Set the columns
-        setColumnHeaders(columnData);
+        // Setup the columns
+        if (columns != null) {
+            fColumns.addAll(columns);
+        }
+
+        TmfMarginColumn collapseCol = new TmfMarginColumn();
+        fColumns.add(MARGIN_COLUMN_INDEX, collapseCol);
 
-        // Set the default column field ids if this is not a subclass
-        if (Arrays.equals(columnData, COLUMN_DATA)) {
-            fTable.getColumns()[0].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TIMESTAMP);
-            fTable.getColumns()[1].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_SOURCE);
-            fTable.getColumns()[2].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TYPE);
-            fTable.getColumns()[3].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_REFERENCE);
-            fTable.getColumns()[4].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_CONTENT);
+        // 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
@@ -346,21 +443,43 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fTable.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(final SelectionEvent e) {
-                final TableItem[] selection = fTable.getSelection();
-                if (selection.length > 0) {
-                    final TableItem selectedTableItem = selection[0];
-                    if (selectedTableItem != null) {
-                        if (selectedTableItem.getData(Key.RANK) instanceof Long) {
-                            fSelectedRank = (Long) selectedTableItem.getData(Key.RANK);
-                            fRawViewer.selectAndReveal((Long) selectedTableItem.getData(Key.RANK));
+                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 (selectedTableItem.getData(Key.TIMESTAMP) instanceof TmfTimestamp) {
-                            final TmfTimestamp ts = (TmfTimestamp) selectedTableItem.getData(Key.TIMESTAMP);
-                            broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, 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;
                         }
                     }
                 }
-                fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, getSelection()));
+                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));
+                }
             }
         });
 
@@ -392,7 +511,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
                 final CachedEvent cachedEvent = fCache.getEvent(index);
                 if (cachedEvent != null) {
-                    setItemData(item, cachedEvent.event, cachedEvent.rank);
+                    setItemData(item, cachedEvent, cachedEvent.rank);
                     return;
                 }
 
@@ -452,7 +571,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                         layout.marginWidth = 2;
                         tooltipShell.setLayout(layout);
                         final Label label = new Label(tooltipShell, SWT.WRAP);
-                        String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : ""); //$NON-NLS-1$ //$NON-NLS-2$
+                        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);
@@ -465,7 +584,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                          * the method toDisplay() expects coordinates relative to an origin that includes the table header.
                          */
                         int y = event.y;
-                        if (System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
+                        if (IS_LINUX) {
                             y += fTable.getHeaderHeight();
                         }
                         Point pt = fTable.toDisplay(event.x, y);
@@ -519,6 +638,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     }
                     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;
@@ -544,6 +664,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         createPopupMenu();
     }
 
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
+
     /**
      * Create a pop-up menu.
      */
@@ -570,7 +694,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 fRawViewer.setVisible(true);
                 fSashForm.layout();
                 final int index = fTable.getSelectionIndex();
-                if (index >= +1) {
+                if (index >= 1) {
                     fRawViewer.selectAndReveal(index - 1);
                 }
             }
@@ -584,7 +708,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             }
         };
 
-        final IAction openCallsiteAction = new Action(Messages.TmfEventsTable_OpenCallsiteActionText) {
+        final IAction openCallsiteAction = new Action(Messages.TmfEventsTable_OpenSourceCodeActionText) {
             @Override
             public void run() {
                 final TableItem items[] = fTable.getSelection();
@@ -594,17 +718,17 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 final TableItem item = items[0];
 
                 final Object data = item.getData();
-                if (data instanceof CtfTmfEvent) {
-                    CtfTmfEvent event = (CtfTmfEvent) data;
-                    CtfTmfCallsite cs = event.getCallsite();
+                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("\\.\\./", ""); //$NON-NLS-1$ //$NON-NLS-2$
-                        final ArrayList<IFile> files = new ArrayList<IFile>();
+                        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 {
@@ -625,8 +749,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                                 }
                             });
                             dialog.setInput(files);
-                            dialog.setTitle(Messages.TmfEventsTable_OpenCallsiteSelectFileDialogTitle);
-                            dialog.setMessage(Messages.TmfEventsTable_OpenCallsiteSelectFileDialogTitle + '\n' + cs.toString());
+                            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) {
@@ -641,14 +765,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                             IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), marker);
                             marker.delete();
                         } else if (files.size() == 0){
-                            displayException(new FileNotFoundException('\'' + cs.toString() + '\'' + '\n' + Messages.TmfEventsTable_OpenCallsiteNotFound));
+                            displayException(new FileNotFoundException('\'' + cs.toString() + '\'' + '\n' + Messages.TmfEventsTable_OpenSourceCodeNotFound));
                         }
-                    } catch (PartInitException e) {
-                        // TODO Auto-generated catch block
-                        e.printStackTrace();
                     } catch (CoreException e) {
-                        // TODO Auto-generated catch block
-                        e.printStackTrace();
+                        displayException(e);
                     }
                 }
             }
@@ -665,8 +785,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 final TableItem item = items[0];
 
                 final Object eventData = item.getData();
-                if (eventData instanceof CtfTmfEvent) {
-                    String modelURI = ((CtfTmfEvent) eventData).getCustomAttribute(CtfConstants.MODEL_URI_KEY);
+                if (eventData instanceof ITmfModelLookup) {
+                    String modelURI = ((ITmfModelLookup) eventData).getModelUri();
 
                     if (modelURI != null) {
                         IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
@@ -707,6 +827,35 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             }
         };
 
+        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() {
@@ -730,10 +879,17 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             }
         };
 
+        final IAction collapseAction = new Action(Messages.TmfEventsTable_CollapseFilterMenuName) {
+            @Override
+            public void run() {
+                applyFilter(new TmfCollapseFilter());
+            }
+        };
+
         class ToggleBookmarkAction extends Action {
-            long fRank;
+            Long fRank;
 
-            public ToggleBookmarkAction(final String text, final long rank) {
+            public ToggleBookmarkAction(final String text, final Long rank) {
                 super(text);
                 fRank = rank;
             }
@@ -788,27 +944,51 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 } else if (!fRawViewer.isVisible()) {
                     tablePopupMenu.add(showRawAction);
                 }
+                tablePopupMenu.add(exportToTextAction);
                 tablePopupMenu.add(new Separator());
 
                 if (item != null) {
                     final Object data = item.getData();
-                    if (data instanceof CtfTmfEvent) {
-                        Separator separator = null;
-                        CtfTmfEvent event = (CtfTmfEvent) data;
+                    Separator separator = null;
+                    if (data instanceof ITmfSourceLookup) {
+                        ITmfSourceLookup event = (ITmfSourceLookup) data;
                         if (event.getCallsite() != null) {
                             tablePopupMenu.add(openCallsiteAction);
                             separator = new Separator();
                         }
-                        if (event.listCustomAttributes().contains(CtfConstants.MODEL_URI_KEY)) {
+                    }
+
+                    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) {
@@ -887,6 +1067,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             fTrace.dispose();
         }
         fResourceManager.dispose();
+        fRawViewer.dispose();
         super.dispose();
     }
 
@@ -911,11 +1092,13 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
     /**
      * @param columnData
-     *
-     * FIXME: Add support for column selection
+     *            columnData
+     * @deprecated The column headers are now set at the constructor, this
+     *             shouldn't be called anymore.
      */
-    protected void setColumnHeaders(final ColumnData[] columnData) {
-        fTable.setColumnHeaders(columnData);
+    @Deprecated
+    protected void setColumnHeaders(final org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData [] columnData) {
+        /* No-op */
     }
 
     /**
@@ -929,26 +1112,28 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      *            Which rank this event has in the trace/experiment
      */
     protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {
-        final ITmfEventField[] fields = extractItemFields(event);
-        final String[] content = new String[fields.length];
-        for (int i = 0; i < fields.length; i++) {
-            content[i] = fields[i].getValue() != null ? fields[i].getValue().toString() : ""; //$NON-NLS-1$
-        }
-        item.setText(content);
+        String[] itemStrings = getItemStrings(fColumns, event);
+        item.setText(itemStrings);
         item.setData(event);
         item.setData(Key.TIMESTAMP, new TmfTimestamp(event.getTimestamp()));
         item.setData(Key.RANK, rank);
 
-        boolean bookmark = false;
-        final Long markerId = fBookmarksMap.get(rank);
-        if (markerId != null) {
-            bookmark = true;
+        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 {
-                final IMarker marker = fBookmarksFile.findMarker(markerId);
-                item.setData(Key.BOOKMARK, marker.getAttribute(IMarker.MESSAGE));
-            } catch (final CoreException e) {
+                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);
         }
@@ -974,16 +1159,20 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         }
 
         if (searchMatch) {
-            if (bookmark) {
+            if (!markerIds.isEmpty()) {
                 item.setImage(SEARCH_MATCH_BOOKMARK_IMAGE);
             } else {
                 item.setImage(SEARCH_MATCH_IMAGE);
             }
-        } else if (bookmark) {
+        } 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();
+        }
     }
 
     /**
@@ -1002,7 +1191,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             txtKey = Key.FILTER_TXT;
         }
         item.setForeground(fGrayColor);
-        for (int i = 0; i < fTable.getColumns().length; i++) {
+        // 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) {
@@ -1029,15 +1219,18 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      */
     protected void setFilterStatusRowItemData(final TableItem item) {
         for (int i = 0; i < fTable.getColumns().length; i++) {
-            if (i == 0) {
+            if (i == MARGIN_COLUMN_INDEX) {
                 if ((fTrace == null) || (fFilterCheckCount == fTrace.getNbEvents())) {
                     item.setImage(FILTER_IMAGE);
                 } else {
                     item.setImage(STOP_IMAGE);
                 }
-                item.setText(0, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
+            }
+
+            if (i == FILTER_SUMMARY_INDEX) {
+                item.setText(FILTER_SUMMARY_INDEX, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
             } else {
-                item.setText(i, ""); //$NON-NLS-1$
+                item.setText(i, EMPTY_STRING);
             }
         }
         item.setData(null);
@@ -1082,6 +1275,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                         } else if (fHeaderState == HeaderState.FILTER) {
                             fHeaderState = HeaderState.SEARCH;
                         }
+                        fTable.setSelection(0);
                         fTable.refresh();
                         return;
                     }
@@ -1130,6 +1324,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                             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();
                             }
@@ -1360,6 +1557,11 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             fTable.setSelection(0);
         }
         fireFilterApplied(null);
+        updateStatusLine(null);
+
+        // Set original width
+        fTable.getColumns()[MARGIN_COLUMN_INDEX].setWidth(0);
+        packMarginColumn();
     }
 
     /**
@@ -1367,7 +1569,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      */
     protected class FilterThread extends Thread {
         private final ITmfFilterTreeNode filter;
-        private TmfDataRequest request;
+        private TmfEventRequest request;
         private boolean refreshBusy = false;
         private boolean refreshPending = false;
         private final Object syncObj = new Object();
@@ -1392,27 +1594,34 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             if (nbRequested <= 0) {
                 return;
             }
-            request = new TmfDataRequest(ITmfEvent.class, (int) fFilterCheckCount,
-                    nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {
+            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);
-                        refreshTable();
-                    } else if ((fFilterCheckCount % 100) == 0) {
+                        refresh = true;
+                    } else {
+                        if (filter instanceof TmfCollapseFilter) {
+                            fCache.updateCollapsedEvent((int) fFilterMatchCount - 1);
+                        }
+                    }
+
+                    if (refresh || (fFilterCheckCount % 100) == 0) {
                         refreshTable();
                     }
                     fFilterCheckCount++;
                 }
             };
-            ((ITmfDataProvider) fTrace).sendRequest(request);
+            ((ITmfEventProvider) fTrace).sendRequest(request);
             try {
                 request.waitForCompletion();
             } catch (final InterruptedException e) {
@@ -1553,7 +1762,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         private int direction;
         private long rank;
         private long foundRank = -1;
-        private TmfDataRequest request;
+        private TmfEventRequest request;
         private ITmfTimestamp foundTimestamp = null;
 
         /**
@@ -1631,7 +1840,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 if (direction == Direction.BACKWARD) {
                     rank = Math.max(0, rank - fTrace.getCacheSize() + 1);
                 }
-                request = new TmfDataRequest(ITmfEvent.class, (int) rank, nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {
+                request = new TmfEventRequest(ITmfEvent.class, TmfTimeRange.ETERNITY,
+                        (int) rank, nbRequested, ExecutionType.BACKGROUND) {
                     long currentRank = rank;
 
                     @Override
@@ -1648,7 +1858,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                         currentRank++;
                     }
                 };
-                ((ITmfDataProvider) fTrace).sendRequest(request);
+                ((ITmfEventProvider) fTrace).sendRequest(request);
                 try {
                     request.waitForCompletion();
                     if (request.isCancelled()) {
@@ -1711,6 +1921,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     synchronized (fSearchSyncObj) {
                         fSearchThread = null;
                     }
+                    updateStatusLine(null);
                 }
             });
             return Status.OK_STATUS;
@@ -1742,53 +1953,92 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         if (fPackDone) {
             return;
         }
-        boolean isLinux = System.getProperty("os.name").contains("Linux") ? true : false; //$NON-NLS-1$ //$NON-NLS-2$
-
-        TableColumn tableColumns[] = fTable.getColumns();
-        for (int i = 0; i < tableColumns.length; i++) {
-            final TableColumn column = tableColumns[i];
-            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.
-            if (isLinux && (i == 0)) {
-                column.setWidth(column.getWidth() + SEARCH_IMAGE.getBounds().width);
+        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));
+                }
 
-            if (column.getWidth() < headerWidth) {
-                column.setWidth(headerWidth);
             }
+            return itemStrings.toArray(new String[0]);
         }
-        fPackDone = true;
     }
 
     /**
-     * Extract the fields of an event (item in the table).
+     * 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 to extract from
-     * @return The array of fields
-     *
-     *         FIXME: Add support for column selection
+     *            The event printed in this row
+     * @return The event row entries
+     * @since 3.0
      */
-    protected ITmfEventField[] extractItemFields(final ITmfEvent event) {
-        ITmfEventField[] fields = new TmfEventField[0];
-        if (event != null) {
-            final String timestamp = event.getTimestamp().toString();
-            final String source = event.getSource();
-            final String type = event.getType().getName();
-            final String reference = event.getReference();
-            final String content = event.getContent().toString();
-            fields = new TmfEventField[] {
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_TIMESTAMP, timestamp),
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_SOURCE, source),
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_TYPE, type),
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_REFERENCE, reference),
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_CONTENT, content)
-            };
-        }
-        return fields;
+    public String[] getItemStrings(ITmfEvent event) {
+        return getItemStrings(fColumns, event);
     }
 
     /**
@@ -1840,6 +2090,30 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         });
     }
 
+    /**
+     * 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
     // ------------------------------------------------------------------------
@@ -1895,9 +2169,6 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     // ISelectionProvider
     // ------------------------------------------------------------------------
 
-    /* (non-Javadoc)
-     * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
-     */
     /**
      * @since 2.0
      */
@@ -1906,9 +2177,6 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         selectionChangedListeners.add(listener);
     }
 
-    /* (non-Javadoc)
-     * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
-     */
     /**
      * @since 2.0
      */
@@ -1917,7 +2185,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         if (fTable == null || fTable.isDisposed()) {
             return StructuredSelection.EMPTY;
         }
-        List<Object> list = new ArrayList<Object>(fTable.getSelection().length);
+        List<Object> list = new ArrayList<>(fTable.getSelection().length);
         for (TableItem item : fTable.getSelection()) {
             if (item.getData() != null) {
                 list.add(item.getData());
@@ -1926,9 +2194,6 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         return new StructuredSelection(list);
     }
 
-    /* (non-Javadoc)
-     * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
-     */
     /**
      * @since 2.0
      */
@@ -1937,9 +2202,6 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         selectionChangedListeners.remove(listener);
     }
 
-    /* (non-Javadoc)
-     * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
-     */
     /**
      * @since 2.0
      */
@@ -2004,9 +2266,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                         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 = (int) rank;
-                            bookmark.setAttribute(IMarker.LOCATION, (Integer) location);
+                            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();
                         }
@@ -2026,26 +2288,28 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      *            The bookmark to remove
      */
     public void removeBookmark(final IMarker bookmark) {
-        for (final Entry<Long, Long> entry : fBookmarksMap.entrySet()) {
+        for (final Entry<Long, Long> entry : fBookmarksMap.entries()) {
             if (entry.getValue().equals(bookmark.getId())) {
-                fBookmarksMap.remove(entry.getKey());
+                fBookmarksMap.remove(entry.getKey(), entry.getValue());
                 fTable.refresh();
                 return;
             }
         }
     }
 
-    private void toggleBookmark(final long rank) {
+    private void toggleBookmark(final Long rank) {
         if (fBookmarksFile == null) {
             return;
         }
         if (fBookmarksMap.containsKey(rank)) {
-            final Long markerId = fBookmarksMap.remove(rank);
+            final Collection<Long> markerIds = fBookmarksMap.removeAll(rank);
             fTable.refresh();
             try {
-                final IMarker bookmark = fBookmarksFile.findMarker(markerId);
-                if (bookmark != null) {
-                    bookmark.delete();
+                for (long markerId : markerIds) {
+                    final IMarker bookmark = fBookmarksFile.findMarker(markerId);
+                    if (bookmark != null) {
+                        bookmark.delete();
+                    }
                 }
             } catch (final CoreException e) {
                 displayException(e);
@@ -2096,6 +2360,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             }
             fSelectedRank = rank;
             fTable.setSelection(index + 1); // +1 for header row
+            updateStatusLine(null);
         }
     }
 
@@ -2103,11 +2368,6 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     // Listeners
     // ------------------------------------------------------------------------
 
-    /*
-     * (non-Javadoc)
-     *
-     * @see org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener#colorSettingsChanged(org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting[])
-     */
     @Override
     public void colorSettingsChanged(final ColorSetting[] colorSettings) {
         fTable.refresh();
@@ -2138,6 +2398,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                         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();
@@ -2163,9 +2424,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             // 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 TmfDataRequest subRequest = new TmfDataRequest(ITmfEvent.class, 0, 1, ExecutionType.FOREGROUND) {
+            final TmfEventRequest subRequest = new TmfEventRequest(ITmfEvent.class,
+                    TmfTimeRange.ETERNITY, 0, 1, ExecutionType.FOREGROUND) {
 
-                TmfTimestamp ts = new TmfTimestamp(signal.getCurrentTime());
+                TmfTimestamp ts = new TmfTimestamp(signal.getBeginTime());
 
                 @Override
                 public void handleData(final ITmfEvent event) {
@@ -2211,12 +2473,13 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                             }
                             fTable.setSelection(index + 1); // +1 for header row
                             fRawViewer.selectAndReveal(rank);
+                            updateStatusLine(null);
                         }
                     });
                 }
             };
 
-            ((ITmfDataProvider) fTrace).sendRequest(subRequest);
+            ((ITmfEventProvider) fTrace).sendRequest(subRequest);
         }
     }
 
@@ -2244,4 +2507,33 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         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.040999 seconds and 5 git commands to generate.