releng: Transition to jdt.annotation 2.0
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / viewers / events / TmfEventsTable.java
index 10c10e164237a1dbf040039a69d777ee01ea09c5..56bb78530a20ace7a11ebf0af627b91b9d8f7014 100644 (file)
@@ -20,6 +20,7 @@ package org.eclipse.tracecompass.tmf.ui.viewers.events;
 
 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
 
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
@@ -39,10 +40,13 @@ import org.eclipse.core.commands.NotHandledException;
 import org.eclipse.core.commands.ParameterizedCommand;
 import org.eclipse.core.commands.common.NotDefinedException;
 import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.IWorkspaceRunnable;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
@@ -62,13 +66,15 @@ import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.IStatusLineManager;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.Separator;
-import org.eclipse.jface.dialogs.InputDialog;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.resource.ColorRegistry;
 import org.eclipse.jface.resource.FontRegistry;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
 import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.jface.util.OpenStrategy;
 import org.eclipse.jface.util.PropertyChangeEvent;
@@ -81,6 +87,7 @@ import org.eclipse.jface.viewers.LabelProvider;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.window.Window;
+import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.SashForm;
 import org.eclipse.swt.custom.StyleRange;
@@ -121,7 +128,7 @@ import org.eclipse.tracecompass.internal.tmf.ui.Activator;
 import org.eclipse.tracecompass.internal.tmf.ui.Messages;
 import org.eclipse.tracecompass.internal.tmf.ui.commands.CopyToClipboardOperation;
 import org.eclipse.tracecompass.internal.tmf.ui.commands.ExportToTextCommandHandler;
-import org.eclipse.tracecompass.internal.tmf.ui.dialogs.MultiLineInputDialog;
+import org.eclipse.tracecompass.internal.tmf.ui.dialogs.AddBookmarkDialog;
 import org.eclipse.tracecompass.tmf.core.component.ITmfEventProvider;
 import org.eclipse.tracecompass.tmf.core.component.TmfComponent;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
@@ -138,6 +145,7 @@ import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterMatchesNode;
 import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode;
 import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType;
 import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
+import org.eclipse.tracecompass.tmf.core.resources.ITmfMarker;
 import org.eclipse.tracecompass.tmf.core.signal.TmfEventFilterAppliedSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfEventSearchAppliedSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfEventSelectedSignal;
@@ -145,6 +153,7 @@ import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceUpdatedSignal;
 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfNanoTimestamp;
 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
@@ -160,13 +169,17 @@ import org.eclipse.tracecompass.tmf.ui.views.colors.IColorSettingsListener;
 import org.eclipse.tracecompass.tmf.ui.views.filter.FilterManager;
 import org.eclipse.tracecompass.tmf.ui.widgets.rawviewer.TmfRawEventViewer;
 import org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.TmfVirtualTable;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IEditorPart;
 import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartSite;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.commands.ICommandService;
 import org.eclipse.ui.dialogs.ListDialog;
 import org.eclipse.ui.handlers.IHandlerService;
 import org.eclipse.ui.ide.IDE;
 import org.eclipse.ui.ide.IGotoMarker;
+import org.eclipse.ui.texteditor.ITextEditor;
 import org.eclipse.ui.themes.ColorUtil;
 import org.eclipse.ui.themes.IThemeManager;
 
@@ -188,7 +201,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     /**
      * Empty string array, used by {@link #getItemStrings}.
      */
