tmf: make ID of views NonNull annotated
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / viewers / events / TmfEventsTable.java
index d1d2881221f619c49189cd3723953fcc244bf93a..e762d54ccadab6fb4e5e315dcf84928ea393eb39 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
  *   Patrick Tasse - Factored out from events view
  *   Francois Chouinard - Replaced Table by TmfVirtualTable
  *   Patrick Tasse - 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
  *******************************************************************************/
 
 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.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;
+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.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;
@@ -43,30 +61,44 @@ 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.ui.Activator;
 import org.eclipse.linuxtools.internal.tmf.ui.Messages;
-import org.eclipse.linuxtools.tmf.core.component.ITmfDataProvider;
+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.ITmfEventField;
-import org.eclipse.linuxtools.tmf.core.event.ITmfTimestamp;
-import org.eclipse.linuxtools.tmf.core.event.TmfEventField;
-import org.eclipse.linuxtools.tmf.core.event.TmfTimestamp;
+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.signal.TmfExperimentUpdatedSignal;
+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.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.location.ITmfLocation;
 import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
 import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting;
 import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSettingsManager;
@@ -105,10 +137,19 @@ 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.Multimap;
+
 /**
  * The generic TMF Events table
  *
@@ -117,9 +158,15 @@ import org.eclipse.ui.themes.ColorUtil;
  * @version 1.0
  * @author Francois Chouinard
  * @author Patrick Tasse
+ * @since 2.0
  */
-public class TmfEventsTable extends TmfComponent implements IGotoMarker,
-        IColorSettingsListener, ITmfEventsFilterProvider {
+public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorSettingsListener, ISelectionProvider {
+
+    /**
+     * Empty string array, used by {@link #getItemStrings}.
+     * @since 3.0
+     */
+    protected static final String[] EMPTY_STRING_ARRAY = new String[0];
 
     private static final Image BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(
             "icons/elcl16/bookmark_obj.gif"); //$NON-NLS-1$
@@ -154,7 +201,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         /** Filter object */
         String FILTER_OBJ = "$fltr_obj"; //$NON-NLS-1$
 
-        /** Timestamp*/
+        /** Timestamp */
         String TIMESTAMP = "$time"; //$NON-NLS-1$
 
         /** Rank */
@@ -190,42 +237,52 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
     // Table data
     // ------------------------------------------------------------------------
 
-    protected Composite fComposite;
-    protected SashForm fSashForm;
+    /** The virtual event table */
     protected TmfVirtualTable fTable;
-    protected TmfRawEventViewer fRawViewer;
-    protected ITmfTrace fTrace;
-    protected boolean fPackDone = false;
-    protected HeaderState fHeaderState = HeaderState.SEARCH;
-    protected long fSelectedRank = 0;
+
+    private Composite fComposite;
+    private SashForm fSashForm;
+    private TmfRawEventViewer fRawViewer;
+    private ITmfTrace fTrace;
+    private boolean fPackDone = false;
+    private HeaderState fHeaderState = HeaderState.SEARCH;
+    private long fSelectedRank = 0;
+    private ITmfTimestamp fSelectedBeginTimestamp = null;
+    private IStatusLineManager fStatusLineManager = null;
 
     // Filter data
-    protected long fFilterMatchCount;
-    protected long fFilterCheckCount;
-    protected FilterThread fFilterThread;
-    protected boolean fFilterThreadResume = false;
-    protected final Object fFilterSyncObj = new Object();
-    protected SearchThread fSearchThread;
-    protected final Object fSearchSyncObj = new Object();
-    protected List<ITmfEventsFilterListener> fEventsFilterListeners = new ArrayList<ITmfEventsFilterListener>();
+    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>
-    protected Map<Long, Long> fBookmarksMap = new HashMap<Long, Long>();
-    protected IFile fBookmarksFile;
-    protected long fPendingGotoRank = -1;
+    private Multimap<Long, Long> fBookmarksMap = HashMultimap.create();
+    private IFile fBookmarksFile;
+    private long fPendingGotoRank = -1;
 
     // SWT resources
-    protected LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());
-    protected Color fGrayColor;
-    protected Color fGreenColor;
-    protected Font fBoldFont;
+    private LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());
+    private Color fGrayColor;
+    private Color fGreenColor;
+    private Font fBoldFont;
 
     // Table column names
