| /******************************************************************************* |
| * Copyright (c) 2000, 2006 IBM Corporation 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 |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.ui.texteditor; |
| |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.ResourceBundle; |
| import java.util.regex.PatternSyntaxException; |
| |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.widgets.Shell; |
| |
| import org.eclipse.jface.action.IStatusLineManager; |
| import org.eclipse.jface.dialogs.IDialogSettings; |
| |
| import org.eclipse.jface.text.IFindReplaceTarget; |
| import org.eclipse.jface.text.IFindReplaceTargetExtension3; |
| import org.eclipse.jface.text.TextUtilities; |
| |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.internal.texteditor.TextEditorPlugin; |
| |
| |
| /** |
| * An action which finds the next/previous occurrence of the last search or the |
| * current selection if present. |
| * <p> |
| * This class may be instantiated; it is not intended to be subclassed. |
| * </p> |
| * |
| * @since 2.0 |
| */ |
| public class FindNextAction extends ResourceAction implements IUpdate { |
| |
| /** The action's target */ |
| private IFindReplaceTarget fTarget; |
| /** The part the action is bound to */ |
| private IWorkbenchPart fWorkbenchPart; |
| /** The workbench window */ |
| private IWorkbenchWindow fWorkbenchWindow; |
| /** The dialog settings to retrieve the last search */ |
| private IDialogSettings fDialogSettings; |
| /** The find history as initially given in the dialog settings. */ |
| private List fFindHistory= new ArrayList(); |
| /** The find string as initially given in the dialog settings. */ |
| private String fFindString; |
| /** The search direction as initially given in the dialog settings. */ |
| private boolean fForward; |
| /** The wrapping flag as initially given in the dialog settings. */ |
| private boolean fWrapInit; |
| /** The case flag as initially given in the dialog settings. */ |
| private boolean fCaseInit; |
| /** The whole word flag as initially given in the dialog settings. */ |
| private boolean fWholeWordInit; |
| /** |
| * The regExSearch flag as initially given in the dialog settings. |
| * |
| * @since 3.0 |
| */ |
| private boolean fRegExSearch; |
| /** |
| * The last selection set by find/replace. |
| * |
| * @since 3.0 |
| */ |
| private String fSelection; |
| |
| /** |
| * Creates a new find/replace action for the given workbench part. |
| * The action configures its visual representation from the given |
| * resource bundle. |
| * |
| * @param bundle the resource bundle |
| * @param prefix a prefix to be prepended to the various resource keys |
| * (described in <code>ResourceAction</code> constructor), or |
| * <code>null</code> if none |
| * @param workbenchPart the workbench part |
| * @param forward the search direction |
| * @see ResourceAction#ResourceAction(ResourceBundle, String) |
| */ |
| public FindNextAction(ResourceBundle bundle, String prefix, IWorkbenchPart workbenchPart, boolean forward) { |
| super(bundle, prefix); |
| fWorkbenchPart= workbenchPart; |
| fForward= forward; |
| update(); |
| } |
| |
| /** |
| * Creates a new find/replace action for the given workbench window. |
| * The action configures its visual representation from the given |
| * resource bundle. |
| * |
| * @param bundle the resource bundle |
| * @param prefix a prefix to be prepended to the various resource keys |
| * (described in <code>ResourceAction</code> constructor), or |
| * <code>null</code> if none |
| * @param workbenchWindow the workbench window |
| * @param forward the search direction |
| * @see ResourceAction#ResourceAction(ResourceBundle, String) |
| * |
| * @deprecated use FindReplaceAction(ResourceBundle, String, IWorkbenchPart, boolean) instead |
| */ |
| public FindNextAction(ResourceBundle bundle, String prefix, IWorkbenchWindow workbenchWindow, boolean forward) { |
| super(bundle, prefix); |
| fWorkbenchWindow= workbenchWindow; |
| fForward= forward; |
| update(); |
| } |
| |
| /** |
| * Returns the find string based on the selection or the find history. |
| * @return the find string |
| */ |
| private String getFindString() { |
| String string= getSelectionString(); |
| |
| if ((string == null || fRegExSearch && string.equals(fSelection)) && !fFindHistory.isEmpty()) |
| string= (String) fFindHistory.get(0); |
| |
| return string; |
| } |
| |
| /** |
| * Returns the status line manager of the active editor. |
| * @return the status line manager of the active editor |
| */ |
| private IStatusLineManager getStatusLineManager() { |
| IEditorPart editor= fWorkbenchPart.getSite().getPage().getActiveEditor(); |
| if (editor == null) |
| return null; |
| |
| return editor.getEditorSite().getActionBars().getStatusLineManager(); |
| } |
| |
| /** |
| * Sets the "no matches found" error message to the status line. |
| * |
| * @since 3.0 |
| */ |
| private void statusNotFound() { |
| fWorkbenchPart.getSite().getShell().getDisplay().beep(); |
| |
| IStatusLineManager manager= getStatusLineManager(); |
| if (manager == null) |
| return; |
| |
| manager.setMessage(EditorMessages.FindNext_Status_noMatch_label); |
| } |
| |
| /** |
| * Clears the status line. |
| */ |
| private void statusClear() { |
| IStatusLineManager manager= getStatusLineManager(); |
| if (manager == null) |
| return; |
| |
| manager.setErrorMessage(""); //$NON-NLS-1$ |
| manager.setMessage(""); //$NON-NLS-1$ |
| } |
| |
| /* |
| * @see IAction#run() |
| */ |
| public void run() { |
| if (fTarget != null) { |
| readConfiguration(); |
| |
| fFindString= getFindString(); |
| if (fFindString == null) { |
| statusNotFound(); |
| return; |
| } |
| |
| boolean wholeWord= fWholeWordInit && !fRegExSearch && isWord(fFindString); |
| |
| statusClear(); |
| if (!findNext(fFindString, fForward, fCaseInit, fWrapInit, wholeWord, fRegExSearch)) |
| statusNotFound(); |
| |
| writeConfiguration(); |
| } |
| } |
| |
| /** |
| * Tests whether each character in the given |
| * string is a letter. |
| * |
| * @param str |
| * @return <code>true</code> if the given string is a word |
| * @since 3.2 |
| */ |
| private 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; |
| } |
| |
| /* |
| * @see IUpdate#update() |
| */ |
| public void update() { |
| |
| if (fWorkbenchPart == null && fWorkbenchWindow != null) |
| fWorkbenchPart= fWorkbenchWindow.getPartService().getActivePart(); |
| |
| if (fWorkbenchPart != null) |
| fTarget= (IFindReplaceTarget) fWorkbenchPart.getAdapter(IFindReplaceTarget.class); |
| else |
| fTarget= null; |
| |
| setEnabled(fTarget != null && fTarget.canPerformFind()); |
| } |
| |
| /* |
| * @see FindReplaceDialog#findIndex(String, int, boolean, boolean, boolean, boolean) |
| * @since 3.0 |
| */ |
| private int findIndex(String findString, int startPosition, boolean forwardSearch, boolean caseSensitive, boolean wrapSearch, boolean wholeWord, boolean regExSearch) { |
| |
| if (forwardSearch) { |
| if (wrapSearch) { |
| int index= findAndSelect(startPosition, findString, true, caseSensitive, wholeWord, regExSearch); |
| if (index == -1) { |
| beep(); |
| index= findAndSelect(-1, findString, true, caseSensitive, wholeWord, regExSearch); |
| } |
| return index; |
| } |
| return findAndSelect(startPosition, findString, true, caseSensitive, wholeWord, regExSearch); |
| } |
| |
| // backward |
| if (wrapSearch) { |
| int index= findAndSelect(startPosition - 1, findString, false, caseSensitive, wholeWord, regExSearch); |
| if (index == -1) { |
| beep(); |
| index= findAndSelect(-1, findString, false, caseSensitive, wholeWord, regExSearch); |
| } |
| return index; |
| } |
| return findAndSelect(startPosition - 1, findString, false, caseSensitive, wholeWord, regExSearch); |
| } |
| |
| /** |
| * Returns whether the specified search string can be found using the given options. |
| * |
| * @param findString the string to search for |
| * @param forwardSearch the search direction |
| * @param caseSensitive should the search honor cases |
| * @param wrapSearch should the search wrap to the start/end if end/start reached |
| * @param wholeWord does the find string represent a complete word |
| * @param regExSearch if <code>true</code> findString represents a regular expression |
| * @return <code>true</code> if the find string can be found using the given options |
| * @since 3.0 |
| */ |
| private boolean findNext(String findString, boolean forwardSearch, boolean caseSensitive, boolean wrapSearch, boolean wholeWord, boolean regExSearch) { |
| |
| Point r= fTarget.getSelection(); |
| int findReplacePosition= r.x; |
| if (forwardSearch) |
| findReplacePosition += r.y; |
| |
| int index= findIndex(findString, findReplacePosition, forwardSearch, caseSensitive, wrapSearch, wholeWord, regExSearch); |
| |
| if (index != -1) |
| return true; |
| |
| return false; |
| } |
| |
| private void beep() { |
| Shell shell= null; |
| if (fWorkbenchPart != null) |
| shell= fWorkbenchPart.getSite().getShell(); |
| else if (fWorkbenchWindow != null) |
| shell= fWorkbenchWindow.getShell(); |
| |
| if (shell != null && !shell.isDisposed()) |
| shell.getDisplay().beep(); |
| } |
| |
| /** |
| * Searches for a string starting at the given offset and using the specified search |
| * directives. If a string has been found it is selected and its start offset is |
| * returned. |
| * |
| * @param offset the offset at which searching starts |
| * @param findString the string which should be found |
| * @param forwardSearch the direction of the search |
| * @param caseSensitive <code>true</code> performs a case sensitive search, <code>false</code> an insensitive search |
| * @param wholeWord if <code>true</code> only occurrences are reported in which the findString stands as a word by itself |
| * @param regExSearch if <code>true</code> findString represents a regular expression |
| * @return the position of the specified string, or -1 if the string has not been found |
| * @since 3.0 |
| */ |
| private int findAndSelect(int offset, String findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord, boolean regExSearch) { |
| if (fTarget instanceof IFindReplaceTargetExtension3) { |
| try { |
| return ((IFindReplaceTargetExtension3)fTarget).findAndSelect(offset, findString, forwardSearch, caseSensitive, wholeWord, regExSearch); |
| } catch (PatternSyntaxException ex) { |
| return -1; |
| } |
| } |
| return fTarget.findAndSelect(offset, findString, forwardSearch, caseSensitive, wholeWord); |
| } |
| |
| //--------------- configuration handling -------------- |
| |
| /** |
| * Returns the dialog settings object used to share state |
| * between several find/replace dialogs. |
| * |
| * @return the dialog settings to be used |
| */ |
| private IDialogSettings getDialogSettings() { |
| IDialogSettings settings= TextEditorPlugin.getDefault().getDialogSettings(); |
| fDialogSettings= settings.getSection(FindReplaceDialog.class.getName()); |
| if (fDialogSettings == null) |
| fDialogSettings= settings.addNewSection(FindReplaceDialog.class.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.getBoolean("wrap"); //$NON-NLS-1$ |
| fCaseInit= s.getBoolean("casesensitive"); //$NON-NLS-1$ |
| fWholeWordInit= s.getBoolean("wholeword"); //$NON-NLS-1$ |
| fRegExSearch= s.getBoolean("isRegEx"); //$NON-NLS-1$ |
| fSelection= s.get("selection"); //$NON-NLS-1$ |
| |
| String[] findHistory= s.getArray("findhistory"); //$NON-NLS-1$ |
| 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() { |
| if (fFindString == null) |
| return; |
| |
| IDialogSettings s= getDialogSettings(); |
| |
| String selection= fTarget.getSelectionText(); |
| if (selection == null) |
| selection= ""; //$NON-NLS-1$ |
| s.put("selection", selection); //$NON-NLS-1$ |
| |
| if (!fFindHistory.isEmpty() && fFindString.equals(fFindHistory.get(0))) |
| return; |
| |
| int index= fFindHistory.indexOf(fFindString); |
| if (index != -1) |
| fFindHistory.remove(index); |
| fFindHistory.add(0, fFindString); |
| |
| while (fFindHistory.size() > 8) |
| fFindHistory.remove(8); |
| String[] names= new String[fFindHistory.size()]; |
| fFindHistory.toArray(names); |
| s.put("findhistory", names); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns the actual selection of the find replace target. |
| * |
| * @return the actual selection of the find replace target |
| */ |
| private String getSelectionString() { |
| |
| /* |
| * 1GF86V3: ITPUI:WINNT - Internal errors using Find/Replace Dialog |
| * Now uses TextUtilities rather than focusing on '\n' |
| */ |
| String selection= fTarget.getSelectionText(); |
| if (selection != null && selection.length() > 0) { |
| int[] info= TextUtilities.indexOf(TextUtilities.DELIMITERS, selection, 0); |
| if (info[0] > 0) |
| return selection.substring(0, info[0]); |
| else if (info[0] == -1) |
| return selection; |
| } |
| return null; |
| } |
| } |