-    protected static final @NonNull String[] EMPTY_STRING_ARRAY = new String[0];
+    protected static final String @NonNull [] EMPTY_STRING_ARRAY = new String[0];
 
     /**
      * Empty string
@@ -217,13 +230,361 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private static final int FILTER_SUMMARY_INDEX = 1;
     private static final int EVENT_COLUMNS_START_INDEX = MARGIN_COLUMN_INDEX + 1;
 
+    private final class ColumnMovedListener extends ControlAdapter {
+        /*
+         * Make sure that the margin column is always first and keep the
+         * column order variable up to date.
+         */
+        @Override
+        public void controlMoved(ControlEvent e) {
+            int[] order = fTable.getColumnOrder();
+            if (order[0] == MARGIN_COLUMN_INDEX) {
+                fColumnOrder = order;
+                return;
+            }
+            for (int i = order.length - 1; i > 0; i--) {
+                if (order[i] == MARGIN_COLUMN_INDEX) {
+                    order[i] = order[i - 1];
+                    order[i - 1] = MARGIN_COLUMN_INDEX;
+                }
+            }
+            fTable.setColumnOrder(order);
+            fColumnOrder = fTable.getColumnOrder();
+        }
+    }
+
+    private final class TableSelectionListener extends SelectionAdapter {
+        @Override
+        public void widgetSelected(final SelectionEvent e) {
+            if (e.item == null) {
+                return;
+            }
+            updateStatusLine(null);
+            if (fTable.getSelectionIndices().length > 0) {
+                if (e.item.getData(Key.RANK) instanceof Long) {
+                    fSelectedRank = (Long) e.item.getData(Key.RANK);
+                    fRawViewer.selectAndReveal((Long) e.item.getData(Key.RANK));
+                } else {
+                    fSelectedRank = -1;
+                }
+                if (fTable.getSelectionIndices().length == 1) {
+                    fSelectedBeginRank = fSelectedRank;
+                }
+                if (e.item.getData(Key.TIMESTAMP) instanceof ITmfTimestamp) {
+                    final ITmfTimestamp ts = NonNullUtils.checkNotNull((ITmfTimestamp) e.item.getData(Key.TIMESTAMP));
+                    if (fTable.getSelectionIndices().length == 1) {
+                        fSelectedBeginTimestamp = ts;
+                    }
+                    ITmfTimestamp selectedBeginTimestamp = fSelectedBeginTimestamp;
+                    if (selectedBeginTimestamp != null) {
+                        broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, selectedBeginTimestamp, ts));
+                        if (fTable.getSelectionIndices().length == 2) {
+                            updateStatusLine(ts.getDelta(selectedBeginTimestamp));
+                        }
+                    }
+                } else {
+                    if (fTable.getSelectionIndices().length == 1) {
+                        fSelectedBeginTimestamp = null;
+                    }
+                }
+            }
+            if (e.item.getData() instanceof ITmfEvent) {
+                broadcast(new TmfEventSelectedSignal(TmfEventsTable.this, (ITmfEvent) e.item.getData()));
+                fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(e.item.getData())));
+            } else {
+                fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
+            }
+        }
+    }
+
+    private final class MouseDoubleClickListener extends MouseAdapter {
+        @Override
+        public void mouseDoubleClick(final MouseEvent event) {
+            if (event.button != 1) {
+                return;
+            }
+            // Identify the selected row
+            final Point point = new Point(event.x, event.y);
+            final TableItem item = fTable.getItem(point);
+            if (item != null) {
+                final Rectangle imageBounds = item.getImageBounds(0);
+                imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
+                if (imageBounds.contains(point)) {
+                    final Long rank = (Long) item.getData(Key.RANK);
+                    if (rank != null) {
+                        toggleBookmark(rank);
+                    }
+                }
+            }
+        }
+    }
+
+    private final class RawSelectionListener implements Listener {
+        @Override
+        public void handleEvent(final Event e) {
+            if (fTrace == null) {
+                return;
+            }
+            long rank;
+            if (e.data instanceof Long) {
+                rank = (Long) e.data;
+            } else if (e.data instanceof ITmfLocation) {
+                rank = findRank((ITmfLocation) e.data);
+            } else {
+                return;
+            }
+            int index = (int) rank;
+            if (fTable.getData(Key.FILTER_OBJ) != null) {
+                // +1 for top filter status row
+                index = fCache.getFilteredEventIndex(rank) + 1;
+            }
+            // +1 for header row
+            fTable.setSelection(index + 1);
+            fSelectedRank = rank;
+            fSelectedBeginRank = fSelectedRank;
+            updateStatusLine(null);
+            final TableItem[] selection = fTable.getSelection();
+            if ((selection != null) && (selection.length > 0)) {
+                TableItem item = fTable.getSelection()[0];
+                final TmfTimestamp ts = (TmfTimestamp) item.getData(Key.TIMESTAMP);
+                if (ts != null) {
+                    broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, ts));
+                }
+                if (item.getData() instanceof ITmfEvent) {
+                    broadcast(new TmfEventSelectedSignal(TmfEventsTable.this, (ITmfEvent) item.getData()));
+                    fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(item.getData())));
+                } else {
+                    fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
+                }
+            }
+        }
+
+        private long findRank(final ITmfLocation selectedLocation) {
+            final double selectedRatio = fTrace.getLocationRatio(selectedLocation);
+            long low = 0;
+            long high = fTrace.getNbEvents();
+            long rank = high / 2;
+            double ratio = -1;
+            while (ratio != selectedRatio) {
+                ITmfContext context = fTrace.seekEvent(rank);
+                ratio = fTrace.getLocationRatio(context.getLocation());
+                context.dispose();
+                if (ratio < selectedRatio) {
+                    low = rank;
+                    rank = (rank + high) / 2;
+                } else if (ratio > selectedRatio) {
+                    high = rank;
+                    rank = (rank + low) / 2;
+                }
+                if ((high - low) < 2) {
+                    break;
+                }
+            }
+            return rank;
+        }
+    }
+
+    private final class SetDataListener implements Listener {
+        @Override
+        public void handleEvent(final Event event) {
+
+            final TableItem item = (TableItem) event.item;
+            int index = event.index - 1; // -1 for the header row
+
+            if (event.index == 0) {
+                setHeaderRowItemData(item);
+                return;
+            }
+
+            if (fTable.getData(Key.FILTER_OBJ) != null) {
+                if ((event.index == 1) || (event.index == (fTable.getItemCount() - 1))) {
+                    setFilterStatusRowItemData(item);
+                    return;
+                }
+                /* -1 for top filter status row */
+                index = index - 1;
+            }
+
+            final CachedEvent cachedEvent = fCache.getEvent(index);
+            if (cachedEvent != null) {
+                setItemData(item, cachedEvent, cachedEvent.rank);
+                return;
+            }
+
+            // Else, fill the cache asynchronously (and off the UI thread)
+            event.doit = false;
+        }
+    }
+
+    private final class PainItemListener implements Listener {
+        @Override
+        public void handleEvent(Event event) {
+            TableItem item = (TableItem) event.item;
+
+            // we promised to paint the table item's foreground
+            GC gc = event.gc;
+            Image image = item.getImage(event.index);
+            if (image != null) {
+                Rectangle imageBounds = item.getImageBounds(event.index);
+                /*
+                 * The image bounds don't match the default image position.
+                 */
+                if (IS_LINUX) {
+                    gc.drawImage(image, imageBounds.x + 1, imageBounds.y + 3);
+                } else {
+                    gc.drawImage(image, imageBounds.x, imageBounds.y + 1);
+                }
+            }
+            gc.setForeground(item.getForeground(event.index));
+            gc.setFont(item.getFont(event.index));
+            String text = item.getText(event.index);
+            Rectangle textBounds = item.getTextBounds(event.index);
+            /*
+             * The text bounds don't match the default text position.
+             */
+            if (IS_LINUX) {
+                gc.drawText(text, textBounds.x + 1, textBounds.y + 3, true);
+            } else {
+                gc.drawText(text, textBounds.x - 1, textBounds.y + 2, true);
+            }
+        }
+    }
+
+    private final class EraseItemListener implements Listener {
+        @Override
+        public void handleEvent(Event event) {
+            TableItem item = (TableItem) event.item;
+            List<?> styleRanges = (List<?>) item.getData(Key.STYLE_RANGES);
+
+            GC gc = event.gc;
+            Color background = item.getBackground(event.index);
+            /*
+             * Paint the background if it is not the default system color.
+             * In Windows, if you let the widget draw the background, it
+             * will not show the item's background color if the item is
+             * selected or hot. If there are no style ranges and the item
+             * background is the default system color, we do not want to
+             * paint it or otherwise we would override the platform theme
+             * (e.g. alternating colors).
+             */
+            if (styleRanges != null || !background.equals(item.getParent().getBackground())) {
+                // we will paint the table item's background
+                event.detail &= ~SWT.BACKGROUND;
+
+                // paint the item's default background
+                gc.setBackground(background);
+                gc.fillRectangle(event.x, event.y, event.width, event.height);
+            }
+
+            /*
+             * We will paint the table item's foreground. In Windows, if you
+             * paint the background but let the widget draw the foreground,
+             * it will override your background, unless the item is selected
+             * or hot.
+             */
+            event.detail &= ~SWT.FOREGROUND;
+
+            // paint the highlighted background for all style ranges
+            if (styleRanges != null) {
+                Rectangle textBounds = item.getTextBounds(event.index);
+                String text = item.getText(event.index);
+                for (Object o : styleRanges) {
+                    if (o instanceof StyleRange) {
+                        StyleRange styleRange = (StyleRange) o;
+                        if (styleRange.data.equals(event.index)) {
+                            int startIndex = styleRange.start;
+                            int endIndex = startIndex + styleRange.length;
+                            int startX = gc.textExtent(text.substring(0, startIndex)).x;
+                            int endX = gc.textExtent(text.substring(0, endIndex)).x;
+                            gc.setBackground(styleRange.background);
+                            gc.fillRectangle(textBounds.x + startX, textBounds.y, (endX - startX), textBounds.height);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private final class TooltipListener implements Listener {
+        Shell tooltipShell = null;
+
+        @Override
+        public void handleEvent(final Event event) {
+            switch (event.type) {
+            case SWT.MouseHover:
+                final TableItem item = fTable.getItem(new Point(event.x, event.y));
+                if (item == null) {
+                    return;
+                }
+                final Long rank = (Long) item.getData(Key.RANK);
+                if (rank == null) {
+                    return;
+                }
+                final String tooltipText = (String) item.getData(Key.BOOKMARK);
+                final Rectangle bounds = item.getImageBounds(0);
+                bounds.width = BOOKMARK_IMAGE.getBounds().width;
+                if (!bounds.contains(event.x, event.y)) {
+                    return;
+                }
+                if ((tooltipShell != null) && !tooltipShell.isDisposed()) {
+                    tooltipShell.dispose();
+                }
+                tooltipShell = new Shell(fTable.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
+                tooltipShell.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+                final FillLayout layout = new FillLayout();
+                layout.marginWidth = 2;
+                tooltipShell.setLayout(layout);
+                final Label label = new Label(tooltipShell, SWT.WRAP);
+                String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : EMPTY_STRING); //$NON-NLS-1$
+                label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+                label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+                label.setText(text);
+                label.addListener(SWT.MouseExit, this);
+                label.addListener(SWT.MouseDown, this);
+                label.addListener(SWT.MouseWheel, this);
+                final Point size = tooltipShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+                /*
+                 * Bug in Linux. The coordinates of the event have an origin
+                 * that excludes the table header but the method toDisplay()
+                 * expects coordinates relative to an origin that includes
+                 * the table header.
+                 */
+                int y = event.y;
+                if (IS_LINUX) {
+                    y += fTable.getHeaderHeight();
+                }
+                Point pt = fTable.toDisplay(event.x, y);
+                pt.x += BOOKMARK_IMAGE.getBounds().width;
+                pt.y += item.getBounds().height;
+                tooltipShell.setBounds(pt.x, pt.y, size.x, size.y);
+                tooltipShell.setVisible(true);
+                break;
+            case SWT.Dispose:
+            case SWT.KeyDown:
+            case SWT.MouseMove:
+            case SWT.MouseExit:
+            case SWT.MouseDown:
+            case SWT.MouseWheel:
+                if (tooltipShell != null) {
+                    tooltipShell.dispose();
+                    tooltipShell = null;
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
     /**
      * The events table search/filter/data keys
      *
-     * @version 1.0
      * @author Patrick Tasse
+     * @noimplement This interface only contains Event Table specific
+     *              static definitions.
      */
     public interface Key {
+
         /** Search text */
         String SEARCH_TXT = "$srch_txt"; //$NON-NLS-1$
 
@@ -258,7 +619,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         /**
          * The width of a table item
          *
-         * @since 1.0
+         * @since 1.1
          */
         String WIDTH = "$width"; //$NON-NLS-1$
     }
@@ -350,6 +711,11 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
     private Menu fRawTablePopup;
 
+    private Point fLastMenuCursorLocation;
+    private MenuManager fRawViewerPopupMenuManager;
+    private MenuManager fTablePopupMenuManager;
+    private MenuManager fHeaderPopupMenuManager;
+
     // ------------------------------------------------------------------------
     // Constructors
     // ------------------------------------------------------------------------
@@ -468,28 +834,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 column.setMoveable(true);
                 column.setData(Key.WIDTH, -1);
             }
-            column.addControlListener(new ControlAdapter() {
-                /*
-                 * Make sure that the margin column is always first and keep the
-                 * column order variable up to date.
-                 */
-                @Override
-                public void controlMoved(ControlEvent e) {
-                    int[] order = fTable.getColumnOrder();
-                    if (order[0] == MARGIN_COLUMN_INDEX) {
-                        fColumnOrder = order;
-                        return;
-                    }
-                    for (int i = order.length - 1; i > 0; i--) {
-                        if (order[i] == MARGIN_COLUMN_INDEX) {
-                            order[i] = order[i - 1];
-                            order[i - 1] = MARGIN_COLUMN_INDEX;
-                        }
-                    }
-                    fTable.setColumnOrder(order);
-                    fColumnOrder = fTable.getColumnOrder();
-                }
-            });
+            column.addControlListener(new ColumnMovedListener());
         }
         fColumnOrder = fTable.getColumnOrder();
 
@@ -500,190 +845,29 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         createHeaderEditor();
 
         // Handle the table item selection
-        fTable.addSelectionListener(new SelectionAdapter() {
-            @Override
-            public void widgetSelected(final SelectionEvent e) {
-                if (e.item == null) {
-                    return;
-                }
-                updateStatusLine(null);
-                if (fTable.getSelectionIndices().length > 0) {
-                    if (e.item.getData(Key.RANK) instanceof Long) {
-                        fSelectedRank = (Long) e.item.getData(Key.RANK);
-                        fRawViewer.selectAndReveal((Long) e.item.getData(Key.RANK));
-                    } else {
-                        fSelectedRank = -1;
-                    }
-                    if (fTable.getSelectionIndices().length == 1) {
-                        fSelectedBeginRank = fSelectedRank;
-                    }
-                    if (e.item.getData(Key.TIMESTAMP) instanceof ITmfTimestamp) {
-                        final ITmfTimestamp ts = NonNullUtils.checkNotNull((ITmfTimestamp) e.item.getData(Key.TIMESTAMP));
-                        if (fTable.getSelectionIndices().length == 1) {
-                            fSelectedBeginTimestamp = ts;
-                        }
-                        ITmfTimestamp selectedBeginTimestamp = fSelectedBeginTimestamp;
-                        if (selectedBeginTimestamp != null) {
-                            broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, selectedBeginTimestamp, ts));
-                            if (fTable.getSelectionIndices().length == 2) {
-                                updateStatusLine(ts.getDelta(selectedBeginTimestamp));
-                            }
-                        }
-                    } else {
-                        if (fTable.getSelectionIndices().length == 1) {
-                            fSelectedBeginTimestamp = null;
-                        }
-                    }
-                }
-                if (e.item.getData() instanceof ITmfEvent) {
-                    broadcast(new TmfEventSelectedSignal(TmfEventsTable.this, (ITmfEvent) e.item.getData()));
-                    fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(e.item.getData())));
-                } else {
-                    fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
-                }
-            }
-        });
+        fTable.addSelectionListener(new TableSelectionListener());
 
         int realCacheSize = Math.max(cacheSize, Display.getDefault().getBounds().height / fTable.getItemHeight());
         realCacheSize = Math.min(realCacheSize, MAX_CACHE_SIZE);
         fCache = new TmfEventsCache(realCacheSize, this);
 
         // Handle the table item requests
-        fTable.addListener(SWT.SetData, new Listener() {
-
-            @Override
-            public void handleEvent(final Event event) {
-
-                final TableItem item = (TableItem) event.item;
-                int index = event.index - 1; // -1 for the header row
-
-                if (event.index == 0) {
-                    setHeaderRowItemData(item);
-                    return;
-                }
-
-                if (fTable.getData(Key.FILTER_OBJ) != null) {
-                    if ((event.index == 1) || (event.index == (fTable.getItemCount() - 1))) {
-                        setFilterStatusRowItemData(item);
-                        return;
-                    }
-                    /* -1 for top filter status row */
-                    index = index - 1;
-                }
-
-                final CachedEvent cachedEvent = fCache.getEvent(index);
-                if (cachedEvent != null) {
-                    setItemData(item, cachedEvent, cachedEvent.rank);
-                    return;
-                }
-
-                // Else, fill the cache asynchronously (and off the UI thread)
-                event.doit = false;
-            }
-        });
+        fTable.addListener(SWT.SetData, new SetDataListener());
 
         fTable.addListener(SWT.MenuDetect, new Listener() {
             @Override
             public void handleEvent(Event event) {
-                Point pt = fTable.getDisplay().map(null, fTable, new Point(event.x, event.y));
+                fLastMenuCursorLocation = new Point(event.x, event.y);
+                Point pt = fTable.getDisplay().map(null, fTable, fLastMenuCursorLocation);
                 Rectangle clientArea = fTable.getClientArea();
                 boolean header = clientArea.y <= pt.y && pt.y < (clientArea.y + fTable.getHeaderHeight());
                 fTable.setMenu(header ? fHeaderMenu : fTablePopup);
             }
         });
 
-        fTable.addMouseListener(new MouseAdapter() {
-            @Override
-            public void mouseDoubleClick(final MouseEvent event) {
-                if (event.button != 1) {
-                    return;
-                }
-                // Identify the selected row
-                final Point point = new Point(event.x, event.y);
-                final TableItem item = fTable.getItem(point);
-                if (item != null) {
-                    final Rectangle imageBounds = item.getImageBounds(0);
-                    imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
-                    if (imageBounds.contains(point)) {
-                        final Long rank = (Long) item.getData(Key.RANK);
-                        if (rank != null) {
-                            toggleBookmark(rank);
-                        }
-                    }
-                }
-            }
-        });
+        fTable.addMouseListener(new MouseDoubleClickListener());
 
-        final Listener tooltipListener = new Listener() {
-            Shell tooltipShell = null;
-
-            @Override
-            public void handleEvent(final Event event) {
-                switch (event.type) {
-                case SWT.MouseHover:
-                    final TableItem item = fTable.getItem(new Point(event.x, event.y));
-                    if (item == null) {
-                        return;
-                    }
-                    final Long rank = (Long) item.getData(Key.RANK);
-                    if (rank == null) {
-                        return;
-                    }
-                    final String tooltipText = (String) item.getData(Key.BOOKMARK);
-                    final Rectangle bounds = item.getImageBounds(0);
-                    bounds.width = BOOKMARK_IMAGE.getBounds().width;
-                    if (!bounds.contains(event.x, event.y)) {
-                        return;
-                    }
-                    if ((tooltipShell != null) && !tooltipShell.isDisposed()) {
-                        tooltipShell.dispose();
-                    }
-                    tooltipShell = new Shell(fTable.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
-                    tooltipShell.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
-                    final FillLayout layout = new FillLayout();
-                    layout.marginWidth = 2;
-                    tooltipShell.setLayout(layout);
-                    final Label label = new Label(tooltipShell, SWT.WRAP);
-                    String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : EMPTY_STRING); //$NON-NLS-1$
-                    label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
-                    label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
-                    label.setText(text);
-                    label.addListener(SWT.MouseExit, this);
-                    label.addListener(SWT.MouseDown, this);
-                    label.addListener(SWT.MouseWheel, this);
-                    final Point size = tooltipShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
-                    /*
-                     * Bug in Linux. The coordinates of the event have an origin
-                     * that excludes the table header but the method toDisplay()
-                     * expects coordinates relative to an origin that includes
-                     * the table header.
-                     */
-                    int y = event.y;
-                    if (IS_LINUX) {
-                        y += fTable.getHeaderHeight();
-                    }
-                    Point pt = fTable.toDisplay(event.x, y);
-                    pt.x += BOOKMARK_IMAGE.getBounds().width;
-                    pt.y += item.getBounds().height;
-                    tooltipShell.setBounds(pt.x, pt.y, size.x, size.y);
-                    tooltipShell.setVisible(true);
-                    break;
-                case SWT.Dispose:
-                case SWT.KeyDown:
-                case SWT.MouseMove:
-                case SWT.MouseExit:
-                case SWT.MouseDown:
-                case SWT.MouseWheel:
-                    if (tooltipShell != null) {
-                        tooltipShell.dispose();
-                        tooltipShell = null;
-                    }
-                    break;
-                default:
-                    break;
-                }
-            }
-        };
+        final Listener tooltipListener = new TooltipListener();
 
         fTable.addListener(SWT.MouseHover, tooltipListener);
         fTable.addListener(SWT.Dispose, tooltipListener);
@@ -693,94 +877,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fTable.addListener(SWT.MouseDown, tooltipListener);
         fTable.addListener(SWT.MouseWheel, tooltipListener);
 
-        fTable.addListener(SWT.EraseItem, new Listener() {
-            @Override
-            public void handleEvent(Event event) {
-                TableItem item = (TableItem) event.item;
-                List<?> styleRanges = (List<?>) item.getData(Key.STYLE_RANGES);
-
-                GC gc = event.gc;
-                Color background = item.getBackground(event.index);
-                /*
-                 * Paint the background if it is not the default system color.
-                 * In Windows, if you let the widget draw the background, it
-                 * will not show the item's background color if the item is
-                 * selected or hot. If there are no style ranges and the item
-                 * background is the default system color, we do not want to
-                 * paint it or otherwise we would override the platform theme
-                 * (e.g. alternating colors).
-                 */
-                if (styleRanges != null || !background.equals(item.getParent().getBackground())) {
-                    // we will paint the table item's background
-                    event.detail &= ~SWT.BACKGROUND;
-
-                    // paint the item's default background
-                    gc.setBackground(background);
-                    gc.fillRectangle(event.x, event.y, event.width, event.height);
-                }
-
-                /*
-                 * We will paint the table item's foreground. In Windows, if you
-                 * paint the background but let the widget draw the foreground,
-                 * it will override your background, unless the item is selected
-                 * or hot.
-                 */
-                event.detail &= ~SWT.FOREGROUND;
-
-                // paint the highlighted background for all style ranges
-                if (styleRanges != null) {
-                    Rectangle textBounds = item.getTextBounds(event.index);
-                    String text = item.getText(event.index);
-                    for (Object o : styleRanges) {
-                        if (o instanceof StyleRange) {
-                            StyleRange styleRange = (StyleRange) o;
-                            if (styleRange.data.equals(event.index)) {
-                                int startIndex = styleRange.start;
-                                int endIndex = startIndex + styleRange.length;
-                                int startX = gc.stringExtent(text.substring(0, startIndex)).x;
-                                int endX = gc.stringExtent(text.substring(0, endIndex)).x;
-                                gc.setBackground(styleRange.background);
-                                gc.fillRectangle(textBounds.x + startX, textBounds.y, (endX - startX), textBounds.height);
-                            }
-                        }
-                    }
-                }
-            }
-        });
+        fTable.addListener(SWT.EraseItem, new EraseItemListener());
 
-        fTable.addListener(SWT.PaintItem, new Listener() {
-            @Override
-            public void handleEvent(Event event) {
-                TableItem item = (TableItem) event.item;
-
-                // we promised to paint the table item's foreground
-                GC gc = event.gc;
-                Image image = item.getImage(event.index);
-                if (image != null) {
-                    Rectangle imageBounds = item.getImageBounds(event.index);
-                    /*
-                     * The image bounds don't match the default image position.
-                     */
-                    if (IS_LINUX) {
-                        gc.drawImage(image, imageBounds.x + 1, imageBounds.y + 3);
-                    } else {
-                        gc.drawImage(image, imageBounds.x, imageBounds.y + 1);
-                    }
-                }
-                gc.setForeground(item.getForeground(event.index));
-                gc.setFont(item.getFont(event.index));
-                String text = item.getText(event.index);
-                Rectangle textBounds = item.getTextBounds(event.index);
-                /*
-                 * The text bounds don't match the default text position.
-                 */
-                if (IS_LINUX) {
-                    gc.drawText(text, textBounds.x + 1, textBounds.y + 3, true);
-                } else {
-                    gc.drawText(text, textBounds.x - 1, textBounds.y + 2, true);
-                }
-            }
-        });
+        fTable.addListener(SWT.PaintItem, new PainItemListener());
 
         // Create resources
         createResources();
@@ -795,70 +894,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
         fRawViewer = new TmfRawEventViewer(fSashForm, SWT.H_SCROLL | SWT.V_SCROLL);
 
-        fRawViewer.addSelectionListener(new Listener() {
-            @Override
-            public void handleEvent(final Event e) {
-                if (fTrace == null) {
-                    return;
-                }
-                long rank;
-                if (e.data instanceof Long) {
-                    rank = (Long) e.data;
-                } else if (e.data instanceof ITmfLocation) {
-                    rank = findRank((ITmfLocation) e.data);
-                } else {
-                    return;
-                }
-                int index = (int) rank;
-                if (fTable.getData(Key.FILTER_OBJ) != null) {
-                    // +1 for top filter status row
-                    index = fCache.getFilteredEventIndex(rank) + 1;
-                }
-                // +1 for header row
-                fTable.setSelection(index + 1);
-                fSelectedRank = rank;
-                fSelectedBeginRank = fSelectedRank;
-                updateStatusLine(null);
-                final TableItem[] selection = fTable.getSelection();
-                if ((selection != null) && (selection.length > 0)) {
-                    TableItem item = fTable.getSelection()[0];
-                    final TmfTimestamp ts = (TmfTimestamp) item.getData(Key.TIMESTAMP);
-                    if (ts != null) {
-                        broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, ts));
-                    }
-                    if (item.getData() instanceof ITmfEvent) {
-                        broadcast(new TmfEventSelectedSignal(TmfEventsTable.this, (ITmfEvent) item.getData()));
-                        fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(item.getData())));
-                    } else {
-                        fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
-                    }
-                }
-            }
-
-            private long findRank(final ITmfLocation selectedLocation) {
-                final double selectedRatio = fTrace.getLocationRatio(selectedLocation);
-                long low = 0;
-                long high = fTrace.getNbEvents();
-                long rank = high / 2;
-                double ratio = -1;
-                while (ratio != selectedRatio) {
-                    ITmfContext context = fTrace.seekEvent(rank);
-                    ratio = fTrace.getLocationRatio(context.getLocation());
-                    context.dispose();
-                    if (ratio < selectedRatio) {
-                        low = rank;
-                        rank = (rank + high) / 2;
-                    } else if (ratio > selectedRatio) {
-                        high = rank;
-                        rank = (rank + low) / 2;
-                    }
-                    if ((high - low) < 2) {
-                        break;
-                    }
-                }
-                return rank;
-            }
-        });
+        fRawViewer.addSelectionListener(new RawSelectionListener());
 
         fSashForm.setWeights(new int[] { 1, 1 });
         fRawViewer.setVisible(false);
