tmf: Preserve order of marker categories
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / widgets / timegraph / TimeGraphViewer.java
index 6baa0f98dcb00118d576e008f7662c91680c8777..f5dc386c5286540c3867294cce8367469e926f1f 100644 (file)
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (c) 2007, 2015 Intel Corporation, Ericsson, others
+ * Copyright (c) 2007, 2016 Intel Corporation, Ericsson, others
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
 package org.eclipse.tracecompass.tmf.ui.widgets.timegraph;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ActionContributionItem;
 import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuCreator;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.viewers.AbstractTreeViewer;
 import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
 import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.window.Window;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ControlAdapter;
 import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
 import org.eclipse.swt.events.KeyAdapter;
 import org.eclipse.swt.events.KeyEvent;
 import org.eclipse.swt.events.MenuDetectListener;
@@ -36,6 +52,7 @@ import org.eclipse.swt.events.MouseWheelListener;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.RGBA;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.layout.GridData;
@@ -45,24 +62,31 @@ import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.Slider;
 import org.eclipse.tracecompass.internal.tmf.ui.Activator;
 import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants;
 import org.eclipse.tracecompass.internal.tmf.ui.Messages;
+import org.eclipse.tracecompass.internal.tmf.ui.dialogs.AddBookmarkDialog;
 import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
 import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.ShowFilterDialogAction;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.TimeGraphLegend;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEvent;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.MarkerEvent;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.ITimeDataProvider;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeDataProviderCyclesConverter;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphColorScheme;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphMarkerAxis;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphScale;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphTooltipHandler;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
+import org.eclipse.ui.PlatformUI;
 
 /**
  * Generic time graph viewer implementation
@@ -82,6 +106,11 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
     private static final long DEFAULT_FREQUENCY = 1000000000L;
     private static final int H_SCROLLBAR_MAX = Integer.MAX_VALUE - 1;
 
+    private static final ImageDescriptor ADD_BOOKMARK = Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_ADD_BOOKMARK);
+    private static final ImageDescriptor NEXT_BOOKMARK = Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_BOOKMARK);
+    private static final ImageDescriptor PREVIOUS_BOOKMARK = Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREVIOUS_BOOKMARK);
+    private static final ImageDescriptor REMOVE_BOOKMARK = Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_REMOVE_BOOKMARK);
+
     private long fMinTimeInterval;
     private ITimeGraphEntry fSelectedEntry;
     private long fBeginTime = SWT.DEFAULT; // The user-specified bounds start time
@@ -102,18 +131,20 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
 
     private TimeGraphControl fTimeGraphCtrl;
     private TimeGraphScale fTimeScaleCtrl;
+    private TimeGraphMarkerAxis fMarkerAxisCtrl;
     private Slider fHorizontalScrollBar;
     private Slider fVerticalScrollBar;
-    private TimeGraphColorScheme fColorScheme;
+    private @NonNull TimeGraphColorScheme fColorScheme = new TimeGraphColorScheme();
     private Object fInputElement;
     private ITimeGraphContentProvider fTimeGraphContentProvider;
     private ITimeGraphPresentationProvider fTimeGraphProvider;
-    private ITimeDataProvider fTimeDataProvider = this;
+    private @NonNull ITimeDataProvider fTimeDataProvider = this;
     private TimeGraphTooltipHandler fToolTipHandler;
 
     private List<ITimeGraphSelectionListener> fSelectionListeners = new ArrayList<>();
     private List<ITimeGraphTimeListener> fTimeListeners = new ArrayList<>();
     private List<ITimeGraphRangeListener> fRangeListeners = new ArrayList<>();
+    private List<ITimeGraphBookmarkListener> fBookmarkListeners = new ArrayList<>();
 
     // Time format, using Epoch reference, Relative time format(default),
     // Number, or Cycles
@@ -134,6 +165,26 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
     private Action fHideArrowsAction;
     private Action fFollowArrowFwdAction;
     private Action fFollowArrowBwdAction;
+    private ShowFilterDialogAction fShowFilterDialogAction;
+    private Action fToggleBookmarkAction;
+    private Action fNextMarkerAction;
+    private Action fPreviousMarkerAction;
+    private MenuManager fMarkersMenu;
+
+    /** The list of bookmarks */
+    private final List<IMarkerEvent> fBookmarks = new ArrayList<>();
+
+    /** The list of marker categories */
+    private final List<String> fMarkerCategories = new ArrayList<>();
+
+    /** The set of hidden marker categories */
+    private final Set<String> fHiddenMarkerCategories = new HashSet<>();
+
+    /** The set of skipped marker categories */
+    private final Set<String> fSkippedMarkerCategories = new HashSet<>();
+
+    /** The list of markers */
+    private final List<IMarkerEvent> fMarkers = new ArrayList<>();
 
     private ListenerNotifier fListenerNotifier;
 