-    static private final String[] COLUMN_NAMES = new String[] { Messages.TmfEventsTable_TimestampColumnHeader,
+    private static final String[] COLUMN_NAMES = new String[] { Messages.TmfEventsTable_TimestampColumnHeader,
         Messages.TmfEventsTable_SourceColumnHeader, Messages.TmfEventsTable_TypeColumnHeader,
         Messages.TmfEventsTable_ReferenceColumnHeader, Messages.TmfEventsTable_ContentColumnHeader };
 
-    static private final ColumnData[] COLUMN_DATA = new ColumnData[] { new ColumnData(COLUMN_NAMES[0], 100, SWT.LEFT),
+    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) };
 
@@ -239,7 +296,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
     private boolean fDisposeOnClose;
 
     // ------------------------------------------------------------------------
-    // Constructor
+    // Constructors
     // ------------------------------------------------------------------------
 
     /**
@@ -278,7 +335,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         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
@@ -311,26 +368,48 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         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;
                         }
                     }
                 }
+                if (e.item.getData() != null) {
+                    fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(e.item.getData())));
+                } else {
+                    fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
+                }
             }
         });
 
-        cacheSize = Math.max(cacheSize, Display.getDefault().getBounds().height / fTable.getItemHeight());
-        cacheSize = Math.min(cacheSize, MAX_CACHE_SIZE);
-        fCache = new TmfEventsCache(cacheSize, this);
+        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() {
@@ -434,7 +513,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                         }
                         Point pt = fTable.toDisplay(event.x, y);
                         pt.x += BOOKMARK_IMAGE.getBounds().width;
-                        pt.y += size.y;
+                        pt.y += item.getBounds().height;
                         tooltipShell.setBounds(pt.x, pt.y, size.x, size.y);
                         tooltipShell.setVisible(true);
                         break;
@@ -483,6 +562,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                     }
                     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;
@@ -508,6 +588,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         createPopupMenu();
     }
 
+    /**
+     * Create a pop-up menu.
+     */
     protected void createPopupMenu() {
         final IAction showTableAction = new Action(Messages.TmfEventsTable_ShowTableActionText) {
             @Override
@@ -531,7 +614,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                 fRawViewer.setVisible(true);
                 fSashForm.layout();
                 final int index = fTable.getSelectionIndex();
-                if (index >= +1) {
+                if (index >= 1) {
                     fRawViewer.selectAndReveal(index - 1);
                 }
             }
@@ -545,6 +628,160 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
             }
         };
 
+        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("\\.\\./", ""); //$NON-NLS-1$ //$NON-NLS-2$
+                        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<>();
+                    StringBuilder header = new StringBuilder();
+                    boolean needTab = false;
+                    for (TableColumn tc: fTable.getColumns()) {
+                        if (needTab) {
+                            header.append('\t');
+                        }
+                        header.append(tc.getText());
+                        needTab = true;
+                    }
+                    Command command = cmdService.getCommand(ExportToTextCommandHandler.COMMAND_ID);
+                    ParameterizedCommand cmd = ParameterizedCommand.generateCommand(command,parameters);
+                    IEvaluationContext context = handlerService.getCurrentState();
+                    context.addVariable(ExportToTextCommandHandler.TMF_EVENT_TABLE_HEADER_ID, header.toString());
+                    context.addVariable(ExportToTextCommandHandler.TMF_EVENT_TABLE_PARAMETER_ID, TmfEventsTable.this);
+                    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() {
@@ -569,9 +806,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         };
 
         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;
             }
@@ -616,6 +853,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                         return;
                     }
                 }
+
                 // Right-click on table
                 if (fTable.isVisible() && fRawViewer.isVisible()) {
                     tablePopupMenu.add(hideTableAction);
@@ -625,7 +863,33 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                 } 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);
+                        }
+                    }
+                }
+
                 tablePopupMenu.add(clearFiltersAction);
                 final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();
                 if (savedFilters.length > 0) {
@@ -671,10 +935,25 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         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
     }
@@ -689,6 +968,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
             fTrace.dispose();
         }
         fResourceManager.dispose();
+        fRawViewer.dispose();
         super.dispose();
     }
 
@@ -720,26 +1000,38 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         fTable.setColumnHeaders(columnData);
     }
 