@@ -940,7 +976,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 for (int i : fTable.getColumnOrder()) {
                     TableColumn column = fTable.getColumns()[i];
                     // Omit the margin column and hidden columns
-                    if (i >= EVENT_COLUMNS_START_INDEX && (column.getResizable() || column.getWidth() > 0)) {
+                    if (isVisibleEventColumn(column)) {
                         columns.add(fColumns.get(i));
                     }
                 }
@@ -1005,16 +1041,50 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 final TableItem item = items[0];
 
                 final Object data = item.getData();
-                if (data instanceof ITmfSourceLookup) {
-                    ITmfSourceLookup event = (ITmfSourceLookup) data;
-                    ITmfCallsite cs = event.getCallsite();
-                    if (cs == null || cs.getFileName() == null) {
-                        return;
-                    }
-                    IMarker marker = null;
-                    try {
-                        String fileName = cs.getFileName();
-                        final String trimmedPath = fileName.replaceAll("\\.\\./", EMPTY_STRING); //$NON-NLS-1$
+                if (!(data instanceof ITmfSourceLookup)) {
+                    return;
+                }
+                ITmfSourceLookup event = (ITmfSourceLookup) data;
+                ITmfCallsite cs = event.getCallsite();
+                if (cs == null) {
+                    return;
+                }
+
+                String fileName = cs.getFileName();
+                final String trimmedPath = fileName.replaceAll("\\.\\./", EMPTY_STRING); //$NON-NLS-1$
+                File fileToOpen = new File(trimmedPath);
+
+                try {
+                    if (fileToOpen.exists() && fileToOpen.isFile()) {
+                        /*
+                         * The path points to a "real" file, attempt to open
+                         * that
+                         */
+                        IFileStore fileStore = EFS.getLocalFileSystem().getStore(fileToOpen.toURI());
+                        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+
+                        IEditorPart editor = IDE.openEditorOnFileStore(page, fileStore);
+                        if (editor instanceof ITextEditor) {
+                            /*
+                             * Calculate the "document offset" corresponding to
+                             * the line number, then seek there.
+                             */
+                            ITextEditor textEditor = (ITextEditor) editor;
+                            int lineNumber = Long.valueOf(cs.getLineNumber()).intValue();
+                            IDocument document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
+
+                            IRegion region = document.getLineInformation(lineNumber - 1);
+                            if (region != null) {
+                                textEditor.selectAndReveal(region.getOffset(), region.getLength());
+                            }
+                        }
+
+                    } else {
+                        /*
+                         * The file was not found on disk, attempt to find it in
+                         * the workspace instead.
+                         */
+                        IMarker marker = null;
                         final ArrayList<IFile> files = new ArrayList<>();
                         ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
                             @Override
@@ -1051,12 +1121,12 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                             marker.setAttribute(IMarker.LINE_NUMBER, Long.valueOf(cs.getLineNumber()).intValue());
                             IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), marker);
                             marker.delete();
-                        } else if (files.size() == 0) {
+                        } else if (files.isEmpty()) {
                             displayException(new FileNotFoundException('\'' + cs.toString() + '\'' + '\n' + Messages.TmfEventsTable_OpenSourceCodeNotFound));
                         }
-                    } catch (CoreException e) {
-                        displayException(e);
                     }
+                } catch (BadLocationException | CoreException e) {
+                    displayException(e);
                 }
             }
         };
