/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * 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 * * Contributors: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.client.ui.rcp.dialogs; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jubula.client.ui.constants.ContextHelpIds; import org.eclipse.jubula.client.ui.rcp.Plugin; import org.eclipse.jubula.client.ui.rcp.businessprocess.UINodeBP; import org.eclipse.jubula.client.ui.rcp.i18n.Messages; import org.eclipse.jubula.client.ui.rcp.search.query.Operation; import org.eclipse.jubula.client.ui.rcp.search.query.TextFinder; import org.eclipse.jubula.client.ui.utils.JobUtils; import org.eclipse.jubula.client.ui.utils.TreeViewerIterator; import org.eclipse.jubula.client.ui.views.IMultiTreeViewerContainer; import org.eclipse.jubula.client.ui.views.ITreeViewerContainer; import org.eclipse.jubula.tools.internal.constants.StringConstants; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.Shell; /** * This class creates a dialog, where you can search a TreeViewer for specified * string. The search is performed by using content- and labelprovider of the * treeviewer. * * @author BREDEX GmbH * @created 22.02.2005 * @param <NODE> INodePO or TestResultNodeGUI */ public class FindDialog <NODE> implements DisposeListener { /** recent queries */ private static final List <String> RECENT = new ArrayList<String>(5); /** recent option */ private static boolean lastforward = true; /** recent option */ private static boolean lastwrapsearch = true; /** recent option */ private static boolean lastregex = false; /** recent option */ private static boolean lastcasesensitive = false; /** the shell depends on the object that is selected */ private Shell m_shell; /** find listener for callback */ private ITreeViewerContainer m_treeViewContainer; /** search Text Field */ private Combo m_searchStringCombo; /** Directions Group */ private Group m_directionGroup; /** Combo to select Find Forward */ private Button m_findForwardButton; /** Combo to select Find Backward */ private Button m_findBackwardButton; /** Options Group */ private Group m_optionsGroup; /** CheckbBox to select Wrap Search */ private Button m_wrapSearchCheck; /** CheckbBox to select use regular expression */ private Button m_useRegExCheck; /** CheckbBox to select use search case sensitiv */ private Button m_caseSensitivCheck; /** help Link */ private Link m_helpLink; /** find Button */ private Button m_findButton; /** cancel Button */ private Button m_cancelButton; /** Label for showing errors */ private Label m_errorLabel; /** * @param parentShell The parent shell. * @param listener IFindListener */ public FindDialog(Shell parentShell, ITreeViewerContainer listener) { m_shell = new Shell(parentShell); setTreeViewContainer(listener); init(); Plugin.getHelpSystem().setHelp(parentShell, ContextHelpIds.FIND_DIALOG); } /** * initializes search window */ private void init() { m_shell.setText(Messages.FindDialogTitle); createContent(); Point location = m_shell.getDisplay().getCursorLocation(); if ((location.x + m_shell.getSize().x) > m_shell.getDisplay(). getPrimaryMonitor().getBounds().width) { location.x = m_shell.getDisplay().getPrimaryMonitor(). getBounds().width - m_shell.getSize().x; } if ((location.y + m_shell.getSize().y) > m_shell.getDisplay(). getPrimaryMonitor().getBounds().height) { location.y = m_shell.getDisplay().getPrimaryMonitor(). getBounds().height - m_shell.getSize().y; } m_shell.setLocation(location); } /** * creates all the dialog content */ private void createContent() { FormLayout layout = new FormLayout(); layout.marginHeight = 5; layout.marginWidth = 10; m_shell.setLayout(layout); Label findLabel = new Label(m_shell, SWT.NONE); findLabel.setText(Messages.FindDialogFind + StringConstants.COLON); FormData findLabelData = new FormData(); // set position and size with relative values findLabelData.left = new FormAttachment(0, 0); findLabelData.top = new FormAttachment(0, 10); findLabel.setLayoutData(findLabelData); m_searchStringCombo = new Combo(m_shell, SWT.BORDER); FormData searchStringTextData = new FormData(); searchStringTextData.top = new FormAttachment(findLabel, 0, SWT.TOP); searchStringTextData.left = new FormAttachment(findLabel, 10, SWT.RIGHT); searchStringTextData.right = new FormAttachment(100, 0); m_searchStringCombo.setLayoutData(searchStringTextData); m_searchStringCombo.setItems(RECENT.toArray (new String[RECENT.size()])); if (m_searchStringCombo.getItemCount() == 0) { m_searchStringCombo.setText(Messages.FindDialogPhrase); } else { m_searchStringCombo.select(0); } m_searchStringCombo.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { if (e.keyCode == 13) { doCallBack(); } } }); createDirectionGroup(); createOptionsGroup(); createButtons(); createHelpLink(); m_errorLabel = new Label(m_shell, SWT.NONE); m_errorLabel.setForeground(Display.getDefault(). getSystemColor(SWT.COLOR_RED)); FormData errorLabelData = new FormData(); errorLabelData.top = new FormAttachment(m_findButton, 5, SWT.BOTTOM); errorLabelData.left = new FormAttachment(5, 0); errorLabelData.right = new FormAttachment(100, 0); m_errorLabel.setLayoutData(errorLabelData); m_shell.pack(); } /** * creates the find and the close button */ private void createButtons() { m_findButton = new Button(m_shell, SWT.PUSH); m_findButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { doCallBack(); } }); m_findButton.setText(Messages.FindDialogFind); FormData findButtonData = new FormData(); findButtonData.top = new FormAttachment(m_optionsGroup, 15, SWT.BOTTOM); findButtonData.left = new FormAttachment(20, 0); findButtonData.right = new FormAttachment(58, 0); m_findButton.setLayoutData(findButtonData); m_cancelButton = new Button(m_shell, SWT.NONE); m_cancelButton.setText(Messages.FindDialogClose); m_cancelButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { m_shell.close(); } }); FormData cancelButtonData = new FormData(); cancelButtonData.top = new FormAttachment(m_optionsGroup, 15, SWT.BOTTOM); cancelButtonData.left = new FormAttachment(62, 0); cancelButtonData.right = new FormAttachment(100, 0); m_cancelButton.setLayoutData(cancelButtonData); } /** * creates the help link * borrowed from TrayDialog * adapted for FormLayout */ private void createHelpLink() { m_helpLink = new Link(m_shell, SWT.WRAP | SWT.NO_FOCUS); m_helpLink.setText("<a>" + IDialogConstants.HELP_LABEL + "</a>"); //$NON-NLS-1$ //$NON-NLS-2$ m_helpLink.setToolTipText(IDialogConstants.HELP_LABEL); FormData helpLinkData = new FormData(); helpLinkData.bottom = new FormAttachment(m_findButton, 0, SWT.BOTTOM); helpLinkData.left = new FormAttachment(0, 0); helpLinkData.right = new FormAttachment(16, 0); m_helpLink.setLayoutData(helpLinkData); m_helpLink.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { helpPressed(); } }); } /** * performs the the action for the help link * borrowed and adapted from TrayDialog */ private void helpPressed() { if (m_shell != null) { Control c = m_shell.getDisplay().getFocusControl(); while (c != null) { if (c.isListening(SWT.Help)) { c.notifyListeners(SWT.Help, new Event()); break; } c = c.getParent(); } } } /** * creates the Direction Group */ private void createDirectionGroup() { m_directionGroup = new Group(m_shell, SWT.NONE); m_directionGroup.setText(Messages.FindDialogDirection); FormLayout groupLayout = new FormLayout(); m_directionGroup.setLayout(groupLayout); groupLayout.marginHeight = 5; groupLayout.marginWidth = 5; FormData groupDirectionData = new FormData(); groupDirectionData.top = new FormAttachment(m_searchStringCombo, 15, SWT.BOTTOM); groupDirectionData.right = new FormAttachment(100, 0); groupDirectionData.left = new FormAttachment(0, 0); m_directionGroup.setLayoutData(groupDirectionData); m_findForwardButton = new Button(m_directionGroup, SWT.RADIO); m_findForwardButton.setText(Messages.FindDialogForward); m_findForwardButton.setSelection(lastforward); FormData findForwardData = new FormData(); findForwardData.left = new FormAttachment(0, 0); findForwardData.right = new FormAttachment(48, 0); m_findForwardButton.setLayoutData(findForwardData); m_findBackwardButton = new Button(m_directionGroup, SWT.RADIO); m_findBackwardButton.setText(Messages.FindDialogBackward); m_findBackwardButton.setSelection(!lastforward); FormData findBackwardData = new FormData(); findBackwardData.left = new FormAttachment(52, 0); findBackwardData.right = new FormAttachment(100, 0); m_findBackwardButton.setLayoutData(findBackwardData); } /** * creates the Direction Group */ private void createOptionsGroup() { m_optionsGroup = new Group(m_shell, SWT.NONE); m_optionsGroup.setText(Messages.FindDialogOptions); FormData groupDirectionData = new FormData(); groupDirectionData.top = new FormAttachment(m_directionGroup, 10, SWT.BOTTOM); groupDirectionData.right = new FormAttachment(100, 0); groupDirectionData.left = new FormAttachment(0, 0); m_optionsGroup.setLayoutData(groupDirectionData); FormLayout layout = new FormLayout(); layout.marginHeight = 5; layout.marginWidth = 5; m_optionsGroup.setLayout(layout); m_caseSensitivCheck = new Button(m_optionsGroup, SWT.CHECK); m_caseSensitivCheck.setText(Messages.FindDialogCaseSen); m_caseSensitivCheck.setSelection(lastcasesensitive); FormData sensitiveData = new FormData(); sensitiveData.left = new FormAttachment(0, 0); sensitiveData.right = new FormAttachment(48, 0); m_caseSensitivCheck.setLayoutData(sensitiveData); m_wrapSearchCheck = new Button(m_optionsGroup, SWT.CHECK); m_wrapSearchCheck.setText(Messages.FindDialogWrap); m_wrapSearchCheck.setSelection(lastwrapsearch); FormData warpSearchData = new FormData(); warpSearchData.left = new FormAttachment(52, 0); warpSearchData.right = new FormAttachment(100, 0); m_wrapSearchCheck.setLayoutData(warpSearchData); m_useRegExCheck = new Button(m_optionsGroup, SWT.CHECK); m_useRegExCheck.setText(Messages.FindDialogRegEx); m_useRegExCheck.setSelection(lastregex); FormData regExData = new FormData(); regExData.left = new FormAttachment(0, 0); regExData.top = new FormAttachment(m_wrapSearchCheck, 5, SWT.BOTTOM); m_useRegExCheck.setLayoutData(regExData); } /** * opens the shell */ public void open() { m_shell.open(); } /** * @return true if shell is disposed */ public boolean isDisposed() { return (m_shell == null) ? true : m_shell.isDisposed(); } /** * calls find method on callback object */ private void doCallBack() { if (!isValidPart(m_treeViewContainer)) { m_shell.close(); return; } final String searchText = m_searchStringCombo.getText(); lastcasesensitive = m_caseSensitivCheck.getSelection(); lastregex = m_useRegExCheck.getSelection(); lastwrapsearch = m_wrapSearchCheck.getSelection(); lastforward = m_findForwardButton.getSelection(); if (RECENT.contains(searchText)) { RECENT.remove(searchText); } m_findButton.setEnabled(false); m_errorLabel.setText(StringConstants.EMPTY); if (RECENT.size() > 4) { RECENT.remove(RECENT.size() - 1); } RECENT.add(0, searchText); ISelection treeSelection = getActiveTreeViewer().getSelection(); final Object nodeToStart = treeSelection instanceof IStructuredSelection ? ((IStructuredSelection)treeSelection).getFirstElement() : null; final String jobName = Messages.UIJobFindingNodes; Job findingNodes = new Job(jobName) { protected IStatus run(IProgressMonitor monitor) { monitor.beginTask(jobName, IProgressMonitor.UNKNOWN); final boolean result = find(searchText, lastcasesensitive, lastregex, lastwrapsearch, lastforward, nodeToStart, monitor); Plugin.getDisplay().syncExec(new Runnable() { public void run() { if (!m_errorLabel.isDisposed()) { if (!result) { m_errorLabel.setText(Messages.FindDialogError); } else { m_errorLabel.setText(StringConstants.EMPTY); } m_searchStringCombo.setItems(RECENT .toArray(new String[RECENT.size()])); m_searchStringCombo.select(0); m_searchStringCombo.setFocus(); m_findButton.setEnabled(true); } } }); monitor.done(); return Status.OK_STATUS; } }; JobUtils.executeJob(findingNodes, null); } /** * @param queryString queryString * @param caseSensitive caseSensitive * @param useRegex useRegex * @param wrapSearch wrapSearch * @param forward forward * @param nodeToStart * the node to start the search; may be null --> root is used as * search start point * @param monitor the progress monitor to use to communicate progress * @return boolean */ private boolean find(String queryString, boolean caseSensitive, boolean useRegex, boolean wrapSearch, boolean forward, Object nodeToStart, IProgressMonitor monitor) { final Object node = findGuiNode(queryString, caseSensitive, useRegex, wrapSearch, forward, nodeToStart, monitor); if (node != null) { // I don't know why it doesn't always work the first // time, but calling this method twice seems to fix the problem Plugin.getDisplay().syncExec(new Runnable() { public void run() { UINodeBP.selectNodeInTree(node, getActiveTreeViewer()); } }); return true; } return false; } /** * * @param treeViewerContainer The container to check. * @return <code>true</code> if the tree of the given container can be * operated on (i.e. not <code>null</code>, not disposed, etc.). * Otherwise, <code>false</code>. */ private boolean isValidPart(ITreeViewerContainer treeViewerContainer) { return !(treeViewerContainer == null || treeViewerContainer.getTreeViewer() == null || treeViewerContainer.getTreeViewer().getTree().isDisposed()); } /** * First flattens INodePO hierarchy to list, then iterate over list * * @param queryString * queryString * @param caseSensitive * caseSensitive * @param useRegex * useRegex * @param wrapSearch * wrapSearch * @param forward * forward * @param nodeToStart * the node to start the search; may be null --> root is used as * search start point * @param monitor the progress monitor to use to communicate progress * @return NODE */ private Object findGuiNode(String queryString, boolean caseSensitive, boolean useRegex, boolean wrapSearch, boolean forward, Object nodeToStart, IProgressMonitor monitor) { TextFinder searcher = new TextFinder( queryString, Operation.create(caseSensitive, useRegex)); if (monitor.isCanceled()) { return null; } if (wrapSearch || nodeToStart != null) { TreeViewerIterator iterator = new TreeViewerIterator( getActiveTreeViewer(), nodeToStart, forward); int noOfElements = iterator.getElements().size(); monitor.beginTask(Messages.SearchingIn + StringConstants.SPACE + noOfElements + StringConstants.SPACE + Messages.Elements + StringConstants.DOT + StringConstants.DOT + StringConstants.DOT, noOfElements); ILabelProvider labelProv = (ILabelProvider)getActiveTreeViewer().getLabelProvider(); while (iterator.hasNext()) { monitor.worked(1); if (monitor.isCanceled()) { return null; } Object o = iterator.next(); if (searcher.matchSearchString(labelProv.getText(o))) { monitor.done(); return o; } } } return null; } /** * Listener method reacting on dispose events * @param e DisposeEvent */ public void widgetDisposed(DisposeEvent e) { closeShell(); } /** * Closes the shell for this dialog, if it is not already closed. */ private void closeShell() { if (!isDisposed()) { m_shell.close(); } } /** * Method for setting acutal part * @param viewContainer TreeViewContainer */ public void setTreeViewContainer(ITreeViewerContainer viewContainer) { // Remove old listener, if necessary if (isValidPart(m_treeViewContainer)) { m_treeViewContainer.getTreeViewer().getTree() .removeDisposeListener(this); } // Add new listener if (isValidPart(viewContainer)) { viewContainer.getTreeViewer().getTree() .addDisposeListener(this); } m_treeViewContainer = viewContainer; } /** * * @return the tree viewer on which operations should be performed, or * <code>null</code> if no such tree viewer exists. */ private TreeViewer getActiveTreeViewer() { if (m_treeViewContainer instanceof IMultiTreeViewerContainer) { return ((IMultiTreeViewerContainer)m_treeViewContainer) .getActiveTreeViewer(); } return m_treeViewContainer.getTreeViewer(); } }