@@ -207,6 +258,17 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
         }
     }
 
+    private final static class MarkerComparator implements Comparator<IMarkerEvent> {
+        @Override
+        public int compare(IMarkerEvent o1, IMarkerEvent o2) {
+            int res = Long.compare(o1.getTime(), o2.getTime());
+            if (res != 0) {
+                return res;
+            }
+            return Long.compare(o1.getDuration(), o2.getDuration());
+        }
+    }
+
     /**
      * Standard constructor.
      * <p>
@@ -254,6 +316,36 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
         fToolTipHandler.activateHoverHelp(fTimeGraphCtrl);
     }
 
+    /**
+     * Sets the tree columns for this time graph combo's filter dialog.
+     *
+     * @param columnNames the tree column names
+     * @since 2.0
+     */
+    public void setFilterColumns(String[] columnNames) {
+        getShowFilterDialogAction().getFilterDialog().setColumnNames(columnNames);
+    }
+
+    /**
+     * Sets the tree content provider used by the filter dialog
+     *
+     * @param contentProvider the tree content provider
+     * @since 2.0
+     */
+    public void setFilterContentProvider(ITreeContentProvider contentProvider) {
+        getShowFilterDialogAction().getFilterDialog().setContentProvider(contentProvider);
+    }
+
+    /**
+     * Sets the tree label provider used by the filter dialog
+     *
+     * @param labelProvider the tree label provider
+     * @since 2.0
+     */
+    public void setFilterLabelProvider(ITableLabelProvider labelProvider) {
+        getShowFilterDialogAction().getFilterDialog().setLabelProvider(labelProvider);
+    }
+
     /**
      * Sets or clears the input for this time graph viewer.
      *
@@ -270,6 +362,7 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
             setTopIndex(0);
             fSelectionBegin = SWT.DEFAULT;
             fSelectionEnd = SWT.DEFAULT;
+            updateMarkerActions();
             fSelectedEntry = null;
             refreshAllData(input);
         }
@@ -362,15 +455,23 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
      */
     protected Control createDataViewer(Composite parent, int style) {
         loadOptions();
-        fColorScheme = new TimeGraphColorScheme();
         fDataViewer = new Composite(parent, style) {
             @Override
             public void redraw() {
                 fTimeScaleCtrl.redraw();
                 fTimeGraphCtrl.redraw();
+                fMarkerAxisCtrl.redraw();
                 super.redraw();
             }
         };
+        fDataViewer.addDisposeListener(new DisposeListener() {
+            @Override
+            public void widgetDisposed(DisposeEvent e) {
+                if (fMarkersMenu != null) {
+                    fMarkersMenu.dispose();
+                }
+            }
+        });
         GridLayout gl = new GridLayout(2, false);
         gl.marginHeight = fBorderWidth;
         gl.marginWidth = 0;
@@ -419,15 +520,38 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
         fTimeGraphCtrl.addKeyListener(new KeyAdapter() {
             @Override
             public void keyPressed(KeyEvent e) {
-                if (e.character == '+') {
+                if ((e.character == '+' || e.character == '=') && ((e.stateMask & SWT.CTRL) == 0)) {
                     zoomIn();
-                } else if (e.character == '-') {
+                } else if (e.character == '-' && ((e.stateMask & SWT.CTRL) == 0)) {
                     zoomOut();
+                } else if (e.keyCode == '.') {
+                    boolean extend = (e.stateMask & SWT.SHIFT) != 0;
+                    if (extend) {
+                        extendToNextMarker();
+                    } else {
+                        selectNextMarker();
+                    }
+                } else if (e.keyCode == ',') {
+                    boolean extend = (e.stateMask & SWT.SHIFT) != 0;
+                    if (extend) {
+                        extendToPrevMarker();
+                    } else {
+                        selectPrevMarker();
+                    }
                 }
                 adjustVerticalScrollBar();
             }
         });
 
+        fMarkerAxisCtrl = createTimeGraphMarkerAxis(fTimeAlignedComposite, fColorScheme, this);
+        fMarkerAxisCtrl.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false));
+        fMarkerAxisCtrl.addMouseWheelListener(new MouseWheelListener() {
+            @Override
+            public void mouseScrolled(MouseEvent e) {
+                fTimeGraphCtrl.zoom(e.count > 0);
+            }
+        });
+
         fVerticalScrollBar = new Slider(fDataViewer, SWT.VERTICAL | SWT.NO_FOCUS);
         fVerticalScrollBar.setLayoutData(new GridData(SWT.DEFAULT, SWT.FILL, false, true, 1, 1));
         fVerticalScrollBar.addSelectionListener(new SelectionAdapter() {
@@ -512,6 +636,23 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
         return new TimeGraphControl(parent, colors);
     }
 
+    /**
+     * Create a new time graph marker axis.
+     *
+     * @param parent
+     *            The parent composite object
+     * @param colorScheme
+     *            The color scheme to use
+     * @param timeProvider
+     *            The time data provider
+     * @return The new TimeGraphMarkerAxis
+     * @since 2.0
+     */
+    protected TimeGraphMarkerAxis createTimeGraphMarkerAxis(Composite parent,
+            @NonNull TimeGraphColorScheme colorScheme, @NonNull ITimeDataProvider timeProvider) {
+        return new TimeGraphMarkerAxis(parent, colorScheme, timeProvider);
+    }
+
     /**
      * Resize the controls
      */
@@ -628,6 +769,8 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
         }
         fTimeGraphCtrl.refreshData(traces);
         fTimeScaleCtrl.redraw();