@@ -1131,7 +1201,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     for (int i : fTable.getColumnOrder()) {
                         TableColumn column = fTable.getColumns()[i];
                         // Omit the margin column and hidden columns
-                        if (i >= EVENT_COLUMNS_START_INDEX && (column.getResizable() || column.getWidth() > 0)) {
+                        if (isVisibleEventColumn(column)) {
                             exportColumns.add(fColumns.get(i));
                         }
                     }
@@ -1190,36 +1260,36 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             }
         }
 
-        final MenuManager headerPopupMenu = new MenuManager();
-        headerPopupMenu.setRemoveAllWhenShown(true);
-        headerPopupMenu.addMenuListener(new IMenuListener() {
+        fHeaderPopupMenuManager = new MenuManager();
+        fHeaderPopupMenuManager.setRemoveAllWhenShown(true);
+        fHeaderPopupMenuManager.addMenuListener(new IMenuListener() {
             @Override
             public void menuAboutToShow(IMenuManager manager) {
                 for (int index : fTable.getColumnOrder()) {
                     if (fTable.getColumns()[index].getData(Key.WIDTH) != null) {
-                        headerPopupMenu.add(createHeaderAction(fTable.getColumns()[index]));
+                        fHeaderPopupMenuManager.add(createHeaderAction(fTable.getColumns()[index]));
                     }
                 }
-                headerPopupMenu.add(new Separator());
-                headerPopupMenu.add(createResetHeaderAction());
+                fHeaderPopupMenuManager.add(new Separator());
+                fHeaderPopupMenuManager.add(createResetHeaderAction());
             }
         });
 
-        final MenuManager tablePopupMenu = new MenuManager();
-        tablePopupMenu.setRemoveAllWhenShown(true);
-        tablePopupMenu.addMenuListener(new IMenuListener() {
+        fTablePopupMenuManager = new MenuManager();
+        fTablePopupMenuManager.setRemoveAllWhenShown(true);
+        fTablePopupMenuManager.addMenuListener(new IMenuListener() {
             @Override
             public void menuAboutToShow(final IMenuManager manager) {
                 if (fTable.getSelectionIndices().length == 1 && fTable.getSelectionIndices()[0] == 0) {
                     // Right-click on header row
                     if (fHeaderState == HeaderState.FILTER) {
-                        tablePopupMenu.add(showSearchBarAction);
+                        fTablePopupMenuManager.add(showSearchBarAction);
                     } else {
-                        tablePopupMenu.add(showFilterBarAction);
+                        fTablePopupMenuManager.add(showFilterBarAction);
                     }
                     return;
                 }
-                final Point point = fTable.toControl(Display.getDefault().getCursorLocation());
+                final Point point = fTable.toControl(fLastMenuCursorLocation);
                 final TableItem item = fTable.getSelection().length > 0 ? fTable.getSelection()[0] : null;
                 if (item != null) {
                     final Rectangle imageBounds = item.getImageBounds(0);
@@ -1229,10 +1299,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                         final Long rank = (Long) item.getData(Key.RANK);
                         if ((rank != null) && (fBookmarksFile != null)) {
                             if (fBookmarksMap.containsKey(rank)) {
-                                tablePopupMenu.add(new ToggleBookmarkAction(
+                                fTablePopupMenuManager.add(new ToggleBookmarkAction(
                                         Messages.TmfEventsTable_RemoveBookmarkActionText, rank));
                             } else {
-                                tablePopupMenu.add(new ToggleBookmarkAction(
+                                fTablePopupMenuManager.add(new ToggleBookmarkAction(
                                         Messages.TmfEventsTable_AddBookmarkActionText, rank));
                             }
                         }
@@ -1242,19 +1312,19 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
                 // Right-click on table
                 if (fSelectedRank != -1 && fSelectedBeginRank != -1) {
-                    tablePopupMenu.add(copyAction);
-                    tablePopupMenu.add(new Separator());
+                    fTablePopupMenuManager.add(copyAction);
+                    fTablePopupMenuManager.add(new Separator());
                 }
                 if (fTable.isVisible() && fRawViewer.isVisible()) {
-                    tablePopupMenu.add(hideTableAction);
-                    tablePopupMenu.add(hideRawAction);
+                    fTablePopupMenuManager.add(hideTableAction);
+                    fTablePopupMenuManager.add(hideRawAction);
                 } else if (!fTable.isVisible()) {
-                    tablePopupMenu.add(showTableAction);
+                    fTablePopupMenuManager.add(showTableAction);
                 } else if (!fRawViewer.isVisible()) {
-                    tablePopupMenu.add(showRawAction);
+                    fTablePopupMenuManager.add(showRawAction);
                 }
-                tablePopupMenu.add(exportToTextAction);
-                tablePopupMenu.add(new Separator());
+                fTablePopupMenuManager.add(exportToTextAction);
+                fTablePopupMenuManager.add(new Separator());
 
                 if (item != null) {
                     final Object data = item.getData();
@@ -1262,7 +1332,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     if (data instanceof ITmfSourceLookup) {
                         ITmfSourceLookup event = (ITmfSourceLookup) data;
                         if (event.getCallsite() != null) {
-                            tablePopupMenu.add(openCallsiteAction);
+                            fTablePopupMenuManager.add(openCallsiteAction);
                             separator = new Separator();
                         }
                     }
@@ -1270,12 +1340,12 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     if (data instanceof ITmfModelLookup) {
                         ITmfModelLookup event = (ITmfModelLookup) data;
                         if (event.getModelUri() != null) {
-                            tablePopupMenu.add(openModelAction);
+                            fTablePopupMenuManager.add(openModelAction);
                             separator = new Separator();
                         }
 
                         if (separator != null) {
-                            tablePopupMenu.add(separator);
+                            fTablePopupMenuManager.add(separator);
                         }
                     }
                 }
@@ -1296,11 +1366,11 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 }
 
                 if (isCollapsible && !(fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter)) {
-                    tablePopupMenu.add(collapseAction);
-                    tablePopupMenu.add(new Separator());
+                    fTablePopupMenuManager.add(collapseAction);
+                    fTablePopupMenuManager.add(new Separator());
                 }
 
-                tablePopupMenu.add(clearFiltersAction);
+                fTablePopupMenuManager.add(clearFiltersAction);
                 final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();
                 if (savedFilters.length > 0) {
                     final MenuManager subMenu = new MenuManager(Messages.TmfEventsTable_ApplyPresetFilterMenuName);
@@ -1315,35 +1385,35 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                             });
                         }
                     }
-                    tablePopupMenu.add(subMenu);
+                    fTablePopupMenuManager.add(subMenu);
                 }
-                appendToTablePopupMenu(tablePopupMenu, item);
+                appendToTablePopupMenu(fTablePopupMenuManager, item);
             }
         });
 
-        final MenuManager rawViewerPopupMenu = new MenuManager();
-        rawViewerPopupMenu.setRemoveAllWhenShown(true);
-        rawViewerPopupMenu.addMenuListener(new IMenuListener() {
+        fRawViewerPopupMenuManager = new MenuManager();
+        fRawViewerPopupMenuManager.setRemoveAllWhenShown(true);
+        fRawViewerPopupMenuManager.addMenuListener(new IMenuListener() {
             @Override
             public void menuAboutToShow(final IMenuManager manager) {
                 if (fTable.isVisible() && fRawViewer.isVisible()) {
-                    rawViewerPopupMenu.add(hideTableAction);
-                    rawViewerPopupMenu.add(hideRawAction);
+                    fRawViewerPopupMenuManager.add(hideTableAction);
+                    fRawViewerPopupMenuManager.add(hideRawAction);
                 } else if (!fTable.isVisible()) {
-                    rawViewerPopupMenu.add(showTableAction);
+                    fRawViewerPopupMenuManager.add(showTableAction);
                 } else if (!fRawViewer.isVisible()) {
-                    rawViewerPopupMenu.add(showRawAction);
+                    fRawViewerPopupMenuManager.add(showRawAction);
                 }
-                appendToRawPopupMenu(tablePopupMenu);
+                appendToRawPopupMenu(fRawViewerPopupMenuManager);
             }
         });
 
-        fHeaderMenu = headerPopupMenu.createContextMenu(fTable);
+        fHeaderMenu = fHeaderPopupMenuManager.createContextMenu(fTable);
 
-        fTablePopup = tablePopupMenu.createContextMenu(fTable);
+        fTablePopup = fTablePopupMenuManager.createContextMenu(fTable);
         fTable.setMenu(fTablePopup);
 
-        fRawTablePopup = rawViewerPopupMenu.createContextMenu(fRawViewer);
+        fRawTablePopup = fRawViewerPopupMenuManager.createContextMenu(fRawViewer);
         fRawViewer.setMenu(fRawTablePopup);
     }
 
@@ -1381,6 +1451,16 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         }
         fResourceManager.dispose();
         fRawViewer.dispose();
+        if (fRawViewerPopupMenuManager != null) {
+            fRawViewerPopupMenuManager.dispose();
+        }
+        if (fHeaderPopupMenuManager != null) {
+            fHeaderPopupMenuManager.dispose();
+        }
+        if (fTablePopupMenuManager != null) {
+            fTablePopupMenuManager.dispose();
+        }
+
         super.dispose();
     }
 
@@ -1447,7 +1527,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             try {
                 for (long markerId : markerIds) {
                     final IMarker marker = fBookmarksFile.findMarker(markerId);
-                    parts.add(marker.getAttribute(IMarker.MESSAGE));
+                    if (marker != null) {
+                        parts.add(marker.getAttribute(IMarker.MESSAGE));
+                    }
                 }
             } catch (CoreException e) {
                 displayException(e);
@@ -2325,7 +2407,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      */
     private void createResources() {
         fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable.getForeground().getRGB()));
-        fGreenColor = fTable.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);
+        fGreenColor = PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);
     }
 
     /**
@@ -2411,6 +2493,22 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         }
     }
 
+    /**
+     * Returns true if the column is a visible event column.
+     *
+     * @param column the column
+     * @return false if the column is the margin column or hidden, true otherwise
+     */
+    private static boolean isVisibleEventColumn(TableColumn column) {
+        if (column.getData(Key.ASPECT) == TmfMarginColumn.MARGIN_ASPECT) {
+            return false;
+        }
+        if (!column.getResizable() && column.getWidth() == 0) {
+            return false;
+        }
+        return true;
+    }
+
     /**
      * 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
@@ -2509,6 +2607,24 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fTable.setFocus();
     }
 
+    /**
+     * Registers context menus with a site for extension. This method can be
+     * called for part sites so that context menu contributions can be added.
+     *
+     * @param site
+     *            the site that the context menus will be registered for
+     *
+     * @since 2.0
+     */
+    public void registerContextMenus(IWorkbenchPartSite site) {
+        if (site instanceof IEditorSite) {
+            IEditorSite editorSite = (IEditorSite) site;
+            // Don't use the editor input when adding contributions, otherwise
+            // we get too many unwanted things.
+            editorSite.registerContextMenu(fTablePopupMenuManager, this, false);
+        }
+    }
+
     /**
      * Assign a new trace to this event table.
      *
@@ -2527,7 +2643,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fDisposeOnClose = disposeOnClose;
 
         // Perform the updates on the UI thread
-        fTable.getDisplay().syncExec(new Runnable() {
+        PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
             @Override
             public void run() {
                 fSelectedRank = -1;
@@ -2602,27 +2718,25 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             fCacheUpdateBusy = true;
         }
         // Event cache is now updated. Perform update on the UI thread
-        if (!fTable.isDisposed()) {
-            fTable.getDisplay().asyncExec(new Runnable() {
-                @Override
-                public void run() {
-                    if (!fTable.isDisposed()) {
-                        fTable.refresh();
-                        packColumns();
-                    }
-                    if (completed) {
-                        populateCompleted();
-                    }
-                    synchronized (fCacheUpdateSyncObj) {
-                        fCacheUpdateBusy = false;
-                        if (fCacheUpdatePending) {
-                            fCacheUpdatePending = false;
-                            cacheUpdated(fCacheUpdateCompleted);
-                        }
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (!fTable.isDisposed()) {
+                    fTable.refresh();
+                    packColumns();
+                }
+                if (completed) {
+                    populateCompleted();
+                }
+                synchronized (fCacheUpdateSyncObj) {
+                    fCacheUpdateBusy = false;
+                    if (fCacheUpdatePending) {
+                        fCacheUpdatePending = false;
+                        cacheUpdated(fCacheUpdateCompleted);
                     }
                 }
-            });
-        }
+            }
+        });
     }
 
     /**
@@ -2705,29 +2819,39 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             final TableItem tableItem = selection[0];
             if (tableItem.getData(Key.RANK) != null) {
                 final StringBuffer defaultMessage = new StringBuffer();
-                for (int i = 0; i < fTable.getColumns().length; i++) {
-                    if (i > 0) {
-                        defaultMessage.append(", "); //$NON-NLS-1$
+                for (int i : fTable.getColumnOrder()) {
+                    TableColumn column = fTable.getColumns()[i];
+                    // Omit the margin column and hidden columns
+                    if (isVisibleEventColumn(column)) {
+                        if (defaultMessage.length() > 0) {
+                            defaultMessage.append(", "); //$NON-NLS-1$
+                        }
+                        defaultMessage.append(tableItem.getText(i));
                     }
-                    defaultMessage.append(tableItem.getText(i));
                 }
-                final InputDialog dialog = new MultiLineInputDialog(
-                        PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
-                        Messages.TmfEventsTable_AddBookmarkDialogTitle,
-                        Messages.TmfEventsTable_AddBookmarkDialogMessage,
-                        defaultMessage.toString());
+                final AddBookmarkDialog dialog = new AddBookmarkDialog(
+                        PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), defaultMessage.toString());
                 if (dialog.open() == Window.OK) {
                     final String message = dialog.getValue();
                     try {
-                        final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
-                        if (bookmark.exists()) {
-                            bookmark.setAttribute(IMarker.MESSAGE, message.toString());
-                            final Long rank = (Long) tableItem.getData(Key.RANK);
-                            final int location = rank.intValue();
-                            bookmark.setAttribute(IMarker.LOCATION, Integer.valueOf(location));
-                            fBookmarksMap.put(rank, bookmark.getId());
-                            fTable.refresh();
-                        }
+                        final Long rank = (Long) tableItem.getData(Key.RANK);
+                        final String location = NLS.bind(Messages.TmfMarker_LocationRank, rank.toString());
+                        final ITmfTimestamp timestamp = (ITmfTimestamp) tableItem.getData(Key.TIMESTAMP);
+                        final long[] id = new long[1];
+                        ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
+                            @Override
+                            public void run(IProgressMonitor monitor) throws CoreException {
+                                final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
+                                bookmark.setAttribute(IMarker.MESSAGE, message.toString());
+                                bookmark.setAttribute(IMarker.LOCATION, location);
+                                bookmark.setAttribute(ITmfMarker.MARKER_RANK, rank.toString());
+                                bookmark.setAttribute(ITmfMarker.MARKER_TIME, Long.toString(new TmfNanoTimestamp(timestamp).getValue()));
+                                bookmark.setAttribute(ITmfMarker.MARKER_COLOR, dialog.getColorValue().toString());
+                                id[0] = bookmark.getId();
+                            }
+                        }, null);
+                        fBookmarksMap.put(rank, id[0]);
+                        fTable.refresh();
                     } catch (final CoreException e) {
                         displayException(e);
                     }
@@ -2738,19 +2862,50 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     }
 
     /**
-     * Remove a bookmark from this event table.
+     * Add one or more bookmarks to this event table.
      *
-     * @param bookmark
-     *            The bookmark to remove
+     * @param bookmarks
+     *            The bookmarks to add
+     * @since 2.0
      */
