tmf : Add search dialog to timegraph views
authorJean-Christian Kouame <jean-christian.kouame@ericsson.com>
Tue, 22 Mar 2016 19:15:40 +0000 (15:15 -0400)
committerMatthew Khouzam <matthew.khouzam@ericsson.com>
Wed, 13 Apr 2016 18:10:23 +0000 (14:10 -0400)
The search dialog is based on the Eclipse FindReplaceDialog
implementation.

Change-Id: Id0758fc7e709d1d66778e0fc33d9f848b626cc70
Signed-off-by: Jean-Christian Kouame <jean-christian.kouame@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/69484
Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Reviewed-by: Hudson CI
Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
15 files changed:
analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/analysis/os/linux/ui/views/controlflow/ControlFlowEntry.java
tmf/org.eclipse.tracecompass.tmf.analysis.xml.ui/src/org/eclipse/tracecompass/tmf/analysis/xml/ui/views/timegraph/XmlEntry.java
tmf/org.eclipse.tracecompass.tmf.ui.tests/widgetStubs/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/test/stub/model/TraceImpl.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/ITmfImageConstants.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/Messages.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/messages.properties
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackEntry.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartAnalysisEntry.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/ShowFindDialogAction.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/TimeGraphFindDialog.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/TimeGraphCombo.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/TimeGraphViewer.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/ITimeGraphEntry.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/TimeGraphEntry.java

index 3ec6ba2287eb5d6686a3609f0a43926a413ca5ac..1b7a09b1703e03513ee5bff223e8a4435641defd 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2015 Ericsson, École Polytechnique de Montréal
+ * Copyright (c) 2012, 2016 Ericsson, École Polytechnique de Montréal
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
 
 package org.eclipse.tracecompass.analysis.os.linux.ui.views.controlflow;
 
+import java.util.regex.Pattern;
+
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.Resolution;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
 
 /**
  * An entry in the Control Flow view
@@ -100,6 +105,26 @@ public class ControlFlowEntry extends TimeGraphEntry {
         return fThreadQuark;
     }
 
+    @Override
+    public boolean matches(@NonNull Pattern pattern) {
+        if (pattern.matcher(getName()).find()) {
+            return true;
+        }
+        if (pattern.matcher(Integer.toString(fThreadId)).find()) {
+            return true;
+        }
+        if (pattern.matcher(Integer.toString(fParentThreadId)).find()) {
+            return true;
+        }
+        if (pattern.matcher(Integer.toString(fThreadQuark)).find()) {
+            return true;
+        }
+        if (pattern.matcher(Utils.formatTime(getStartTime(), TimeFormat.CALENDAR, Resolution.NANOSEC)).find()) {
+            return true;
+        }
+        return pattern.matcher(fTrace.getName()).find();
+    }
+
     @Override
     public String toString() {
         return getClass().getSimpleName() + '(' + getName() + '[' + fThreadId + "])"; //$NON-NLS-1$
index 89af65be3a419cebf7c16b49a0bd0acc1ad6608c..58fe9238b46a58db2f59e003cf34363d69bd10cd 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 École Polytechnique de Montréal
+ * Copyright (c) 2014, 2016 École Polytechnique de Montréal and others.
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
@@ -16,6 +16,7 @@ package org.eclipse.tracecompass.tmf.analysis.xml.ui.views.timegraph;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.regex.Pattern;
 
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.internal.tmf.analysis.xml.ui.TmfXmlUiStrings;
@@ -272,4 +273,14 @@ public class XmlEntry extends TimeGraphEntry implements IXmlStateSystemContainer
         return super.getTimeEventsIterator();
     }
 
+    @Override
+    public boolean matches(@NonNull Pattern pattern) {
+        if (pattern.matcher(getName()).find()) {
+            return true;
+        }
+        if (pattern.matcher(fId).find()) {
+            return true;
+        }
+        return pattern.matcher(fParentId).find();
+    }
 }
index 89ee9a86e8645a99f1a9da4713f0a0f7c6b07bed..50b8d726cc1edb7f14441462fee2852175fb3f67 100644 (file)
@@ -14,6 +14,7 @@ package org.eclipse.tracecompass.tmf.ui.widgets.timegraph.test.stub.model;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Vector;
+import java.util.regex.Pattern;
 
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
@@ -116,4 +117,9 @@ public class TraceImpl implements ITimeGraphEntry {
         return false;
     }
 
+    @Override
+    public boolean matches(@NonNull  Pattern pattern) {
+        return pattern.matcher(name).find();
+    }
+
 }
index 3947e3dd8f2959c56f0d6e0b51cf6e48892dd292..fe36eef1876a567a4c623df7599b4af63b95f396 100755 (executable)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2015 Ericsson and others.
+ * Copyright (c) 2011, 2016 Ericsson and 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
@@ -25,6 +25,7 @@ public interface ITmfImageConstants {
     String IMG_UI_ZOOM_IN_MENU = ICONS_PATH + "elcl16/zoomin_nav.gif";
     String IMG_UI_ZOOM_OUT_MENU = ICONS_PATH + "elcl16/zoomout_nav.gif";
     String IMG_UI_FILTERS = ICONS_PATH + "elcl16/filter_items.gif";
+    String IMG_UI_SEARCH = ICONS_PATH + "elcl16/search.gif";
     String IMG_UI_SEARCH_SEQ = ICONS_PATH + "elcl16/search_seqdiag_menu.gif";
     String IMG_UI_NEXT_PAGE = ICONS_PATH + "elcl16/next_menu.gif";
     String IMG_UI_PREV_PAGE = ICONS_PATH + "elcl16/prev_menu.gif";
index 7442c377a1168cb76192b8300d0d6126be85f584..62bda33b17fb4d99e45fc9186729948ccc612fcb 100644 (file)
@@ -107,6 +107,9 @@ public class Messages extends NLS {
     public static String ShowFilterDialogAction_FilterActionNameText;
     public static String ShowFilterDialogAction_FilterActionToolTipText;
 
+    public static String ShowFindDialogAction_Search;
+    public static String ShowFindDialogAction_ShowSearchDialog;
+
     public static String TmfTimeGraphViewer_ResetScaleActionNameText;
     public static String TmfTimeGraphViewer_ResetScaleActionToolTipText;
     public static String TmfTimeGraphViewer_LegendActionNameText;
@@ -154,6 +157,21 @@ public class Messages extends NLS {
     public static String ColorsView_TickButtonText;
     public static String TickColorDialog_TickColorDialogTitle;
 
+    public static String TimeGraphFindDialog_BackwardRadioButtonLabel;
+    public static String TimeGraphFindDialog_CaseCheckBoxLabel;
+    public static String TimeGraphFindDialog_CloseButtonLabel;
+    public static String TimeGraphFindDialog_Direction;
+    public static String TimeGraphFindDialog_FindLabel;
+    public static String TimeGraphFindDialog_FindTitle;
+    public static String TimeGraphFindDialog_FindNextButtonLabel;
+    public static String TimeGraphFindDialog_ForwardRadioButtonLabel;
+    public static String TimeGraphFindDialog_Options;
+    public static String TimeGraphFindDialog_REgExCheckBoxLabel;
+    public static String TimeGraphFindDialog_StatusNoMatchLabel;
+    public static String TimeGraphFindDialog_StatusWrappedLabel;
+    public static String TimeGraphFindDialog_WholeWordCheckBoxLabel;
+    public static String TimeGraphFindDialog_WrapCheckBoxLabel;
+
     public static String CustomTxtParserInputWizardPage_addChildLine;
     public static String CustomTxtParserInputWizardPage_addGroup;
     public static String CustomTxtParserInputWizardPage_addNextLine;
index e37439dc231909dc0f75e83b4d394b29301d692a..e07a4779ba516aa567365a72b0fc98eb839b02bc 100644 (file)
@@ -103,6 +103,10 @@ TmfTimeTipHandler_TRACE_STOP_TIME=Stop Time
 ShowFilterDialogAction_FilterActionNameText=Filter
 ShowFilterDialogAction_FilterActionToolTipText=Show View Filters
 
+# org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.ShowFindDialogAction
+ShowFindDialogAction_Search=search
+ShowFindDialogAction_ShowSearchDialog=Show search dialog
+
 TmfTimeGraphViewer_ResetScaleActionNameText=Reset
 TmfTimeGraphViewer_ResetScaleActionToolTipText=Reset the Time Scale to Default
 TmfTimeGraphViewer_LegendActionNameText=Legend
@@ -151,6 +155,22 @@ ColorsView_MoveUpActionToolTipText=Increase priority
 ColorsView_TickButtonText=Tick
 TickColorDialog_TickColorDialogTitle=Choose tick color
 
+# org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.TimeGraphFindDialog
+TimeGraphFindDialog_BackwardRadioButtonLabel=&Backward
+TimeGraphFindDialog_CaseCheckBoxLabel=&Case sensitive
+TimeGraphFindDialog_CloseButtonLabel=Close
+TimeGraphFindDialog_Direction=Direction
+TimeGraphFindDialog_FindLabel=&Find:
+TimeGraphFindDialog_FindTitle=Find
+TimeGraphFindDialog_FindNextButtonLabel=Fi&nd
+TimeGraphFindDialog_ForwardRadioButtonLabel=F&orward
+TimeGraphFindDialog_Options=Options
+TimeGraphFindDialog_REgExCheckBoxLabel=Regular e&xpression
+TimeGraphFindDialog_StatusNoMatchLabel=Entry not found
+TimeGraphFindDialog_StatusWrappedLabel=Wrapped search
+TimeGraphFindDialog_WholeWordCheckBoxLabel=&Whole word
+TimeGraphFindDialog_WrapCheckBoxLabel=Wra&p search
+
 # org.eclipse.tracecompass.tmf.ui.parsers.wizards
 CustomTxtParserInputWizardPage_addChildLine=Add child line
 CustomTxtParserInputWizardPage_addGroup=Add group
index 868c58a5c57f8fbe097b1540e3c2781424231555..c388cd232ce92d24eb03482957c055077e974581 100644 (file)
@@ -12,6 +12,8 @@
 
 package org.eclipse.tracecompass.tmf.ui.views.callstack;
 
+import java.util.regex.Pattern;
+
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
@@ -182,4 +184,9 @@ public class CallStackEntry extends TimeGraphEntry {
         return fSS;
     }
 
+    @Override
+    public boolean matches(@NonNull Pattern pattern) {
+        return pattern.matcher(fFunctionName).find();
+    }
+
 }
index fde4c706dfad7e2975cfe7ce36144e9afb64f40e..4cb987dab6534a383880157dc614eb11745064b1 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010, 2014 Ericsson
+ * Copyright (c) 2010, 2016 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
@@ -18,6 +18,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Vector;
+import java.util.regex.Pattern;
 
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
@@ -274,4 +275,12 @@ public class TimeChartAnalysisEntry implements ITimeGraphEntry {
     public long getLastRank() {
         return fLastRank;
     }
+
+    /**
+     * @since 2.0
+     */
+    @Override
+    public boolean matches(@NonNull Pattern pattern) {
+        return getName() != null ? pattern.matcher(getName()).find() : false;
+    }
 }