+        fMarkerAxisCtrl.redraw();
+        updateMarkerActions();
         adjustVerticalScrollBar();
     }
 
@@ -699,6 +842,11 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
         }
         fTimeGraphCtrl.redraw();
         fTimeScaleCtrl.redraw();
+        fMarkerAxisCtrl.redraw();
+        /* force update the controls to keep them aligned */
+        fTimeScaleCtrl.update();
+        fMarkerAxisCtrl.update();
+        fTimeGraphCtrl.update();
     }
 
     @Override
@@ -780,6 +928,11 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
         adjustHorizontalScrollBar();
         fTimeGraphCtrl.redraw();
         fTimeScaleCtrl.redraw();
+        fMarkerAxisCtrl.redraw();
+        /* force update the controls to keep them aligned */
+        fTimeScaleCtrl.update();
+        fMarkerAxisCtrl.update();
+        fTimeGraphCtrl.update();
     }
 
     @Override
@@ -802,57 +955,53 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
         setSelectedTimeInt(time, ensureVisible, false);
     }
 
+    private void setSelectedTimeInt(long time, boolean ensureVisible, boolean doNotify) {
+        setSelectionRangeInt(time, time, ensureVisible, doNotify);
+    }
+
+    /**
+     * @since 2.0
+     */
     @Override