+    /**
+     * 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) {
-        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);
+        item.setText(getItemStrings(event));
+        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);
         }
@@ -765,18 +1057,24 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         }
 
         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);
         }
     }
 
+    /**
+     * 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) {
@@ -806,6 +1104,12 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         }
     }
 
+    /**
+     * 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 == 0) {
@@ -819,12 +1123,16 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                 item.setText(i, ""); //$NON-NLS-1$
             }
         }
+        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;
@@ -857,6 +1165,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                         } else if (fHeaderState == HeaderState.FILTER) {
                             fHeaderState = HeaderState.SEARCH;
                         }
+                        fTable.setSelection(0);
                         fTable.refresh();
                         return;
                     }
@@ -905,6 +1214,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                             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();
                             }
@@ -967,8 +1279,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                 if (fHeaderState == HeaderState.SEARCH) {
                     stopSearchThread();
                     final TmfFilterAndNode filter = new TmfFilterAndNode(null);
-                    for (final TableColumn column : fTable.getColumns()) {
-                        final Object filterObj = column.getData(Key.SEARCH_OBJ);
+                    for (final TableColumn col : fTable.getColumns()) {
+                        final Object filterObj = col.getData(Key.SEARCH_OBJ);
                         if (filterObj instanceof ITmfFilterTreeNode) {
                             filter.addChild((ITmfFilterTreeNode) filterObj);
                         }
@@ -985,8 +1297,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                     }
                 } else if (fHeaderState == HeaderState.FILTER) {
                     final TmfFilterAndNode filter = new TmfFilterAndNode(null);
-                    for (final TableColumn column : fTable.getColumns()) {
-                        final Object filterObj = column.getData(Key.FILTER_OBJ);
+                    for (final TableColumn col : fTable.getColumns()) {
+                        final Object filterObj = col.getData(Key.FILTER_OBJ);
                         if (filterObj instanceof ITmfFilterTreeNode) {
                             filter.addChild((ITmfFilterTreeNode) filterObj);
                         }
@@ -1034,18 +1346,29 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         });
     }
 
+    /**
+     * Send an event indicating a filter has been applied.
+     *
+     * @param filter
+     *            The filter that was just applied
+     */
     protected void fireFilterApplied(final ITmfFilter filter) {
-        for (final ITmfEventsFilterListener listener : fEventsFilterListeners) {
-            listener.filterApplied(filter, fTrace);
-        }
+        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) {
-        for (final ITmfEventsFilterListener listener : fEventsFilterListeners) {
-            listener.searchApplied(filter, fTrace);
-        }
+        broadcast(new TmfEventSearchAppliedSignal(this, fTrace, filter));
     }
 
+    /**
+     * Start the filtering thread.
+     */
     protected void startFilterThread() {
         synchronized (fFilterSyncObj) {
             final ITmfFilterTreeNode filter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
@@ -1062,6 +1385,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         }
     }
 
+    /**
+     * Stop the filtering thread.
+     */
     protected void stopFilterThread() {
         synchronized (fFilterSyncObj) {
             if (fFilterThread != null) {
@@ -1073,11 +1399,15 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
     }
 
     /**
+     * Apply a filter.
+     *
+     * @param filter
+     *            The filter to apply
      * @since 1.1
      */
     protected void applyFilter(ITmfFilter filter) {
-       stopFilterThread();
-       stopSearchThread();
+        stopFilterThread();
+        stopSearchThread();
         fFilterMatchCount = 0;
         fFilterCheckCount = 0;
         fCache.applyFilter(filter);
@@ -1088,6 +1418,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         fireFilterApplied(filter);
     }
 
+    /**
+     * Clear all currently active filters.
+     */
     protected void clearFilters() {
         if (fTable.getData(Key.FILTER_OBJ) == null) {
             return;
@@ -1114,15 +1447,25 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
             fTable.setSelection(0);
         }
         fireFilterApplied(null);
+        updateStatusLine(null);
     }
 
+    /**
+     * Wrapper Thread object for the filtering thread.
+     */
     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();
 
+        /**
+         * Constructor.
+         *
+         * @param filter
+         *            The filter this thread will be processing
+         */
         public FilterThread(final ITmfFilterTreeNode filter) {
             super("Filter Thread"); //$NON-NLS-1$
             this.filter = filter;
@@ -1137,8 +1480,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
             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);
@@ -1149,7 +1492,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                         final long rank = fFilterCheckCount;
                         final int index = (int) fFilterMatchCount;
                         fFilterMatchCount++;
-                        fCache.storeEvent(event.clone(), rank, index);
+                        fCache.storeEvent(event, rank, index);
                         refreshTable();
                     } else if ((fFilterCheckCount % 100) == 0) {
                         refreshTable();
@@ -1157,7 +1500,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                     fFilterCheckCount++;
                 }
             };