index c615ab215fe886af812dc15cba4ca0879aca6f42..8dc4573f3aa4a0e8fa19cfb529055e8201438984 100644 (file)
@@ -53,6 +53,7 @@ import org.eclipse.jface.action.IStatusLineManager;
 import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.commands.ActionHandler;
 import org.eclipse.jface.viewers.AbstractTreeViewer;
 import org.eclipse.jface.viewers.ILabelProvider;
 import org.eclipse.jface.viewers.ILabelProviderListener;
@@ -74,6 +75,7 @@ import org.eclipse.swt.graphics.RGBA;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.Tree;
 import org.eclipse.swt.widgets.TreeColumn;
 import org.eclipse.tracecompass.internal.tmf.ui.Activator;
@@ -118,7 +120,13 @@ import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
 import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IPartListener;
 import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.handlers.IHandlerActivation;
+import org.eclipse.ui.handlers.IHandlerService;
 
 /**
  * An abstract view all time graph views can inherit
@@ -250,6 +258,21 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
      */
     private final @NonNull MenuManager fEntryMenuManager = new MenuManager();
 
+    /** Time Graph View part listener */
+    private TimeGraphPartListener fPartListener;
+
+    /** Action for the find command. There is only one for all Time Graph views */
+    private static final ShowFindDialogAction FIND_ACTION = new ShowFindDialogAction();
+
+    /** The find action handler */
+    private static ActionHandler fFindActionHandler;
+
+    /** The find handler activation */
+    private static IHandlerActivation fFindHandlerActivation;
+
+    /** The find target to use */
+    private final FindTarget fFindTarget;
+
     // ------------------------------------------------------------------------
     // Classes
     // ------------------------------------------------------------------------
@@ -307,6 +330,9 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         ITimeGraphEntry getSelection();
 
         void setSelection(ITimeGraphEntry selection);