-    public void setSelectionRangeNotify(long beginTime, long endTime) {
-        long time0 = fTime0;
-        long time1 = fTime1;
-        long selectionBegin = fSelectionBegin;
-        long selectionEnd = fSelectionEnd;
-        fSelectionBegin = Math.max(fTime0Bound, Math.min(fTime1Bound, beginTime));
-        fSelectionEnd = Math.max(fTime0Bound, Math.min(fTime1Bound, endTime));
-        boolean changed = (selectionBegin != fSelectionBegin || selectionEnd != fSelectionEnd);
-        ensureVisible(fSelectionEnd);
-        fTimeGraphCtrl.redraw();
-        fTimeScaleCtrl.redraw();
-        if ((time0 != fTime0) || (time1 != fTime1)) {
-            notifyRangeListeners();
-        }
-        if (changed) {
-            notifyTimeListeners();
-        }
+    public void setSelectionRangeNotify(long beginTime, long endTime, boolean ensureVisible) {
+        setSelectionRangeInt(beginTime, endTime, ensureVisible, true);
     }
 
+    /**
+     * @since 2.0
+     */
     @Override
-    public void setSelectionRange(long beginTime, long endTime) {
+    public void setSelectionRange(long beginTime, long endTime, boolean ensureVisible) {
         /* if there is a pending time selection, ignore this one */
         if (fListenerNotifier != null && fListenerNotifier.hasTimeSelected()) {
             return;
         }
-        fSelectionBegin = Math.max(fTime0Bound, Math.min(fTime1Bound, beginTime));
-        fSelectionEnd = Math.max(fTime0Bound, Math.min(fTime1Bound, endTime));
-        fTimeGraphCtrl.redraw();
-        fTimeScaleCtrl.redraw();
+        setSelectionRangeInt(beginTime, endTime, ensureVisible, false);
     }
 
-    private void setSelectedTimeInt(long time, boolean ensureVisible, boolean doNotify) {
-        long selection = Math.max(fTime0Bound, Math.min(fTime1Bound, time));
+    private void setSelectionRangeInt(long beginTime, long endTime, boolean ensureVisible, boolean doNotify) {
         long time0 = fTime0;
         long time1 = fTime1;
+        long selectionBegin = fSelectionBegin;
+        long selectionEnd = fSelectionEnd;
+        fSelectionBegin = Math.max(fTime0Bound, Math.min(fTime1Bound, beginTime));
+        fSelectionEnd = Math.max(fTime0Bound, Math.min(fTime1Bound, endTime));
+        boolean changed = (selectionBegin != fSelectionBegin || selectionEnd != fSelectionEnd);
+
         if (ensureVisible) {
-            ensureVisible(selection);
+            ensureVisible(selectionBegin != fSelectionBegin ? fSelectionBegin : fSelectionEnd);
         }
+
         fTimeGraphCtrl.redraw();
         fTimeScaleCtrl.redraw();
-
-        boolean notifySelectedTime = (selection != fSelectionBegin || selection != fSelectionEnd);
-        fSelectionBegin = selection;
-        fSelectionEnd = selection;
+        fMarkerAxisCtrl.redraw();
+        updateMarkerActions();
 
         if ((time0 != fTime0) || (time1 != fTime1)) {
             notifyRangeListeners();
         }
 
-        if (doNotify && notifySelectedTime) {
+        if (doNotify && changed) {
             notifyTimeListeners();
         }
     }
@@ -1081,6 +1230,112 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
         }
     }
 
+    /**
+     * Add a bookmark listener
+     *
+     * @param listener
+     *            The listener to add
+     * @since 2.0
+     */
+    public void addBookmarkListener(ITimeGraphBookmarkListener listener) {
+        fBookmarkListeners.add(listener);
+    }
+
+    /**
+     * Remove a bookmark listener
+     *
+     * @param listener
+     *            The listener to remove
+     * @since 2.0
+     */
+    public void removeBookmarkListener(ITimeGraphBookmarkListener listener) {
+        fBookmarkListeners.remove(listener);
+    }
+
+    private void fireBookmarkAdded(IMarkerEvent bookmark) {
+        TimeGraphBookmarkEvent event = new TimeGraphBookmarkEvent(this, bookmark);
+
+        for (ITimeGraphBookmarkListener listener : fBookmarkListeners) {
+            listener.bookmarkAdded(event);
+        }
+    }
+
+    private void fireBookmarkRemoved(IMarkerEvent bookmark) {
+        TimeGraphBookmarkEvent event = new TimeGraphBookmarkEvent(this, bookmark);
+
+        for (ITimeGraphBookmarkListener listener : fBookmarkListeners) {
+            listener.bookmarkRemoved(event);
+        }
+    }
+
+    /**
+     * Set the bookmarks list.
+     *
+     * @param bookmarks
+     *            The bookmarks list, or null
+     * @since 2.0
+     */
+    public void setBookmarks(List<IMarkerEvent> bookmarks) {
+        fBookmarks.clear();
+        if (bookmarks != null) {
+            fBookmarks.addAll(bookmarks);
+        }
+        updateMarkerList();
+        updateMarkerActions();
+    }
+
+    /**
+     * Get the bookmarks list.
+     *
+     * @return The bookmarks list
+     * @since 2.0
+     */
+    public List<IMarkerEvent> getBookmarks() {
+        return Collections.unmodifiableList(fBookmarks);
+    }
+
+    /**
+     * Set the list of marker categories.
+     *
+     * @param categories
+     *            The list of marker categories, or null
+     * @since 2.0
+     */
+    public void setMarkerCategories(List<String> categories) {
+        fMarkerCategories.clear();
+        if (categories != null) {
+            fMarkerCategories.addAll(categories);
+        }
+        fMarkerCategories.add(IMarkerEvent.BOOKMARKS);
+        fMarkerAxisCtrl.setMarkerCategories(fMarkerCategories);
+    }
+
+    /**
+     * Set the markers list.
+     *
+     * @param markers
+     *            The markers list, or null
+     * @since 2.0
+     */
+    public void setMarkers(List<IMarkerEvent> markers) {
+        fMarkers.clear();
+        if (markers != null) {
+            fMarkers.addAll(markers);
+        }
+        updateMarkerList();
+        updateMarkerActions();
+    }
+
+    /**
+     * Get the markers list.
+     *
+     * @return The markers list, or null
+     * @since 2.0
+     */
+    public List<IMarkerEvent> getMarkers() {
+        return Collections.unmodifiableList(fMarkers);
+    }
+
     /**
      * Callback to set a selected event in the view
      *
@@ -1465,7 +1720,7 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
      * @param entry
      *            The entry
      * @return true if the entry is expanded, false if collapsed
-     * @since 2.0
+     * @since 1.1
      */
     public boolean getExpandedState(ITimeGraphEntry entry) {
         return fTimeGraphCtrl.getExpandedState(entry);
@@ -1806,6 +2061,326 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
         return fFollowArrowBwdAction;
     }
 
+    /**
+     * Get the show filter dialog action.
+     *
+     * @return The Action object
+     * @since 2.0
+     */
+    public ShowFilterDialogAction getShowFilterDialogAction() {
+        if (fShowFilterDialogAction == null) {
+            fShowFilterDialogAction = new ShowFilterDialogAction(this);
+        }
+        return fShowFilterDialogAction;
+    }
+
+    /**
+     * Get the toggle bookmark action.
+     *
+     * @return The Action object
+     * @since 2.0
+     */
+    public Action getToggleBookmarkAction() {
+        if (fToggleBookmarkAction == null) {
+            fToggleBookmarkAction = new Action() {
+                @Override
+                public void runWithEvent(Event event) {
+                    IMarkerEvent selectedBookmark = getBookmarkAtSelection();
+                    if (selectedBookmark == null) {
+                        final long time = Math.min(fSelectionBegin, fSelectionEnd);
+                        final long duration = Math.max(fSelectionBegin, fSelectionEnd) - time;
+                        final AddBookmarkDialog dialog = new AddBookmarkDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), null);
+                        if (dialog.open() == Window.OK) {
+                            final String label = dialog.getValue();
+                            final RGBA rgba = dialog.getColorValue();
+                            IMarkerEvent bookmark = new MarkerEvent(null, time, duration, IMarkerEvent.BOOKMARKS, rgba, label, true);
+                            fBookmarks.add(bookmark);
+                            updateMarkerList();
+                            updateMarkerActions();
+                            getControl().redraw();
+                            fireBookmarkAdded(bookmark);
+                        }
+                    } else {
+                        fBookmarks.remove(selectedBookmark);
+                        updateMarkerList();
+                        updateMarkerActions();
+                        getControl().redraw();
+                        fireBookmarkRemoved(selectedBookmark);
+                    }
+                }
+            };
+            fToggleBookmarkAction.setText(Messages.TmfTimeGraphViewer_BookmarkActionAddText);
+            fToggleBookmarkAction.setToolTipText(Messages.TmfTimeGraphViewer_BookmarkActionAddText);
+            fToggleBookmarkAction.setImageDescriptor(ADD_BOOKMARK);
+        }
+        return fToggleBookmarkAction;
+    }
+
+    /**
+     * Get the next marker action.
+     *
+     * @return The Action object
+     * @since 2.0
+     */
+    public Action getNextMarkerAction() {
+        if (fNextMarkerAction == null) {
+            fNextMarkerAction = new Action(Messages.TmfTimeGraphViewer_NextMarkerActionText, IAction.AS_DROP_DOWN_MENU) {
+                @Override
+                public void runWithEvent(Event event) {
+                    boolean extend = (event.stateMask & SWT.SHIFT) != 0;
+                    if (extend) {
+                        extendToNextMarker();
+                    } else {
+                        selectNextMarker();
+                    }
+                }
+            };
+            fNextMarkerAction.setToolTipText(Messages.TmfTimeGraphViewer_NextMarkerActionText);
+            fNextMarkerAction.setImageDescriptor(NEXT_BOOKMARK);
+            fNextMarkerAction.setMenuCreator(new IMenuCreator () {
+                Menu menu = null;
+                @Override
+                public void dispose() {
+                    if (menu != null) {
+                        menu.dispose();
+                        menu = null;
+                    }
+                }
+
+                @Override
+                public Menu getMenu(Control parent) {
+                    if (menu != null) {
+                        menu.dispose();
+                    }
+                    menu = new Menu(parent);
+                    for (String category : fMarkerCategories) {
+                        final Action action = new Action(category, IAction.AS_CHECK_BOX) {
+                            @Override
+                            public void runWithEvent(Event event) {
+                                if (isChecked()) {
+                                    fSkippedMarkerCategories.remove(getText());
+                                } else {
+                                    fSkippedMarkerCategories.add(getText());
+                                }
+                                updateMarkerActions();
+                            }
+                        };
+                        action.setEnabled(!fHiddenMarkerCategories.contains(category));
+                        action.setChecked(action.isEnabled() && !fSkippedMarkerCategories.contains(category));
+                        new ActionContributionItem(action).fill(menu, -1);
+                    }
+                    return menu;
+                }
+
+                @Override
+                public Menu getMenu(Menu parent) {
+                    return null;
+                }
+            });
+        }
+        return fNextMarkerAction;
+    }
+
+    /**
+     * Get the previous marker action.
+     *
+     * @return The Action object
+     * @since 2.0
+     */
+    public Action getPreviousMarkerAction() {
+        if (fPreviousMarkerAction == null) {
+            fPreviousMarkerAction = new Action() {
+                @Override
+                public void runWithEvent(Event event) {
+                    boolean extend = (event.stateMask & SWT.SHIFT) != 0;
+                    if (extend) {
+                        extendToPrevMarker();
+                    } else {
+                        selectPrevMarker();
+                    }
+                }
+            };
+            fPreviousMarkerAction.setText(Messages.TmfTimeGraphViewer_PreviousMarkerActionText);
+            fPreviousMarkerAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousMarkerActionText);
+            fPreviousMarkerAction.setImageDescriptor(PREVIOUS_BOOKMARK);
+        }
+        return fPreviousMarkerAction;
+    }
+
+    /**
+     * Get the show markers menu.
+     *
+     * @return The menu manager object
+     * @since 2.0
+     */
+    public MenuManager getMarkersMenu() {
+        if (fMarkersMenu == null) {
+            fMarkersMenu = new MenuManager(Messages.TmfTimeGraphViewer_ShowMarkersMenuText);
+            fMarkersMenu.setRemoveAllWhenShown(true);
+            fMarkersMenu.addMenuListener(new IMenuListener() {
+                @Override
+                public void menuAboutToShow(IMenuManager manager) {
+                    for (String category : fMarkerCategories) {
+                        final Action action = new Action(category, IAction.AS_CHECK_BOX) {
+                            @Override
+                            public void runWithEvent(Event event) {
+                                if (isChecked()) {
+                                    fHiddenMarkerCategories.remove(getText());
+                                } else {
+                                    fHiddenMarkerCategories.add(getText());
+                                }
+                                updateMarkerList();
+                                updateMarkerActions();
+                                getControl().redraw();
+                            }
+                        };
+                        action.setChecked(!fHiddenMarkerCategories.contains(category));
+                        manager.add(action);
+                    }
+                }
+            });
+        }
+        return fMarkersMenu;
+    }
+
+    /**
+     * Select the next marker that begins at or after the current selection
+     * begin time. Markers that begin at the same time are ordered by end time.
+     */
+    private void selectNextMarker() {
+        List<IMarkerEvent> markers = getTimeGraphControl().getMarkers();
+        if (markers == null) {
+            return;
+        }
+        for (IMarkerEvent marker : markers) {
+            final long time = Math.min(fSelectionBegin, fSelectionEnd);
+            final long duration = Math.max(fSelectionBegin, fSelectionEnd) - time;
+            if ((marker.getTime() > time ||
+                    (marker.getTime() == time && marker.getDuration() > duration))
+                    && !fSkippedMarkerCategories.contains(marker.getCategory())) {
+                setSelectionRangeNotify(marker.getTime(), marker.getTime() + marker.getDuration(), true);
+                fTimeGraphCtrl.updateStatusLine();
+                return;
+            }
+        }
+    }
+
+    /**
+     * Select the previous marker that begins at or before the current selection
+     * begin time. Markers that begin at the same time are ordered by end time.
+     */
+    private void selectPrevMarker() {
+        List<IMarkerEvent> markers = getTimeGraphControl().getMarkers();
+        if (markers == null) {
+            return;
+        }
+        final long time = Math.min(fSelectionBegin, fSelectionEnd);
+        final long duration = Math.max(fSelectionBegin, fSelectionEnd) - time;
+        for (int i = markers.size() - 1; i >= 0; i--) {
+            IMarkerEvent marker = markers.get(i);
+            if ((marker.getTime() < time ||
+                    (marker.getTime() == time && marker.getDuration() < duration))
+                    && !fSkippedMarkerCategories.contains(marker.getCategory())) {
+                setSelectionRangeNotify(marker.getTime(), marker.getTime() + marker.getDuration(), true);
+                fTimeGraphCtrl.updateStatusLine();
+                return;
+            }
+        }
+    }
+
+    /**
+     * Extend the selection to the closest next marker end time.
+     */
+    private void extendToNextMarker() {
+        List<IMarkerEvent> markers = getTimeGraphControl().getMarkers();
+        if (markers == null) {
+            return;
+        }
+        IMarkerEvent nextMarker = null;
+        for (IMarkerEvent marker : markers) {
+            if (marker.getTime() + marker.getDuration() > fSelectionEnd
+                    && !fSkippedMarkerCategories.contains(marker.getCategory())
+                    && (nextMarker == null || marker.getTime() + marker.getDuration() < nextMarker.getTime() + nextMarker.getDuration())) {
+                nextMarker = marker;
+            }
+        }
+        if (nextMarker != null) {
+            setSelectionRangeNotify(fSelectionBegin, nextMarker.getTime() + nextMarker.getDuration(), true);
+            fTimeGraphCtrl.updateStatusLine();
+        }
+    }
+
+    /**
+     * Extend the selection to the closest previous marker start time.
+     */
+    private void extendToPrevMarker() {
+        List<IMarkerEvent> markers = getTimeGraphControl().getMarkers();
+        if (markers == null) {
+            return;
+        }
+        for (int i = markers.size() - 1; i >= 0; i--) {
+            IMarkerEvent marker = markers.get(i);
+            if (marker.getTime() < fSelectionEnd
+                    && !fSkippedMarkerCategories.contains(marker.getCategory())) {
+                setSelectionRangeNotify(fSelectionBegin, marker.getTime(), true);
+                fTimeGraphCtrl.updateStatusLine();
+                return;
+            }
+        }
+    }
+
+    private IMarkerEvent getBookmarkAtSelection() {
+        final long time = Math.min(fSelectionBegin, fSelectionEnd);
+        final long duration = Math.max(fSelectionBegin, fSelectionEnd) - time;
+        for (IMarkerEvent bookmark : fBookmarks) {
+            if (bookmark.getTime() == time && bookmark.getDuration() == duration) {
+                return bookmark;
+            }
+        }
+        return null;
+    }
+
+    private void updateMarkerActions() {
+        boolean enabled = fTime0Bound != SWT.DEFAULT || fTime1Bound != SWT.DEFAULT;
+        if (fToggleBookmarkAction != null) {
+            if (getBookmarkAtSelection() != null) {
+                fToggleBookmarkAction.setText(Messages.TmfTimeGraphViewer_BookmarkActionRemoveText);
+                fToggleBookmarkAction.setToolTipText(Messages.TmfTimeGraphViewer_BookmarkActionRemoveText);
+                fToggleBookmarkAction.setImageDescriptor(REMOVE_BOOKMARK);
+            } else {
+                fToggleBookmarkAction.setText(Messages.TmfTimeGraphViewer_BookmarkActionAddText);
+                fToggleBookmarkAction.setToolTipText(Messages.TmfTimeGraphViewer_BookmarkActionAddText);
+                fToggleBookmarkAction.setImageDescriptor(ADD_BOOKMARK);
+            }
+            fToggleBookmarkAction.setEnabled(enabled);
+        }
+        List<IMarkerEvent> markers = getTimeGraphControl().getMarkers();
+        if (markers == null) {
+            markers = Collections.emptyList();
+        }
+        if (fPreviousMarkerAction != null) {
+            fPreviousMarkerAction.setEnabled(enabled && !markers.isEmpty());
+        }
+        if (fNextMarkerAction != null) {
+            fNextMarkerAction.setEnabled(enabled && !markers.isEmpty());
+        }
+    }
+
+    private void updateMarkerList() {
+        List<IMarkerEvent> markers = new ArrayList<>();
+        for (IMarkerEvent marker : fMarkers) {
+            if (!fHiddenMarkerCategories.contains(marker.getCategory())) {
+                markers.add(marker);
+            }
+        }
+        if (!fHiddenMarkerCategories.contains(IMarkerEvent.BOOKMARKS)) {
+            markers.addAll(fBookmarks);
+        }
+        Collections.sort(markers, new MarkerComparator());
+        fTimeGraphCtrl.setMarkers(markers);
+        fMarkerAxisCtrl.setMarkers(markers);
+    }
+
     private void adjustHorizontalScrollBar() {
         long time0 = getTime0();
         long time1 = getTime1();
@@ -1880,7 +2455,7 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
      * @param filter
      *            The filter object to be attached to the view
      */
-    public void addFilter(ViewerFilter filter) {
+    public void addFilter(@NonNull ViewerFilter filter) {
         fTimeGraphCtrl.addFilter(filter);
         refresh();
     }
@@ -1889,7 +2464,7 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
      * @param filter
      *            The filter object to be attached to the view
      */
-    public void removeFilter(ViewerFilter filter) {
+    public void removeFilter(@NonNull ViewerFilter filter) {
         fTimeGraphCtrl.removeFilter(filter);
         refresh();
     }
@@ -1900,7 +2475,7 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
      * @return an array of viewer filters
      * @since 2.0
      */
-    public ViewerFilter[] getFilters() {
+    public @NonNull ViewerFilter[] getFilters() {
         return fTimeGraphCtrl.getFilters();
     }
 
@@ -1912,7 +2487,7 @@ public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
      *            an array of viewer filters, or null
      * @since 2.0
      */
-    public void setFilters(ViewerFilter[] filters) {
+    public void setFilters(@NonNull ViewerFilter[] filters) {
         fTimeGraphCtrl.setFilters(filters);
         refresh();
     }
This page took 0.035268 seconds and 5 git commands to generate.