--- /dev/null
+/*****************************************************************************
+ * 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;
+ }
+}
// 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();
@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);
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());
}
/**
- * 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;
}
/**
- * 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;
}
/**
- * 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 {
// ------------------------------------------------------------------------
/**
- * 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);
// 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) {
// 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);
}
/**
* 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));
/**
* 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));
/**
* 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);
/**
* 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);
/**
* 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();
/**
* 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);
/**
* 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);
/**
* 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;
@Override
public void run() {
alignTreeItems(true);
- }});
+ }
+ });
}
/**
/**
* 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;
}
/**
- * @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;
}
/**
- * 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;
/**
* 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);
/**
* 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);
/**
* 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);
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) {
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();
}
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;