-            ((ITmfDataProvider) fTrace).sendRequest(request);
+            ((ITmfEventProvider) fTrace).sendRequest(request);
             try {
                 request.waitForCompletion();
             } catch (final InterruptedException e) {
@@ -1173,6 +1516,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
             }
         }
 
+        /**
+         * Refresh the filter.
+         */
         public void refreshTable() {
             synchronized (syncObj) {
                 if (refreshBusy) {
@@ -1203,6 +1549,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
             });
         }
 
+        /**
+         * Cancel this filtering thread.
+         */
         public void cancel() {
             if (request != null) {
                 request.cancel();
@@ -1210,6 +1559,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         }
     }
 
+    /**
+     * Go to the next item of a search.
+     */
     protected void searchNext() {
         synchronized (fSearchSyncObj) {
             if (fSearchThread != null) {
@@ -1228,8 +1580,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                 startIndex = Math.max(0, fTable.getTopIndex() - 1); // -1 for header row
             }
             final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
-            if (eventFilter != null)
-             {
+            if (eventFilter != null) {
                 startIndex = Math.max(0, startIndex - 1); // -1 for top filter status row
             }
             fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.FORWARD);
@@ -1237,6 +1588,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         }
     }
 
+    /**
+     * Go to the previous item of a search.
+     */
     protected void searchPrevious() {
         synchronized (fSearchSyncObj) {
             if (fSearchThread != null) {
@@ -1255,8 +1609,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                 startIndex = fTable.getTopIndex() - 2; // -1 for header row, -1 for previous event
             }
             final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
-            if (eventFilter != null)
-             {
+            if (eventFilter != null) {
                 startIndex = startIndex - 1; // -1 for top filter status row
             }
             fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.BACKWARD);
@@ -1264,6 +1617,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         }
     }
 
+    /**
+     * Stop the search thread.
+     */
     protected void stopSearchThread() {
         fPendingGotoRank = -1;
         synchronized (fSearchSyncObj) {
@@ -1274,17 +1630,36 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         }
     }
 
+    /**
+     * Wrapper for the search thread.
+     */
     protected class SearchThread extends Job {
-        protected ITmfFilterTreeNode searchFilter;
-        protected ITmfFilterTreeNode eventFilter;
-        protected int startIndex;
-        protected int direction;
-        protected long rank;
-        protected long foundRank = -1;
-        protected TmfDataRequest request;
+
+        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;
 
-        public SearchThread(final ITmfFilterTreeNode searchFilter, final ITmfFilterTreeNode eventFilter, final int startIndex,
+        /**
+         * 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;
@@ -1344,7 +1719,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                 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
@@ -1361,7 +1737,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                         currentRank++;
                     }
                 };
-                ((ITmfDataProvider) fTrace).sendRequest(request);
+                ((ITmfEventProvider) fTrace).sendRequest(request);
                 try {
                     request.waitForCompletion();
                     if (request.isCancelled()) {
@@ -1420,9 +1796,11 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                     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;
@@ -1437,6 +1815,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         }
     }
 
+    /**
+     * Create the resources.
+     */
     protected void createResources() {
         fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable
                 .getForeground().getRGB()));
@@ -1444,44 +1825,61 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         fBoldFont = fResourceManager.createFont(FontDescriptor.createFrom(fTable.getFont()).setStyle(SWT.BOLD));
     }
 
+    /**
+     * Pack the columns.
+     */
     protected void packColumns() {
         if (fPackDone) {
             return;
         }
-        for (final TableColumn column : fTable.getColumns()) {
+        fTable.setRedraw(false);
+
+        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);
+            }
+
             if (column.getWidth() < headerWidth) {
                 column.setWidth(headerWidth);
             }
         }