-    public void removeBookmark(final IMarker bookmark) {
-        for (final Entry<Long, Long> entry : fBookmarksMap.entries()) {
-            if (entry.getValue().equals(bookmark.getId())) {
-                fBookmarksMap.remove(entry.getKey(), entry.getValue());
-                fTable.refresh();
-                return;
+    public void addBookmark(final IMarker... bookmarks) {
+        for (IMarker bookmark : bookmarks) {
+            /* try location as an integer for backward compatibility */
+            long rank = bookmark.getAttribute(IMarker.LOCATION, -1);
+            if (rank == -1) {
+                String rankString = bookmark.getAttribute(ITmfMarker.MARKER_RANK, (String) null);
+                if (rankString != null) {
+                    try {
+                        rank = Long.parseLong(rankString);
+                    } catch (NumberFormatException e) {
+                        Activator.getDefault().logError("Invalid marker rank", e); //$NON-NLS-1$
+                    }
+                }
+            }
+            if (rank != -1) {
+                fBookmarksMap.put(rank, bookmark.getId());
             }
         }
+        fTable.refresh();
+    }
+
+    /**
+     * Remove one or more bookmarks from this event table.
+     *
+     * @param bookmarks
+     *            The bookmarks to remove
+     * @since 2.0
+     */
+    public void removeBookmark(final IMarker... bookmarks) {
+        for (IMarker bookmark : bookmarks) {
+            for (final Entry<Long, Long> entry : fBookmarksMap.entries()) {
+                if (entry.getValue().equals(bookmark.getId())) {
+                    fBookmarksMap.remove(entry.getKey(), entry.getValue());
+                    break;
+                }
+            }
+        }
+        fTable.refresh();
     }
 
     private void toggleBookmark(final Long rank) {
@@ -2791,14 +2946,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         }
         try {
             fBookmarksMap.clear();
-            for (final IMarker bookmark : bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) {
-                final int location = bookmark.getAttribute(IMarker.LOCATION, -1);
-                if (location != -1) {
-                    final long rank = location;
-                    fBookmarksMap.put(rank, bookmark.getId());
-                }
-            }
-            fTable.refresh();
+            IMarker[] bookmarks = bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO);
+            addBookmark(bookmarks);
         } catch (final CoreException e) {
             displayException(e);
         }
@@ -2806,9 +2955,35 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
     @Override
     public void gotoMarker(final IMarker marker) {
-        final int rank = marker.getAttribute(IMarker.LOCATION, -1);
+        ITmfTimestamp tsBegin = null;
+        ITmfTimestamp tsEnd = null;
+        /* try location as an integer for backward compatibility */
+        long rank = marker.getAttribute(IMarker.LOCATION, -1);
+        if (rank == -1) {
+            String rankString = marker.getAttribute(ITmfMarker.MARKER_RANK, (String) null);
+            try {
+                rank = Long.parseLong(rankString);
+            } catch (NumberFormatException e) {
+                /* ignored */
+            }
+        }
+        try {
+            String timeString = marker.getAttribute(ITmfMarker.MARKER_TIME, (String) null);
+            long time = Long.parseLong(timeString);
+            tsBegin = new TmfNanoTimestamp(time);
+            String durationString = marker.getAttribute(ITmfMarker.MARKER_DURATION, (String) null);
+            long duration = Long.parseLong(durationString);
+            tsEnd = new TmfNanoTimestamp(time + duration);
+        } catch (NumberFormatException e) {
+            /* ignored */
+        }
+        if (rank == -1 && tsBegin != null) {
+            final ITmfContext context = fTrace.seekEvent(tsBegin);
+            rank = context.getRank();
+            context.dispose();
+        }
         if (rank != -1) {
-            int index = rank;
+            int index = (int) rank;
             if (fTable.getData(Key.FILTER_OBJ) != null) {
                 // +1 for top filter status row
                 index = fCache.getFilteredEventIndex(rank) + 1;
@@ -2819,6 +2994,13 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             fSelectedBeginRank = fSelectedRank;
             fTable.setSelection(index + 1); // +1 for header row
             updateStatusLine(null);
+            if (tsBegin != null) {
+                if (tsEnd != null) {
+                    broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, tsBegin, tsEnd));
+                } else {
+                    broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, tsBegin));
+                }
+            }
         }
     }
 
@@ -2924,7 +3106,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     final long rank = context.getRank();
                     context.dispose();
 
-                    fTable.getDisplay().asyncExec(new Runnable() {
+                    PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
                         @Override
                         public void run() {
                             // Return if table is disposed
This page took 0.043148 seconds and 5 git commands to generate.