+
+        void selectAndReveal(@NonNull ITimeGraphEntry selection);
+
     }
 
     private class TimeGraphViewerWrapper implements ITimeGraphWrapper {
@@ -445,6 +471,11 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         public void setSelection(ITimeGraphEntry selection) {
             viewer.setSelection(selection);
         }
+
+        @Override
+        public void selectAndReveal(@NonNull ITimeGraphEntry selection) {
+            viewer.selectAndReveal(selection);
+        }
     }
 
     private class TimeGraphComboWrapper implements ITimeGraphWrapper {
@@ -591,6 +622,11 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         public void setSelection(ITimeGraphEntry selection) {
             combo.setSelection(selection);
         }
+
+        @Override
+        public void selectAndReveal(@NonNull ITimeGraphEntry selection) {
+            combo.selectAndReveal(selection);
+        }
     }
 
     /**
@@ -821,6 +857,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         super(id);
         fPresentation = pres;
         fDisplayWidth = Display.getDefault().getBounds().width;
+        fFindTarget = new FindTarget();
     }
 
     // ------------------------------------------------------------------------
@@ -1176,6 +1213,11 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         return Messages.AbstractTimeGraphView_PreviousTooltip;
     }
 
+
+    FindTarget getFindTarget() {
+        return fFindTarget;
+    }
+
     // ------------------------------------------------------------------------
     // ViewPart
     // ------------------------------------------------------------------------
@@ -1296,6 +1338,8 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
 
         createContextMenu();
+        fPartListener = new TimeGraphPartListener();
+        getSite().getPage().addPartListener(fPartListener);
     }
 
     @Override
@@ -1307,6 +1351,7 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
     public void dispose() {
         super.dispose();
         ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+        getSite().getPage().removePartListener(fPartListener);
     }
 
     /**
@@ -2204,6 +2249,60 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
      * @since 2.0
      */
     protected void fillTimeGraphEntryContextMenu (@NonNull IMenuManager menuManager) {
+    }
+
+    /*
+     * Inner classes used for searching
+     */
+    class FindTarget {
+        public ITimeGraphEntry getSelection() {
+            return fTimeGraphWrapper.getSelection();
+        }
 
+        public void selectAndReveal(@NonNull ITimeGraphEntry entry) {
+            fTimeGraphWrapper.selectAndReveal(entry);
+        }
+
+        public ITimeGraphEntry[] getEntries() {
+            TimeGraphViewer viewer = getTimeGraphViewer();
+            return viewer.getTimeGraphContentProvider().getElements(viewer.getInput());
+        }
+
+        public Shell getShell() {
+            return getSite().getShell();
+        }
+    }
+
+    class TimeGraphPartListener implements IPartListener {
+        @Override
+        public void partActivated(IWorkbenchPart part) {
+            if (part == AbstractTimeGraphView.this) {
+                synchronized (FIND_ACTION) {
+                    if (fFindActionHandler == null) {
+                        fFindActionHandler = new ActionHandler(FIND_ACTION);
+                    }
+                    final Object service = PlatformUI.getWorkbench().getService(IHandlerService.class);
+                    fFindHandlerActivation = ((IHandlerService) service).activateHandler(ActionFactory.FIND.getCommandId(), fFindActionHandler);
+                }
+            }
+            // Notify action for all parts
+            FIND_ACTION.partActivated(part);
+        }
+        @Override
+        public void partDeactivated(IWorkbenchPart part) {
+            if ((part == AbstractTimeGraphView.this) && (fFindHandlerActivation != null)) {
+                final Object service = PlatformUI.getWorkbench().getService(IHandlerService.class);
+                ((IHandlerService) service).deactivateHandler(fFindHandlerActivation);
+            }
+        }
+        @Override
+        public void partBroughtToTop(IWorkbenchPart part) {
+        }
+        @Override
+        public void partClosed(IWorkbenchPart part) {
+        }
+        @Override
+        public void partOpened(IWorkbenchPart part) {
+        }
     }
 }
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/ShowFindDialogAction.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/ShowFindDialogAction.java
new file mode 100644 (file)
index 0000000..d2ad6ac
--- /dev/null
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.tracecompass.tmf.ui.views.timegraph;
+
+import org.eclipse.jface.action.Action;
+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.tmf.ui.views.timegraph.AbstractTimeGraphView.FindTarget;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * Action to show a time graph find dialog to search for a
+ * {@link ITimeGraphEntry}
+ *
+ * @author Jean-Christian Kouame
+ */
+class ShowFindDialogAction extends Action {
+
+    private static final int SHELL_MINIMUM_SIZE = 320;
+    private static TimeGraphFindDialog fDialog;
+    private FindTarget fFindTarget;
+
+    /**
+     * Constructor
+     */
+    public ShowFindDialogAction() {
+        setText(Messages.ShowFindDialogAction_Search);
+        setToolTipText(Messages.ShowFindDialogAction_ShowSearchDialog);
+        setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SEARCH));
+    }
+
+    @Override
+    public void run() {
+        FindTarget findTarget = fFindTarget;
+        if (findTarget == null) {
+            return;
+        }
+        checkShell(findTarget);
+        if (fDialog == null) {
+            fDialog = new TimeGraphFindDialog(findTarget.getShell());
+        }
+        if (fDialog != null) {
+            fDialog.update(findTarget);
+            fDialog.open();
+            fDialog.getShell().setMinimumSize(SHELL_MINIMUM_SIZE, SHELL_MINIMUM_SIZE);
+        }
+    }
+
+    /**
+     * Checks if the dialogs shell is the same as the given <code>shell</code>
+     * and if not clears the stub and closes the dialog.
+     *
+     * @param target
+     *            the target that owns the shell to check
+     */
+    public void checkShell(FindTarget target) {
+        if (fDialog != null && !fDialog.isDialogParentShell(target.getShell())) {
+            fDialog.close();
+            fDialog = null;
+        }
+    }
+
+    /**
+     * Define what to do when a part is activated.
+     *
+     * @param part
+     *            The activated workbenchPart
+     */
+    public synchronized void partActivated(IWorkbenchPart part) {
+        FindTarget newTarget = null;
+        if (part instanceof AbstractTimeGraphView) {
+            newTarget = ((AbstractTimeGraphView) part).getFindTarget();
+            if (newTarget != fFindTarget) {
+                fFindTarget = newTarget;
+            }
+        }
+        /*
+         *  Update target in all for all parts. If it is null the dialog will
+         *  disable the find button.
+         */
+        if (fDialog != null) {
+            fDialog.updateTarget(newTarget, false);
+        }
+    }
+
+}
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/TimeGraphFindDialog.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/TimeGraphFindDialog.java
new file mode 100644 (file)
index 0000000..8168c93
--- /dev/null
@@ -0,0 +1,1372 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Ericsson
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *****************************************************************************/
+package org.eclipse.tracecompass.tmf.ui.views.timegraph;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.action.LegacyActionTools;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.fieldassist.ComboContentAdapter;
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider;
+import org.eclipse.jface.util.Util;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+import org.eclipse.tracecompass.internal.tmf.ui.Activator;
+import org.eclipse.tracecompass.internal.tmf.ui.Messages;
+import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractTimeGraphView.FindTarget;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;
+import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+/**
+ * Find dialog to search entries into a time graph. This implementation is based
+ * on the org.eclipse.ui.texteditor.FindReplaceDialog of Eclipse.
+ *
+ * @author Jean-Christian Kouame
+ */
+class TimeGraphFindDialog extends Dialog {
+
+    /**
+     * Updates the find dialog on activation changes.
+     */
+    class ActivationListener extends ShellAdapter {
+        /*
+         * @see ShellListener#shellActivated(ShellEvent)
+         */
+        @Override
+        public void shellActivated(ShellEvent e) {
+            fActiveShell = (Shell) e.widget;
+            updateButtonState();
+
+            if (fGiveFocusToFindField && getShell() == fActiveShell && okToUse(fFindField)) {
+                fFindField.setFocus();
+            }
+
+        }
+
+        /*
+         * @see ShellListener#shellDeactivated(ShellEvent)
+         */
+        @Override
+        public void shellDeactivated(ShellEvent e) {
+            fGiveFocusToFindField = false;
+
+            storeSettings();
+
+            fActiveShell = null;
+            updateButtonState();
+        }
+    }
+
+    /**
+     * Modify listener to update the search result in case of incremental
+     * search.
+     */
+    private class FindModifyListener implements ModifyListener {
+
+        /*
+         * @see ModifyListener#modifyText(ModifyEvent)
+         */
+        @Override
+        public void modifyText(ModifyEvent e) {
+            updateButtonState();
+        }
+    }
+
+    /** The size of the dialogs search history. */
+    private static final int HISTORY_SIZE = 5;
+
+    private final String WRAP = "wrap"; //$NON-NLS-1$
+    private final String CASE_SENSITIVE = "casesensitive"; //$NON-NLS-1$
+    private final String WHOLE_WORD = "wholeword"; //$NON-NLS-1$
+    private final String IS_REGEX_EXPRESSION = "isRegEx"; //$NON-NLS-1$
+    private final String FIND_HISTORY = "findhistory"; //$NON-NLS-1$
+
+    private boolean fWrapInit;
+    private boolean fCaseInit;
+    private boolean fWholeWordInit;
+    private boolean fForwardInit;
+    private boolean fIsRegExInit;
+
+    private @NonNull List<String> fFindHistory;
+
+    private static Shell fParentShell;
+    private Shell fActiveShell;
+
+    private final ActivationListener fActivationListener = new ActivationListener();
+    private final FindModifyListener fFindModifyListener = new FindModifyListener();
+
+    private Label fStatusLabel;
+    private Button fForwardRadioButton;
+    private Button fCaseCheckBox;
+    private Button fWrapCheckBox;
+    private Button fWholeWordCheckBox;
+    private Button fIsRegExCheckBox;
+
+    private Button fFindNextButton;
+    private Combo fFindField;
+
+    /**
+     * Find command adapters.
+     */
+    private ContentAssistCommandAdapter fContentAssistFindField;
+
+    private Rectangle fDialogPositionInit;
+
+    private IDialogSettings fDialogSettings;
+    /**
+     * <code>true</code> if the find field should receive focus the next time
+     * the dialog is activated, <code>false</code> otherwise.
+     */
+    private boolean fGiveFocusToFindField = true;
+
+    /**
+     * Holds the mnemonic/button pairs for all buttons.
+     */
+    private HashMap<Character, Button> fMnemonicButtonMap = new HashMap<>();
+
+    private @Nullable FindTarget fFindTarget;
+
+    /**
+     * Creates a new dialog with the given shell as parent.
+     *
+     * @param parentShell
+     *            the parent shell
+     */
+    public TimeGraphFindDialog(Shell parentShell) {
+        super(parentShell);
+
+        fParentShell = null;
+        fFindTarget = null;
+
+        fDialogPositionInit = null;
+        fFindHistory = new ArrayList<>(HISTORY_SIZE - 1);
+
+        fWrapInit = true;
+        fCaseInit = false;
+        fIsRegExInit = false;
+        fWholeWordInit = false;
+        fForwardInit = true;
+
+        readConfiguration();
+        setShellStyle(getShellStyle() & ~SWT.APPLICATION_MODAL);
+        setBlockOnOpen(false);
+    }
+
+    @Override
+    protected boolean isResizable() {
+        return true;
+    }
+
+    /**
+     * Returns <code>true</code> if control can be used.
+     *
+     * @param control
+     *            the control to be checked
+     * @return <code>true</code> if control can be used
+     */
+    private static boolean okToUse(Control control) {
+        return control != null && !control.isDisposed();
+    }
+
+    @Override
+    public void create() {
+        super.create();
+
+        Shell shell = getShell();
+        shell.addShellListener(fActivationListener);
+
+        // fill in combo contents
+        fFindField.removeModifyListener(fFindModifyListener);
+        updateCombo(fFindField, fFindHistory);
+        fFindField.addModifyListener(fFindModifyListener);
+
+        // get find string
+        initFindStringFromSelection();
+
+        // set dialog position
+        if (fDialogPositionInit != null) {
+            shell.setBounds(fDialogPositionInit);
+        }
+
+        shell.setText(Messages.TimeGraphFindDialog_FindTitle);
+    }
+
+    /**
+     * Creates the options configuration section of the find dialog.
+     *
+     * @param parent
+     *            the parent composite
+     * @return the options configuration section
+     */
+    private Composite createConfigPanel(Composite parent) {
+
+        Composite panel = new Composite(parent, SWT.NONE);
+        GridLayout layout = new GridLayout();
+        panel.setLayout(layout);
+
+        Composite directionGroup = createDirectionGroup(panel);
+        setGridData(directionGroup, SWT.FILL, true, SWT.FILL, false);
+
+        Composite optionsGroup = createOptionsGroup(panel);
+        setGridData(optionsGroup, SWT.FILL, true, SWT.FILL, true);
+        ((GridData) optionsGroup.getLayoutData()).horizontalSpan = 2;
+
+        return panel;
+    }
+
+    @Override
+    protected Control createContents(Composite parent) {
+
+        Composite panel = new Composite(parent, SWT.NULL);
+        GridLayout layout = new GridLayout();
+        layout.numColumns = 1;
+        layout.makeColumnsEqualWidth = true;
+        panel.setLayout(layout);
+        panel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+        Composite inputPanel = createInputPanel(panel);
+        setGridData(inputPanel, SWT.FILL, true, SWT.TOP, false);
+
+        Composite configPanel = createConfigPanel(panel);
+        setGridData(configPanel, SWT.FILL, true, SWT.TOP, true);
+
+        Composite statusBar = createStatusAndCloseButton(panel);
+        setGridData(statusBar, SWT.FILL, true, SWT.BOTTOM, false);
+
+        panel.addTraverseListener(new TraverseListener() {
+            @Override
+            public void keyTraversed(TraverseEvent e) {
+                if (e.detail == SWT.TRAVERSE_RETURN) {
+                    if (!Util.isMac()) {
+                        Control controlWithFocus = getShell().getDisplay().getFocusControl();
+                        if (controlWithFocus != null && (controlWithFocus.getStyle() & SWT.PUSH) == SWT.PUSH) {
+                            return;
+                        }
+                    }
+                    Event event = new Event();
+                    event.type = SWT.Selection;
+                    event.stateMask = e.stateMask;
+                    fFindNextButton.notifyListeners(SWT.Selection, event);
+                    e.doit = false;
+                } else if (e.detail == SWT.TRAVERSE_MNEMONIC) {
+                    Character mnemonic = new Character(Character.toLowerCase(e.character));
+                    Button button = fMnemonicButtonMap.get(mnemonic);
+                    if (button != null) {
+                        if ((fFindField.isFocusControl() || (button.getStyle() & SWT.PUSH) != 0)
+                                && button.isEnabled()) {
+                            Event event = new Event();
+                            event.type = SWT.Selection;
+                            event.stateMask = e.stateMask;
+                            if ((button.getStyle() & SWT.RADIO) != 0) {
+                                Composite buttonParent = button.getParent();
+                                if (buttonParent != null) {
+                                    Control[] children = buttonParent.getChildren();
+                                    for (int i = 0; i < children.length; i++) {
+                                        ((Button) children[i]).setSelection(false);
+                                    }
+                                }
+                                button.setSelection(true);
+                            } else {
+                                button.setSelection(!button.getSelection());
+                            }
+                            button.notifyListeners(SWT.Selection, event);
+                            e.detail = SWT.TRAVERSE_NONE;
+                            e.doit = true;
+                        }
+                    }
+                }
+            }
+        });
+
+        updateButtonState();
+
+        applyDialogFont(panel);
+
+        return panel;
+    }
+
+    private void setContentAssistsEnablement(boolean enable) {
+        fContentAssistFindField.setEnabled(enable);
+    }
+
+    /**
+     * Creates the direction defining part of the options defining section of
+     * the find dialog.
+     *
+     * @param parent
+     *            the parent composite
+     * @return the direction defining part
+     */
+    private Composite createDirectionGroup(Composite parent) {
+
+        Composite panel = new Composite(parent, SWT.NONE);
+        GridLayout layout = new GridLayout();
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        panel.setLayout(layout);
+
+        Group group = new Group(panel, SWT.SHADOW_ETCHED_IN);
+        group.setText(Messages.TimeGraphFindDialog_Direction);
+        GridLayout groupLayout = new GridLayout();
+        group.setLayout(groupLayout);
+        group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+        fForwardRadioButton = new Button(group, SWT.RADIO | SWT.LEFT);
+        fForwardRadioButton.setText(Messages.TimeGraphFindDialog_ForwardRadioButtonLabel);
+        setGridData(fForwardRadioButton, SWT.LEFT, false, SWT.CENTER, false);
+        storeButtonWithMnemonicInMap(fForwardRadioButton);
+
+        Button backwardRadioButton = new Button(group, SWT.RADIO | SWT.LEFT);
+        backwardRadioButton.setText(Messages.TimeGraphFindDialog_BackwardRadioButtonLabel);
+        setGridData(backwardRadioButton, SWT.LEFT, false, SWT.CENTER, false);
+        storeButtonWithMnemonicInMap(backwardRadioButton);
+
+        backwardRadioButton.setSelection(!fForwardInit);
+        fForwardRadioButton.setSelection(fForwardInit);
+
+        return panel;
+    }
+
+    /**
+     * Creates the panel where the user specifies the text to search for
+     *
+     * @param parent
+     *            the parent composite
+     * @return the input panel
+     */
+    private Composite createInputPanel(Composite parent) {
+        Composite panel = new Composite(parent, SWT.NULL);
+        GridLayout layout = new GridLayout();
+        layout.numColumns = 2;
+        panel.setLayout(layout);
+
+        Label findLabel = new Label(panel, SWT.LEFT);
+        findLabel.setText(Messages.TimeGraphFindDialog_FindLabel);
+        setGridData(findLabel, SWT.LEFT, false, SWT.CENTER, false);
+
+        // Create the find content assist field
+        ComboContentAdapter contentAdapter = new ComboContentAdapter();
+        FindReplaceDocumentAdapterContentProposalProvider findProposer = new FindReplaceDocumentAdapterContentProposalProvider(true);
+        fFindField = new Combo(panel, SWT.DROP_DOWN | SWT.BORDER);
+        fContentAssistFindField = new ContentAssistCommandAdapter(
+                fFindField,
+                contentAdapter,
+                findProposer,
+                ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS,
+                new char[0],
+                true);
+        setGridData(fFindField, SWT.FILL, true, SWT.CENTER, false);
+        fFindField.addModifyListener(fFindModifyListener);
+
+        return panel;
+    }
+
+    /**
+     * Creates the functional options part of the options defining section of
+     * the find dialog.
+     *
+     * @param parent
+     *            the parent composite
+     * @return the options group
+     */
+    private Composite createOptionsGroup(Composite parent) {
+
+        Composite panel = new Composite(parent, SWT.NONE);
+        GridLayout layout = new GridLayout();
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        panel.setLayout(layout);
+
+        Group group = new Group(panel, SWT.SHADOW_NONE);
+        group.setText(Messages.TimeGraphFindDialog_Options);
+        GridLayout groupLayout = new GridLayout();
+        groupLayout.numColumns = 1;
+        groupLayout.makeColumnsEqualWidth = true;
+        group.setLayout(groupLayout);
+        group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+        SelectionListener selectionListener = new SelectionListener() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                storeSettings();
+            }
+
+            @Override
+            public void widgetDefaultSelected(SelectionEvent e) {
+            }
+        };
+
+        fCaseCheckBox = new Button(group, SWT.CHECK | SWT.LEFT);
+        fCaseCheckBox.setText(Messages.TimeGraphFindDialog_CaseCheckBoxLabel);
+        setGridData(fCaseCheckBox, SWT.LEFT, false, SWT.CENTER, false);
+        fCaseCheckBox.setSelection(fCaseInit);
+        fCaseCheckBox.addSelectionListener(selectionListener);
+        storeButtonWithMnemonicInMap(fCaseCheckBox);
+
+        fWrapCheckBox = new Button(group, SWT.CHECK | SWT.LEFT);
+        fWrapCheckBox.setText(Messages.TimeGraphFindDialog_WrapCheckBoxLabel);
+        setGridData(fWrapCheckBox, SWT.LEFT, false, SWT.CENTER, false);
+        fWrapCheckBox.setSelection(fWrapInit);
+        fWrapCheckBox.addSelectionListener(selectionListener);
+        storeButtonWithMnemonicInMap(fWrapCheckBox);
+
+        fWholeWordCheckBox = new Button(group, SWT.CHECK | SWT.LEFT);
+        fWholeWordCheckBox.setText(Messages.TimeGraphFindDialog_WholeWordCheckBoxLabel);
+        setGridData(fWholeWordCheckBox, SWT.LEFT, false, SWT.CENTER, false);
+        fWholeWordCheckBox.setSelection(fWholeWordInit);
+        fWholeWordCheckBox.addSelectionListener(selectionListener);
+        storeButtonWithMnemonicInMap(fWholeWordCheckBox);
+
+        fIsRegExCheckBox = new Button(group, SWT.CHECK | SWT.LEFT);
+        fIsRegExCheckBox.setText(Messages.TimeGraphFindDialog_REgExCheckBoxLabel);
+        setGridData(fIsRegExCheckBox, SWT.LEFT, false, SWT.CENTER, false);
+        ((GridData) fIsRegExCheckBox.getLayoutData()).horizontalSpan = 2;
+        fIsRegExCheckBox.setSelection(fIsRegExInit);
+        fIsRegExCheckBox.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                boolean newState = fIsRegExCheckBox.getSelection();
+                updateButtonState();
+                storeSettings();
+                setContentAssistsEnablement(newState);
+            }
+        });
+        storeButtonWithMnemonicInMap(fIsRegExCheckBox);
+        fWholeWordCheckBox.setEnabled(!isRegExSearch());
+        fWholeWordCheckBox.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                updateButtonState();
+            }
+        });
+        return panel;
+    }
+
+    /**
+     * Creates the status and close section of the dialog.
+     *
+     * @param parent
+     *            the parent composite
+     * @return the status and close button
+     */
+    private Composite createStatusAndCloseButton(Composite parent) {
+
+        Composite panel = new Composite(parent, SWT.NULL);
+        GridLayout layout = new GridLayout();
+        layout.numColumns = 2;
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        panel.setLayout(layout);
+
+        fStatusLabel = new Label(panel, SWT.LEFT);
+        setGridData(fStatusLabel, SWT.FILL, true, SWT.CENTER, false);
+
+        Composite buttonSection = new Composite(panel, SWT.NULL);
+        GridLayout buttonLayout = new GridLayout();
+        buttonLayout.numColumns = 2;
+        buttonSection.setLayout(buttonLayout);
+
+        String label = Messages.TimeGraphFindDialog_CloseButtonLabel;
+        Button closeButton = createButton(buttonSection, 101, label, false);
+        setGridData(closeButton, SWT.RIGHT, false, SWT.BOTTOM, false);
+
+        fFindNextButton = makeButton(buttonSection, Messages.TimeGraphFindDialog_FindNextButtonLabel, 102, true, new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                performSearch(((e.stateMask & SWT.SHIFT) != 0) ^ isForwardSearch());
+                updateFindHistory();
+            }
+        });
+        setGridData(fFindNextButton, SWT.FILL, true, SWT.FILL, false);
+
+        return panel;
+    }
+
+    @Override
+    protected void buttonPressed(int buttonID) {
+        if (buttonID == 101) {
+            close();
+        }
+    }
+
+    /**
+     * Update the dialog data (parentShell, listener, input, ...)
+     *
+     * @param findTarget
+     *            the new find target
+     */
+    public void update(@NonNull FindTarget findTarget) {
+        updateTarget(findTarget, true);
+    }
+
+    // ------- action invocation ---------------------------------------
+
+    /**
+     * Returns the index of the entry that match the specified search string, or
+     * <code>-1</code> if the string can not be found when searching using the
+     * given options.
+     *
+     * @param findString
+     *            the string to search for
+     * @param startIndex
+     *            the index at which to start the search
+     * @param items
+     *            The map of items in the time graph view
+     * @param options
+     *            The options use for the search
+     * @return the index of the find entry following the options or
+     *         <code>-1</code> if nothing found
+     */
+    private int findNext(String findString, int startIndex, BiMap<ITimeGraphEntry, Integer> items, SearchOptions options) {
+        int index;
+        if (options.forwardSearch) {
+            index = startIndex == items.size() - 1 ? -1 : findNext(startIndex + 1, findString, items, options);
+        } else {
+            index = startIndex == 0 ? -1 : findNext(startIndex - 1, findString, items, options);
+        }
+
+        if (index == -1) {
+            if (okToUse(getShell())) {
+                getShell().getDisplay().beep();
+            }
+            if (options.wrapSearch) {
+                statusMessage(Messages.TimeGraphFindDialog_StatusWrappedLabel);
+                index = findNext(-1, findString, items, options);
+            }
+        }
+        return index;
+    }
+
+    private int findNext(int startIndex, String findString, BiMap<ITimeGraphEntry, Integer> items, SearchOptions options) {
+        if (fFindTarget != null) {
+            if (findString == null || findString.length() == 0) {
+                return -1;
+            }
+
+            final @NonNull Pattern pattern = getPattern(findString, options);
+            int index = adjustIndex(startIndex, items.size(), options.forwardSearch);
+            BiMap<Integer, ITimeGraphEntry> entries = items.inverse();
+            while (index >= 0 && index < entries.size()) {
+                final @Nullable ITimeGraphEntry entry = entries.get(index);
+                if (entry != null && entry.matches(pattern)) {
+                    return index;
+                }
+                index = options.forwardSearch ? ++index : --index;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns whether the specified search string can be found using the given
+     * options.
+     *
+     * @param findString
+     *            the string to search for
+     * @param options
+     *            The search options
+     * @return <code>true</code> if the search string can be found using the
+     *         given options
+     *
+     */
+    private boolean findAndSelect(String findString, SearchOptions options) {
+        FindTarget findTarget = fFindTarget;
+        if (findTarget == null) {
+            return false;
+        }
+        ITimeGraphEntry[] topInput = findTarget.getEntries();
+        BiMap<@NonNull ITimeGraphEntry, @NonNull Integer> items = HashBiMap.create();
+        for (ITimeGraphEntry entry : topInput) {
+            listEntries(items, entry);
+        }
+        int startPosition = findTarget.getSelection() == null ? 0 : NonNullUtils.checkNotNull(items.get(findTarget.getSelection()));
+
+        int index = findNext(findString, startPosition, items, options);
+
+        if (index == -1) {
+            statusMessage(Messages.TimeGraphFindDialog_StatusNoMatchLabel);
+            return false;
+        }
+
+        if (options.forwardSearch && index >= startPosition || !options.forwardSearch && index <= startPosition) {
+            statusMessage(""); //$NON-NLS-1$
+        }
+
+        // Send the entry found to target
+        findTarget.selectAndReveal(NonNullUtils.checkNotNull(items.inverse().get(index)));
+        return true;
+    }
+
+    private void listEntries(Map<ITimeGraphEntry, Integer> items, ITimeGraphEntry root) {
+        items.put(root, items.size());
+        for (ITimeGraphEntry child : root.getChildren()) {
+            listEntries(items, child);
+        }
+    }
+
+    // ------- accessors ---------------------------------------
+
+    /**
+     * Retrieves the string to search for from the appropriate text input field
+     * and returns it.
+     *
+     * @return the search string
+     */
+    private String getFindString() {
+        if (okToUse(fFindField)) {
+            return fFindField.getText();
+        }
+        return ""; //$NON-NLS-1$
+    }
+
+    /**
+     * Returns the dialog's boundaries.
+     *
+     * @return the dialog's boundaries
+     */
+    private Rectangle getDialogBoundaries() {
+        if (okToUse(getShell())) {
+            return getShell().getBounds();
+        }
+        return fDialogPositionInit;
+    }
+
+    // ------- init / close ---------------------------------------
+    @Override
+    public boolean close() {
+        handleDialogClose();
+        return super.close();
+    }
+
+    /**
+     * Removes focus changed listener from browser and stores settings for
+     * re-open.
+     */
+    private void handleDialogClose() {
+
+        // remove listeners
+        if (okToUse(fFindField)) {
+            fFindField.removeModifyListener(fFindModifyListener);
+        }
+        updateTarget(null, false);
+
+        if (fParentShell != null) {
+            fParentShell.removeShellListener(fActivationListener);
+            fParentShell = null;
+        }
+
+        if (getShell() != null && !getShell().isDisposed()) {
+            getShell().removeShellListener(fActivationListener);
+        }
+
+        // store current settings in case of re-open
+        storeSettings();
+
+        // prevent leaks
+        fActiveShell = null;
+
+    }
+
+    /**
+     * Writes the current selection to the dialog settings.
+     */
+    private void writeSelection() {
+        final FindTarget input = fFindTarget;
+        if (input == null) {
+            return;
+        }
+
+        IDialogSettings s = getDialogSettings();
+        final ITimeGraphEntry selection = input.getSelection();
+        if (selection != null) {
+            s.put("selection", selection.getName()); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Stores the current state in the dialog settings.
+     */
+    private void storeSettings() {
+        fDialogPositionInit = getDialogBoundaries();
+        fWrapInit = isWrapSearch();
+        fWholeWordInit = isWholeWordSetting();
+        fCaseInit = isCaseSensitiveSearch();
+        fIsRegExInit = isRegExSearch();
+        fForwardInit = isForwardSearch();
+
+        writeConfiguration();
+    }
+
+    /**
+     * Initializes the string to search for and the appropriate text in the Find
+     * field based on the selection found in the timegraph view.
+     */
+    private void initFindStringFromSelection() {
+        FindTarget findTarget = fFindTarget;
+        if (findTarget != null && okToUse(fFindField)) {
+            final ITimeGraphEntry selection = findTarget.getSelection();
+            if (selection != null) {
+                String fullSelection = selection.getName();
+                fFindField.removeModifyListener(fFindModifyListener);
+                if (fullSelection.length() > 0) {
+                    fFindField.setText(fullSelection);
+                }
+            } else {
+                if ("".equals(fFindField.getText())) { //$NON-NLS-1$
+                    if (fFindHistory.size() > 0) {
+                        fFindField.setText(fFindHistory.get(0));
+                    } else {
+                        fFindField.setText(""); //$NON-NLS-1$
+                    }
+                }
+            }
+            fFindField.setSelection(new Point(0, fFindField.getText().length()));
+            fFindField.addModifyListener(fFindModifyListener);
+        }
+    }
+
+    // ------- Options ---------------------------------------
+
+    /**
+     * Retrieves and returns the option case sensitivity from the appropriate
+     * check box.
+     *
+     * @return <code>true</code> if case sensitive
+     */
+    private boolean isCaseSensitiveSearch() {
+        if (okToUse(fCaseCheckBox)) {
+            return fCaseCheckBox.getSelection();
+        }
+        return fCaseInit;
+    }
+
+    /**
+     * Retrieves and returns the regEx option from the appropriate check box.
+     *
+     * @return <code>true</code> if case sensitive
+     */
+    private boolean isRegExSearch() {
+        if (okToUse(fIsRegExCheckBox)) {
+            return fIsRegExCheckBox.getSelection();
+        }
+        return fIsRegExInit;
+    }
+
+    /**
+     * Retrieves and returns the option search direction from the appropriate
+     * check box.
+     *
+     * @return <code>true</code> if searching forward
+     */
+    private boolean isForwardSearch() {
+        if (okToUse(fForwardRadioButton)) {
+            return fForwardRadioButton.getSelection();
+        }
+        return fForwardInit;
+    }
+
+    /**
+     * Retrieves and returns the option search whole words from the appropriate
+     * check box.
+     *
+     * @return <code>true</code> if searching for whole words
+     */
+    private boolean isWholeWordSetting() {
+        if (okToUse(fWholeWordCheckBox)) {
+            return fWholeWordCheckBox.getSelection();
+        }
+        return fWholeWordInit;
+    }
+
+    /**
+     * Returns <code>true</code> if searching should be restricted to entire
+     * words, <code>false</code> if not. This is the case if the respective
+     * checkbox is turned on, regex is off, and the checkbox is enabled, i.e.
+     * the current find string is an entire word.
+     *
+     * @return <code>true</code> if the search is restricted to whole words
+     */
+    private boolean isWholeWordSearch() {
+        return isWholeWordSetting() && !isRegExSearch() && (okToUse(fWholeWordCheckBox) ? fWholeWordCheckBox.isEnabled() : true);
+    }
+
+    /**
+     * Retrieves and returns the option wrap search from the appropriate check
+     * box.
+     *
+     * @return <code>true</code> if wrapping while searching
+     */
+    private boolean isWrapSearch() {
+        if (okToUse(fWrapCheckBox)) {
+            return fWrapCheckBox.getSelection();
+        }
+        return fWrapInit;
+    }
+
+    /**
+     * Creates a button.
+     *
+     * @param parent
+     *            the parent control
+     * @param label
+     *            the button label
+     * @param id
+     *            the button id
+     * @param dfltButton
+     *            is this button the default button
+     * @param listener
+     *            a button pressed listener
+     * @return the new button
+     */
+    private Button makeButton(Composite parent, String label, int id, boolean dfltButton, SelectionListener listener) {
+        Button button = createButton(parent, id, label, dfltButton);
+        button.addSelectionListener(listener);
+        storeButtonWithMnemonicInMap(button);
+        return button;
+    }
+
+    /**
+     * Stores the button and its mnemonic in {@link #fMnemonicButtonMap}.
+     *
+     * @param button
+     *            button whose mnemonic has to be stored
+     */
+    private void storeButtonWithMnemonicInMap(Button button) {
+        char mnemonic = LegacyActionTools.extractMnemonic(button.getText());
+        if (mnemonic != LegacyActionTools.MNEMONIC_NONE) {
+            fMnemonicButtonMap.put(new Character(Character.toLowerCase(mnemonic)), button);
+        }
+    }
+
+    /**
+     * Sets the given status message in the status line.
+     *
+     * @param message
+     *            the message
+     */
+    private void statusMessage(String message) {
+        fStatusLabel.setText(message);
+        getShell().getDisplay().beep();
+    }
+
+    /**
+     * Locates the user's findString in the entries information of the time
+     * graph view.
+     *
+     * @param forwardSearch
+     *            the search direction
+     */
+    private void performSearch(boolean forwardSearch) {
+
+        String findString = getFindString();
+        if (findString != null && findString.length() > 0) {
+            findAndSelect(findString, getSearchOptions(forwardSearch));
+        }
+        writeSelection();
+        updateButtonState();
+    }
+
+    private SearchOptions getSearchOptions(boolean forwardSearch) {
+        SearchOptions options = new SearchOptions();
+        options.forwardSearch = forwardSearch;
+        options.caseSensitive = isCaseSensitiveSearch();
+        options.wrapSearch = isWrapSearch();
+        options.wholeWord = isWholeWordSearch();
+        options.regExSearch = isRegExSearch();
+        return options;
+    }
+
+    // ------- UI creation ---------------------------------------
+
+    /**
+     * Attaches the given layout specification to the <code>component</code>.
+     *
+     * @param component
+     *            the component
+     * @param horizontalAlignment
+     *            horizontal alignment
+     * @param grabExcessHorizontalSpace
+     *            grab excess horizontal space
+     * @param verticalAlignment
+     *            vertical alignment
+     * @param grabExcessVerticalSpace
+     *            grab excess vertical space
+     */
+    private static void setGridData(Control component, int horizontalAlignment, boolean grabExcessHorizontalSpace, int verticalAlignment, boolean grabExcessVerticalSpace) {
+        GridData gd;
+        if (component instanceof Button && (((Button) component).getStyle() & SWT.PUSH) != 0) {
+            gd = (GridData) component.getLayoutData();
+            gd.horizontalAlignment = GridData.FILL;
+            gd.widthHint = 100;
+        } else {
+            gd = new GridData();
+            component.setLayoutData(gd);
+            gd.horizontalAlignment = horizontalAlignment;
+            gd.grabExcessHorizontalSpace = grabExcessHorizontalSpace;
+        }
+        gd.verticalAlignment = verticalAlignment;
+        gd.grabExcessVerticalSpace = grabExcessVerticalSpace;
+    }
+
+    /**
+     * Updates the enabled state of the buttons.
+     */
+    private void updateButtonState() {
+        if (okToUse(getShell()) && okToUse(fFindNextButton)) {
+
+            boolean enable = fFindTarget != null && (fActiveShell == fParentShell || fActiveShell == getShell());
+            String str = getFindString();
+            boolean findString = str != null && str.length() > 0;
+
+            fWholeWordCheckBox.setEnabled(isWord(str) && !isRegExSearch());
+
+            fFindNextButton.setEnabled(enable && findString);
+        }
+    }
+
+    /**
+     * Tests whether each character in the given string is a letter.
+     *
+     * @param str
+     *            the string to check
+     * @return <code>true</code> if the given string is a word
+     */
+    private static boolean isWord(String str) {
+        if (str == null || str.length() == 0) {
+            return false;
+        }
+
+        for (int i = 0; i < str.length(); i++) {
+            if (!Character.isJavaIdentifierPart(str.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Updates the given combo with the given content.
+     *
+     * @param combo
+     *            combo to be updated
+     * @param content
+     *            to be put into the combo
+     */
+    private static void updateCombo(Combo combo, List<String> content) {
+        combo.removeAll();
+        for (int i = 0; i < content.size(); i++) {
+            combo.add(content.get(i).toString());
+        }
+    }
+
+    // ------- open / reopen ---------------------------------------
+
+    /**
+     * Called after executed find action to update the history.
+     */
+    private void updateFindHistory() {
+        if (okToUse(fFindField)) {
+            fFindField.removeModifyListener(fFindModifyListener);
+
+            updateHistory(fFindField, fFindHistory);
+            fFindField.addModifyListener(fFindModifyListener);
+        }
+    }
+
+    /**
+     * Updates the combo with the history.
+     *
+     * @param combo
+     *            to be updated
+     * @param history
+     *            to be put into the combo
+     */
+    private static void updateHistory(Combo combo, List<String> history) {
+        String findString = combo.getText();
+        int index = history.indexOf(findString);
+        if (index != 0) {
+            if (index != -1) {
+                history.remove(index);
+            }
+            history.add(0, findString);
+            Point selection = combo.getSelection();
+            updateCombo(combo, history);
+            combo.setText(findString);
+            combo.setSelection(selection);
+        }
+    }
+
+    /**
+     * Sets the parent shell of this dialog to be the given shell.
+     *
+     * @param shell
+     *            the new parent shell
+     */
+    @Override
+    public void setParentShell(Shell shell) {
+        if (shell != fParentShell) {
+
+            if (fParentShell != null) {
+                fParentShell.removeShellListener(fActivationListener);
+            }
+
+            fParentShell = shell;
+            fParentShell.addShellListener(fActivationListener);
+        }
+
+        fActiveShell = shell;
+    }
+
+    // --------------- configuration handling --------------
+
+    /**
+     * Returns the dialog settings object used to share state of the find
+     * dialog.
+     *
+     * @return the dialog settings to be used
+     */
+    private IDialogSettings getDialogSettings() {
+        IDialogSettings settings = Activator.getDefault().getDialogSettings();
+        fDialogSettings = settings.getSection(getClass().getName());
+        if (fDialogSettings == null) {
+            fDialogSettings = settings.addNewSection(getClass().getName());
+        }
+        return fDialogSettings;
+    }
+
+    /**
+     * Initializes itself from the dialog settings with the same state as at the
+     * previous invocation.
+     */
+    private void readConfiguration() {
+        IDialogSettings s = getDialogSettings();
+
+        fWrapInit = s.get(WRAP) == null || s.getBoolean(WRAP);
+        fCaseInit = s.getBoolean(CASE_SENSITIVE);
+        fWholeWordInit = s.getBoolean(WHOLE_WORD);
+        fIsRegExInit = s.getBoolean(IS_REGEX_EXPRESSION);
+
+        String[] findHistory = s.getArray(FIND_HISTORY);
+        if (findHistory != null) {
+            fFindHistory.clear();
+            for (int i = 0; i < findHistory.length; i++) {
+                fFindHistory.add(findHistory[i]);
+            }
+        }
+    }
+
+    /**
+     * Stores its current configuration in the dialog store.
+     */
+    private void writeConfiguration() {
+        IDialogSettings s = getDialogSettings();
+
+        s.put(WRAP, fWrapInit);
+        s.put(CASE_SENSITIVE, fCaseInit);
+        s.put(WHOLE_WORD, fWholeWordInit);
+        s.put(CASE_SENSITIVE, fIsRegExInit);
+
+        String findString = getFindString();
+        if (findString.length() > 0) {
+            fFindHistory.add(0, findString);
+        }
+        writeHistory(fFindHistory, s, FIND_HISTORY);
+    }
+
+    /**
+     * Writes the given history into the given dialog store.
+     *
+     * @param history
+     *            the history
+     * @param settings
+     *            the dialog settings
+     * @param sectionName
+     *            the section name
+     */
+    private static void writeHistory(List<String> history, IDialogSettings settings, String sectionName) {
+        int itemCount = history.size();
+        Set<String> distinctItems = new HashSet<>(itemCount);
+        for (int i = 0; i < itemCount; i++) {
+            String item = history.get(i);
+            if (distinctItems.contains(item)) {
+                history.remove(i--);
+                itemCount--;
+            } else {
+                distinctItems.add(item);
+            }
+        }
+
+        while (history.size() > 8) {
+            history.remove(8);
+        }
+
+        String[] names = new String[history.size()];
+        history.toArray(names);
+        settings.put(sectionName, names);
+
+    }
+
+    /**
+     * Update the time graph viewer this dialog search in
+     *
+     * @param findTarget
+     *            The find target. Can be <code>null</code>.
+     * @param initFindString
+     *            This value should be true if the find text field needs to be
+     *            populated with selected entry in the find viewer, false
+     *            otherwise
+     */
+    public void updateTarget(@Nullable FindTarget findTarget, boolean initFindString) {
+        fFindTarget = findTarget;
+        if (initFindString) {
+            initFindStringFromSelection();
+        }
+        updateButtonState();
+    }
+
+    /**
+     * Test if the shell to test is the parent shell of the dialog
+     *
+     * @param shell
+     *            The shell to test
+     * @return <code>true</code> if the shell to test is the parent shell,
+     *         <code>false</code> otherwise
+     */
+    public boolean isDialogParentShell(Shell shell) {
+        return shell == getParentShell();
+    }
+
+    /**
+     * Adjust the index where to start the search
+     *
+     * @param previousIndex
+     *            The previous index
+     * @param count
+     *            The number of items to search into
+     * @param forwardSearch
+     *            The search direction
+     * @return The adjusted index
+     */
+    private static int adjustIndex(int previousIndex, int count, boolean forwardSearch) {
+        int index = previousIndex;
+        if (forwardSearch) {
+            index = index >= count || index < 0 ? 0 : index;
+        } else {
+            index = index >= count || index < 0 ? count - 1 : index;
+        }
+        return index;
+    }
+
+    /**
+     * Create a pattern from the string to find and with options provided
+     *
+     * This implementation is drawn from the jface implementation of
+     * org.eclipse.jface.text.FindReplaceDocumentAdapter
+     *
+     * @param findString
+     *            The string to find
+     * @param caseSensitive
+     *            Tells if the pattern will activate the case sensitive flag
+     * @param wholeWord
+     *            Tells if the pattern will activate the whole word flag
+     * @param regExSearch
+     *            Tells if the pattern will activate the regEx flag
+     * @return The created pattern
+     */
+    private static @NonNull Pattern getPattern(String findString, SearchOptions options) {
+        String toFind = findString;
+
+        int patternFlags = 0;
+
+        if (options.regExSearch) {
+            toFind = substituteLinebreak(toFind);
+        }
+
+        if (!options.caseSensitive) {
+            patternFlags |= Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
+        }
+
+        if (!options.regExSearch) {
+            toFind = asRegPattern(toFind);
+        }
+
+        if (options.wholeWord) {
+            toFind = "\\b" + toFind + "\\b"; //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        Pattern pattern = Pattern.compile(toFind, patternFlags);
+        return pattern;
+    }
+
+    /**
+     * Substitutes \R in a regex find pattern with (?>\r\n?|\n)
+     *
+     * This implementation is drawn from the jface implementation of
+     * org.eclipse.jface.text.FindReplaceDocumentAdapter
+     *
+     * @param findString
+     *            the original find pattern
+     * @return the transformed find pattern
+     */
+    private static String substituteLinebreak(String findString) {
+        int length = findString.length();
+        StringBuffer buf = new StringBuffer(length);
+
+        int inCharGroup = 0;
+        int inBraces = 0;
+        boolean inQuote = false;
+        for (int i = 0; i < length; i++) {
+            char ch = findString.charAt(i);
+            switch (ch) {
+            case '[':
+                buf.append(ch);
+                if (!inQuote) {
+                    inCharGroup++;
+                }
+                break;
+
+            case ']':
+                buf.append(ch);
+                if (!inQuote) {
+                    inCharGroup--;
+                }
+                break;
+
+            case '{':
+                buf.append(ch);
+                if (!inQuote && inCharGroup == 0) {
+                    inBraces++;
+                }
+                break;
+
+            case '}':
+                buf.append(ch);
+                if (!inQuote && inCharGroup == 0) {
+                    inBraces--;
+                }
+                break;
+
+            case '\\':
+                if (i + 1 < length) {
+                    char ch1 = findString.charAt(i + 1);
+                    if (inQuote) {
+                        if (ch1 == 'E') {
+                            inQuote = false;
+                        }
+                        buf.append(ch).append(ch1);
+                        i++;
+
+                    } else if (ch1 == 'R') {
+                        if (inCharGroup > 0 || inBraces > 0) {
+                            String msg = "TimeGrahViewer.illegalLinebreak"; //$NON-NLS-1$
+                            throw new PatternSyntaxException(msg, findString, i);
+                        }
+                        buf.append("(?>\\r\\n?|\\n)"); //$NON-NLS-1$
+                        i++;
+
+                    } else {
+                        if (ch1 == 'Q') {
+                            inQuote = true;
+                        }
+                        buf.append(ch).append(ch1);
+                        i++;
+                    }
+                } else {
+                    buf.append(ch);
+                }
+                break;
+
+            default:
+                buf.append(ch);
+                break;
+            }
+
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Converts a non-regex string to a pattern that can be used with the regex
+     * search engine.
+     *
+     * This implementation is drawn from the jface implementation of
+     * org.eclipse.jface.text.FindReplaceDocumentAdapter
+     *
+     * @param string
+     *            the non-regex pattern
+     * @return the string converted to a regex pattern
+     */
+    private static String asRegPattern(String string) {
+        StringBuffer out = new StringBuffer(string.length());
+        boolean quoting = false;
+
+        for (int i = 0, length = string.length(); i < length; i++) {
+            char ch = string.charAt(i);
+            if (ch == '\\') {
+                if (quoting) {
+                    out.append("\\E"); //$NON-NLS-1$
+                    quoting = false;
+                }
+                out.append("\\\\"); //$NON-NLS-1$
+                continue;
+            }
+            if (!quoting) {
+                out.append("\\Q"); //$NON-NLS-1$
+                quoting = true;
+            }
+            out.append(ch);
+        }
+        if (quoting) {
+            out.append("\\E"); //$NON-NLS-1$
+        }
+
+        return out.toString();
+    }
+
+    private class SearchOptions {
+        boolean forwardSearch;
+        boolean caseSensitive;
+        boolean wrapSearch;
+        boolean wholeWord;
+        boolean regExSearch;
+    }
+}
index 8601b41e47fa2430be4b23f0402ee00695af796c..bc582af9140c3c0ee1fdb5869c5f36726a1ce245 100644 (file)
@@ -94,7 +94,9 @@ public class TimeGraphCombo extends Composite {
     // Constants
     // ------------------------------------------------------------------------
 
-    /** Constant indicating that all levels of the time graph should be expanded */
+    /**
+     * Constant indicating that all levels of the time graph should be expanded
+     */
     public static final int ALL_LEVELS = AbstractTreeViewer.ALL_LEVELS;
 
     private static final Object FILLER = new Object();
@@ -183,8 +185,8 @@ public class TimeGraphCombo extends Composite {
                 @Override
                 public void setElementPosition(ITimeGraphEntry entry, int y) {
                     /*
-                     * Queue the update to make sure the time graph combo has finished
-                     * updating the item heights.
+                     * Queue the update to make sure the time graph combo has
+                     * finished updating the item heights.
                      */
                     getDisplay().asyncExec(() -> {
                         super.setElementPosition(entry, y);
@@ -252,8 +254,8 @@ public class TimeGraphCombo extends Composite {
             Tree tree = fTreeViewer.getTree();
             tree.addPaintListener(e -> {
                 /*
-                 * Draw the marker axis over the tree. Only the name area
-                 * will be drawn, since the time area has zero width.
+                 * Draw the marker axis over the tree. Only the name area will
+                 * be drawn, since the time area has zero width.
                  */
                 Rectangle bounds = timeGraphMarkerAxis.getAxisBounds();
                 e.gc.setBackground(timeGraphMarkerAxis.getBackground());
@@ -279,8 +281,8 @@ public class TimeGraphCombo extends Composite {
     }
 
     /**
-     * The TreeContentProviderWrapper is used to insert filler items after
-     * the elements of the tree's real content provider.
+     * The TreeContentProviderWrapper is used to insert filler items after the
+     * elements of the tree's real content provider.
      */
     private class TreeContentProviderWrapper implements ITreeContentProvider {
         private final ITreeContentProvider contentProvider;
@@ -336,8 +338,8 @@ public class TimeGraphCombo extends Composite {
     }
 
     /**
-     * The TreeLabelProviderWrapper is used to intercept the filler items
-     * from the calls to the tree's real label provider.
+     * The TreeLabelProviderWrapper is used to intercept the filler items from
+     * the calls to the tree's real label provider.
      */
     private static class TreeLabelProviderWrapper implements ITableLabelProvider {
         private final ITableLabelProvider labelProvider;
@@ -427,9 +429,9 @@ public class TimeGraphCombo extends Composite {
     }
 
     /**
-     * The ViewerFilterWrapper is used to intercept the filler items from
-     * the time graph combo's real ViewerFilters. These filler items should
-     * always be visible.
+     * The ViewerFilterWrapper is used to intercept the filler items from the
+     * time graph combo's real ViewerFilters. These filler items should always
+     * be visible.
      */
     private static class ViewerFilterWrapper extends ViewerFilter {
 
@@ -455,11 +457,14 @@ public class TimeGraphCombo extends Composite {
     // ------------------------------------------------------------------------
 
     /**
-     * Constructs a new instance of this class given its parent
-     * and a style value describing its behavior and appearance.
+     * Constructs a new instance of this class given its parent and a style
+     * value describing its behavior and appearance.
      *
-     * @param parent a widget which will be the parent of the new instance (cannot be null)
-     * @param style the style of widget to construct
+     * @param parent
+     *            a widget which will be the parent of the new instance (cannot
+     *            be null)
+     * @param style
+     *            the style of widget to construct
      */
     public TimeGraphCombo(Composite parent, int style) {
         this(parent, style, DEFAULT_WEIGHTS);
@@ -524,7 +529,8 @@ public class TimeGraphCombo extends Composite {
         // This work around used to be done on control resized but the header
         // height was not initialized on the initial resize on GTK3.
         tree.addPaintListener(new PaintListener() {
-            @Override
+
+    @Override
             public void paintControl(PaintEvent e) {
                 int headerHeight = tree.getHeaderHeight();
                 if (headerHeight > 0) {
@@ -696,20 +702,13 @@ public class TimeGraphCombo extends Composite {
         // ensure synchronization of selected item between tree and time graph
         fTimeGraphViewer.addSelectionListener(event -> {
             ITimeGraphEntry entry = fTimeGraphViewer.getSelection();
-            fInhibitTreeSelection = true; // block the tree selection changed listener
-            if (entry != null) {
-                StructuredSelection selection = new StructuredSelection(entry);
-                fTreeViewer.setSelection(selection);
-            } else {
-                fTreeViewer.setSelection(new StructuredSelection());
-            }
-            fInhibitTreeSelection = false;
-            alignTreeItems(false);
+            setSelectionInTree(entry);
         });
 
         // ensure alignment of top item between tree and time graph
         fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() {
-            @Override
+
+    @Override
             public void widgetSelected(SelectionEvent e) {
                 alignTreeItems(false);
             }
@@ -899,7 +898,8 @@ public class TimeGraphCombo extends Composite {
     /**
      * Sets the tree content provider used by this time graph combo.
      *
-     * @param contentProvider the tree content provider
+     * @param contentProvider
+     *            the tree content provider
      */
     public void setTreeContentProvider(ITreeContentProvider contentProvider) {
         fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider));
@@ -908,7 +908,8 @@ public class TimeGraphCombo extends Composite {
     /**
      * Sets the tree label provider used by this time graph combo.
      *
-     * @param labelProvider the tree label provider
+     * @param labelProvider
+     *            the tree label provider
      */
     public void setTreeLabelProvider(ITableLabelProvider labelProvider) {
         fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider));
@@ -917,7 +918,8 @@ public class TimeGraphCombo extends Composite {
     /**
      * Sets the tree content provider used by the filter dialog
      *
-     * @param contentProvider the tree content provider
+     * @param contentProvider
+     *            the tree content provider
      */
     public void setFilterContentProvider(ITreeContentProvider contentProvider) {
         getShowFilterDialogAction().getFilterDialog().setContentProvider(contentProvider);
@@ -926,7 +928,8 @@ public class TimeGraphCombo extends Composite {
     /**
      * Sets the tree label provider used by the filter dialog
      *
-     * @param labelProvider the tree label provider
+     * @param labelProvider
+     *            the tree label provider
      */
     public void setFilterLabelProvider(ITableLabelProvider labelProvider) {
         getShowFilterDialogAction().getFilterDialog().setLabelProvider(labelProvider);
@@ -957,7 +960,8 @@ public class TimeGraphCombo extends Composite {
     /**
      * Sets the tree columns for this time graph combo.
      *
-     * @param columnNames the tree column names
+     * @param columnNames
+     *            the tree column names
      */
     public void setTreeColumns(String[] columnNames) {
         final Tree tree = fTreeViewer.getTree();
@@ -972,7 +976,8 @@ public class TimeGraphCombo extends Composite {
     /**
      * Sets the tree columns for this time graph combo's filter dialog.
      *
-     * @param columnNames the tree column names
+     * @param columnNames
+     *            the tree column names
      */
     public void setFilterColumns(String[] columnNames) {
         getShowFilterDialogAction().getFilterDialog().setColumnNames(columnNames);
@@ -991,7 +996,8 @@ public class TimeGraphCombo extends Composite {
     /**
      * Sets the time graph presentation provider used by this time graph combo.
      *
-     * @param timeGraphProvider the time graph provider
+     * @param timeGraphProvider
+     *            the time graph provider
      */
     public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
         fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider);
@@ -1000,7 +1006,9 @@ public class TimeGraphCombo extends Composite {
     /**
      * Sets or clears the input for this time graph combo.
      *
-     * @param input the input of this time graph combo, or <code>null</code> if none
+     * @param input
+     *            the input of this time graph combo, or <code>null</code> if
+     *            none
      */
     public void setInput(Object input) {
         fInhibitTreeSelection = true;
@@ -1022,7 +1030,8 @@ public class TimeGraphCombo extends Composite {
             @Override
             public void run() {
                 alignTreeItems(true);
-            }});
+            }
+        });
     }
 
     /**
@@ -1037,14 +1046,16 @@ public class TimeGraphCombo extends Composite {
     /**
      * Sets or clears the list of links to display on this combo
      *
-     * @param links the links to display in this time graph combo
+     * @param links
+     *            the links to display in this time graph combo
      */
     public void setLinks(List<ILinkEvent> links) {
         fTimeGraphViewer.setLinks(links);
     }
 
     /**
-     * @param filter The filter object to be attached to the view
+     * @param filter
+     *            The filter object to be attached to the view
      */
     public void addFilter(@NonNull ViewerFilter filter) {
         fInhibitTreeSelection = true;
@@ -1057,7 +1068,8 @@ public class TimeGraphCombo extends Composite {
     }
 
     /**
-     * @param filter The filter object to be removed from the view
+     * @param filter
+     *            The filter object to be removed from the view
      */
     public void removeFilter(@NonNull ViewerFilter filter) {
         fInhibitTreeSelection = true;
@@ -1106,7 +1118,8 @@ public class TimeGraphCombo extends Composite {
     }
 
     /**
-     * Refreshes this time graph completely with information freshly obtained from its model.
+     * Refreshes this time graph completely with information freshly obtained
+     * from its model.
      */
     public void refresh() {
         fInhibitTreeSelection = true;
@@ -1125,7 +1138,8 @@ public class TimeGraphCombo extends Composite {
     /**
      * Adds a listener for selection changes in this time graph combo.
      *
-     * @param listener a selection listener
+     * @param listener
+     *            a selection listener
      */
     public void addSelectionListener(ITimeGraphSelectionListener listener) {
         SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener);
@@ -1137,7 +1151,8 @@ public class TimeGraphCombo extends Composite {
     /**
      * Removes the given selection listener from this time graph combo.
      *
-     * @param listener a selection changed listener
+     * @param listener
+     *            a selection changed listener
      */
     public void removeSelectionListener(ITimeGraphSelectionListener listener) {
         SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener);
@@ -1148,11 +1163,36 @@ public class TimeGraphCombo extends Composite {
     /**
      * Sets the current selection for this time graph combo.
      *
-     * @param selection the new selection
+     * @param selection
+     *            the new selection
      */
     public void setSelection(ITimeGraphEntry selection) {
         fTimeGraphViewer.setSelection(selection);
-        fInhibitTreeSelection = true; // block the tree selection changed listener
+        setSelectionInTree(selection);
+    }
+
+    /**
+     * Sets the current selection for this time graph combo and reveal it if
+     * needed.
+     *
+     * @param selection
+     *            The new selection
+     * @since 2.0
+     */
+    public void selectAndReveal(@NonNull ITimeGraphEntry selection) {
+        fTimeGraphViewer.selectAndReveal(selection);
+        setSelectionInTree(selection);
+    }
+
+    /**
+     * Select the entry in the tree structure
+     *
+     * @param selection
+     *            The new selection
+     */
+    private void setSelectionInTree(ITimeGraphEntry selection) {
+        fInhibitTreeSelection = true; // block the tree selection changed
+                                      // listener
         if (selection != null) {
             StructuredSelection structuredSelection = new StructuredSelection(selection);
             fTreeViewer.setSelection(structuredSelection);
@@ -1274,7 +1314,8 @@ public class TimeGraphCombo extends Composite {
 
     private int getItemHeight(final Tree tree, boolean force) {
         /*
-         * Bug in Linux.  The method getItemHeight doesn't always return the correct value.
+         * Bug in Linux. The method getItemHeight doesn't always return the
+         * correct value.
          */
         if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
             if (fLinuxItemHeight != 0 && !force) {
@@ -1305,7 +1346,8 @@ public class TimeGraphCombo extends Composite {
                 tree.addPaintListener(paintListener);
             }
         } else {
-            fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore
+            fLinuxItemHeight = -1; // Not Linux, don't perform os.name check
+                                   // anymore
         }
         return tree.getItemHeight();
     }
@@ -1397,8 +1439,9 @@ public class TimeGraphCombo extends Composite {
 
     private Rectangle alignTreeItem(TreeItem item, Rectangle bounds, TreeItem nextItem) {
         /*
-         * Bug in Linux. The method getBounds doesn't always return the correct height.
-         * Use the difference of y position between items to calculate the height.
+         * Bug in Linux. The method getBounds doesn't always return the correct
+         * height. Use the difference of y position between items to calculate
+         * the height.
          */
         Rectangle nextBounds = nextItem.getBounds();
         Integer itemHeight = nextBounds.y - bounds.y;
index b783337ce2e4c07267344e8c7b7e43f22ee53aed..ab5fd9b1643628e5e28000af43911c06823987a9 100644 (file)
@@ -1815,6 +1815,22 @@ public class TimeGraphViewer implements ITimeDataProvider, IMarkerAxisListener,
         adjustVerticalScrollBar();
     }
 
+    /**
+     * Select an entry and reveal it
+     *
+     * @param entry
+     *            The entry to select
+     * @since 2.0
+     */
+    public void selectAndReveal(@NonNull ITimeGraphEntry entry) {
+        final ITimeGraphEntry parent = entry.getParent();
+        if (parent != null) {
+            fTimeGraphCtrl.setExpandedState(parent, true);
+        }
+        setSelection(entry);
+        adjustVerticalScrollBar();
+    }
+
     /**
      * Get the number of expanded (visible) time graph entries. This includes
      * leafs and does not include filtered-out entries.
index cf94fbd82368728b285a4488786627809934accc..47d8f0e4346e5cd8fa64b1d7b3276970e4057c2f 100644 (file)
@@ -15,6 +15,7 @@ package org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model;
 
 import java.util.Iterator;
 import java.util.List;
+import java.util.regex.Pattern;
 
 import org.eclipse.jdt.annotation.NonNull;
 
@@ -103,4 +104,14 @@ public interface ITimeGraphEntry {
      * @return the iterator
      */
     <T extends ITimeEvent> Iterator<@NonNull T> getTimeEventsIterator(long startTime, long stopTime, long visibleDuration);
+
+    /**
+     * Test if this time graph entry matches with this pattern
+     *
+     * @param pattern
+     *            The pattern to match
+     * @return True if it matches, false otherwise.
+     * @since 2.0
+     */
+    boolean matches(@NonNull Pattern pattern);
 }
index f5289745149daece368768d6b2cc786fdafc0783..4c6356bb6a4ad345da0f0b29f8d97467fb9ce757 100644 (file)
@@ -19,6 +19,7 @@ import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.regex.Pattern;
 
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.swt.SWT;
@@ -333,4 +334,13 @@ public class TimeGraphEntry implements ITimeGraphEntry {
         return getClass().getSimpleName() + '(' + fName + ')';
     }
 
+    /**
+     * @since 2.0
+     */
+    @Override
+    public boolean matches(@NonNull Pattern pattern) {
+        // Default implementation
+        return pattern.matcher(fName).find();
+    }
+
 }
This page took 0.05371 seconds and 5 git commands to generate.