+
+        fTable.setRedraw(true);
         fPackDone = true;
     }
 
     /**
-     * @param event
-     * @return
+     * Get the contents of the row in the events table corresponding to an
+     * event. The order of the elements corresponds to the order of the columns.
      *
-     *         FIXME: Add support for column selection
+     * TODO Use column IDs, not indexes, so that the column order can be
+     * re-arranged.
+     *
+     * @param event
+     *            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 ITmfEventField content = event.getContent();
-            final String value = (content.getValue() != null) ? content.getValue().toString() : content.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, value)
-            };
+    public String[] getItemStrings(ITmfEvent event) {
+        if (event == null) {
+            return EMPTY_STRING_ARRAY;
         }
-        return fields;
+        return new String[] {
+                event.getTimestamp().toString(),
+                event.getSource(),
+                event.getType().getName(),
+                event.getReference(),
+                event.getContent().toString()
+        };
     }
 
     /**
@@ -1533,6 +1931,30 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         });
     }
 
+    /**
+     * 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(""); //$NON-NLS-1$
+        }
+        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
     // ------------------------------------------------------------------------
@@ -1577,10 +1999,80 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
         }
     }
 
+    /**
+     * 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
     // ------------------------------------------------------------------------
@@ -1599,24 +2091,25 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
             if (tableItem.getData(Key.RANK) != null) {
                 final StringBuffer defaultMessage = new StringBuffer();
                 for (int i = 0; i < fTable.getColumns().length; i++) {
-                    if (i > 0)
-                     {
+                    if (i > 0) {
                         defaultMessage.append(", "); //$NON-NLS-1$
                     }
                     defaultMessage.append(tableItem.getText(i));
                 }
-                final InputDialog dialog = new InputDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
-                        Messages.TmfEventsTable_AddBookmarkDialogTitle, Messages.TmfEventsTable_AddBookmarkDialogText,
-                        defaultMessage.toString(), null);
+                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 = (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();
                         }
@@ -1636,26 +2129,28 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
      *            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);
@@ -1704,7 +2199,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
             } else if (rank >= fTable.getItemCount()) {
                 fPendingGotoRank = rank;
             }
+            fSelectedRank = rank;
             fTable.setSelection(index + 1); // +1 for header row
+            updateStatusLine(null);
         }
     }
 
@@ -1712,65 +2209,15 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
     // 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();
     }
 
-    @Override
-    public void addEventsFilterListener(final ITmfEventsFilterListener listener) {
-        if (!fEventsFilterListeners.contains(listener)) {
-            fEventsFilterListeners.add(listener);
-        }
-    }
-
-    @Override
-    public void removeEventsFilterListener(final ITmfEventsFilterListener listener) {
-        fEventsFilterListeners.remove(listener);
-    }
-
     // ------------------------------------------------------------------------
     // Signal handlers
     // ------------------------------------------------------------------------
 
-    /**
-     * Handler for the experiment updated signal.
-     *
-     * @param signal
-     *            The incoming signal
-     */
-    @TmfSignalHandler
-    public void experimentUpdated(final TmfExperimentUpdatedSignal signal) {
-        if ((signal.getExperiment() != 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;
-                        }
-                    } else {
-                        startFilterThread();
-                    }
-                }
-                if (!fRawViewer.isDisposed() && (fTrace != null)) {
-                    fRawViewer.refreshEventCount();
-                }
-            }
-        });
-    }
-
     /**
      * Handler for the trace updated signal
      *
@@ -1792,6 +2239,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                         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();
@@ -1817,9 +2265,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
             // 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) {
@@ -1845,6 +2294,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                     // 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() {
@@ -1859,18 +2309,18 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
                             if (fTable.isDisposed()) {
                                 return;
                             }
-                            if (fTable.getData(Key.FILTER_OBJ) != null)
-                             {
+                            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);
                         }
                     });
                 }
             };
 
-            ((ITmfDataProvider) fTrace).sendRequest(subRequest);
+            ((ITmfEventProvider) fTrace).sendRequest(subRequest);
         }
     }
 
@@ -1885,9 +2335,17 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker,
      */
     private static void displayException(final Exception e) {
         final MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
-        mb.setText(e.getClass().getName());
+        mb.setText(e.getClass().getSimpleName());
         mb.setMessage(e.getMessage());
         mb.open();
     }
 
+    /**
+     * @since 2.0
+     */
+    public void refresh() {
+        fCache.clear();
+        fTable.refresh();
+        fTable.redraw();
+    }
 }
This page took 0.039858 seconds and 5 git commands to generate.