| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.ui.texteditor; |
| |
| |
| import java.text.BreakIterator; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.regex.PatternSyntaxException; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.BusyIndicator; |
| 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.graphics.Color; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.RGB; |
| 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.Group; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Shell; |
| |
| import org.eclipse.jface.contentassist.SubjectControlContentAssistant; |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.dialogs.IDialogSettings; |
| import org.eclipse.jface.resource.JFaceColors; |
| |
| import org.eclipse.jface.text.DefaultInformationControl; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IFindReplaceTarget; |
| import org.eclipse.jface.text.IFindReplaceTargetExtension; |
| import org.eclipse.jface.text.IFindReplaceTargetExtension3; |
| import org.eclipse.jface.text.IInformationControl; |
| import org.eclipse.jface.text.IInformationControlCreator; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.jface.text.TextUtilities; |
| import org.eclipse.jface.text.contentassist.IContentAssistProcessor; |
| import org.eclipse.jface.text.contentassist.IContentAssistant; |
| |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.contentassist.ContentAssistHandler; |
| import org.eclipse.ui.help.WorkbenchHelp; |
| import org.eclipse.ui.internal.texteditor.TextEditorPlugin; |
| |
| |
| |
| /** |
| * Find/Replace dialog. The dialog is opened on a particular |
| * target but can be re-targeted. Internally used by the <code>FindReplaceAction</code> |
| */ |
| class FindReplaceDialog extends Dialog { |
| |
| /** |
| * Updates the find replace dialog on activation changes. |
| */ |
| class ActivationListener extends ShellAdapter { |
| /* |
| * @see ShellListener#shellActivated(ShellEvent) |
| */ |
| public void shellActivated(ShellEvent e) { |
| |
| String oldText= fFindField.getText(); // XXX workaround for 10766 |
| List oldList= new ArrayList(); |
| oldList.addAll(fFindHistory); |
| |
| readConfiguration(); |
| |
| |
| fFindField.removeModifyListener(fFindModifyListener); |
| |
| updateCombo(fFindField, fFindHistory); |
| if (!fFindHistory.equals(oldList) && !fFindHistory.isEmpty()) |
| fFindField.setText((String) fFindHistory.get(0)); |
| else |
| fFindField.setText(oldText); |
| if (findFieldHadFocus()) |
| fFindField.setSelection(new Point(0, fFindField.getText().length())); |
| fFindField.addModifyListener(fFindModifyListener); |
| |
| fActiveShell= (Shell)e.widget; |
| updateButtonState(); |
| |
| if (findFieldHadFocus() && getShell() == fActiveShell && !fFindField.isDisposed()) |
| fFindField.setFocus(); |
| } |
| |
| /** |
| * Returns <code>true</code> if the find field had focus, |
| * <code>false</code> if it did not. |
| * |
| * @return <code>true</code> if the find field had focus, |
| * <code>false</code> if it did not |
| */ |
| private boolean findFieldHadFocus() { |
| /* |
| * See bug 45447. Under GTK and Motif, the focus of the find field |
| * is already gone when shellDeactivated is called. On the other |
| * hand focus has already been restored when shellActivated is |
| * called. |
| * |
| * Therefore, we select and give focus if either |
| * fGiveFocusToFindField is true or the find field has focus. |
| */ |
| return fGiveFocusToFindField || okToUse(fFindField) && fFindField.isFocusControl(); |
| } |
| |
| /* |
| * @see ShellListener#shellDeactivated(ShellEvent) |
| */ |
| public void shellDeactivated(ShellEvent e) { |
| fGiveFocusToFindField= fFindField.isFocusControl(); |
| |
| storeSettings(); |
| |
| fGlobalRadioButton.setSelection(true); |
| fSelectedRangeRadioButton.setSelection(false); |
| fUseSelectedLines= false; |
| |
| if (fTarget != null && (fTarget instanceof IFindReplaceTargetExtension)) |
| ((IFindReplaceTargetExtension) fTarget).setScope(null); |
| |
| fOldScope= null; |
| |
| fActiveShell= null; |
| updateButtonState(); |
| } |
| } |
| |
| /** |
| * Modify listener to update the search result in case of incremental search. |
| * @since 2.0 |
| */ |
| private class FindModifyListener implements ModifyListener { |
| |
| /* |
| * @see ModifyListener#modifyText(ModifyEvent) |
| */ |
| public void modifyText(ModifyEvent e) { |
| if (isIncrementalSearch() && !isRegExSearchAvailableAndChecked()) { |
| if (fFindField.getText().equals("") && fTarget != null) { //$NON-NLS-1$ |
| // empty selection at base location |
| int offset= fIncrementalBaseLocation.x; |
| |
| if (isForwardSearch() && !fNeedsInitialFindBeforeReplace || !isForwardSearch() && fNeedsInitialFindBeforeReplace) |
| offset= offset + fIncrementalBaseLocation.y; |
| |
| fNeedsInitialFindBeforeReplace= false; |
| findAndSelect(offset, "", isForwardSearch(), isCaseSensitiveSearch(), isWholeWordSearch(), isRegExSearchAvailableAndChecked()); //$NON-NLS-1$ |
| } else { |
| performSearch(false); |
| } |
| } |
| |
| updateButtonState(!isIncrementalSearch()); |
| } |
| } |
| |
| /** The size of the dialogs search history. */ |
| private static final int HISTORY_SIZE= 5; |
| |
| private Point fLocation; |
| private Point fIncrementalBaseLocation; |
| private boolean fWrapInit, fCaseInit, fWholeWordInit, fForwardInit, fGlobalInit, fIncrementalInit; |
| /** |
| * Tells whether an initial find operation is needed |
| * before the replace operation. |
| * @since 3.0 |
| */ |
| private boolean fNeedsInitialFindBeforeReplace; |
| /** |
| * Initial value for telling whether the search string is a regular expression. |
| * @since 3.0 |
| */ |
| boolean fIsRegExInit; |
| |
| private List fFindHistory; |
| private List fReplaceHistory; |
| private IRegion fOldScope; |
| |
| private boolean fIsTargetEditable; |
| private IFindReplaceTarget fTarget; |
| private Shell fParentShell; |
| private Shell fActiveShell; |
| |
| private final ActivationListener fActivationListener= new ActivationListener(); |
| private final ModifyListener fFindModifyListener= new FindModifyListener(); |
| |
| private Label fReplaceLabel, fStatusLabel; |
| private Button fForwardRadioButton, fGlobalRadioButton, fSelectedRangeRadioButton; |
| private Button fCaseCheckBox, fWrapCheckBox, fWholeWordCheckBox, fIncrementalCheckBox; |
| |
| /** |
| * Checkbox for selecting whether the search string is a regular expression. |
| * @since 3.0 |
| */ |
| private Button fIsRegExCheckBox; |
| |
| private Button fReplaceSelectionButton, fReplaceFindButton, fFindNextButton, fReplaceAllButton; |
| Combo fFindField, fReplaceField; |
| private Rectangle fDialogPositionInit; |
| |
| private IDialogSettings fDialogSettings; |
| /** |
| * Tells whether the target supports regular expressions. |
| * <code>true</code> if the target supports regular expressions |
| * @since 3.0 |
| */ |
| private boolean fIsTargetSupportingRegEx; |
| /** |
| * Tells whether fUseSelectedLines radio is checked. |
| * @since 3.0 |
| */ |
| private boolean fUseSelectedLines; |
| /** |
| * The content assist handler for the find combo. |
| * @since 3.0 |
| */ |
| private ContentAssistHandler fFindContentAssistHandler; |
| /** |
| * The content assist handler for the replace combo. |
| * @since 3.0 |
| */ |
| private ContentAssistHandler fReplaceContentAssistHandler; |
| /** |
| * Content assist's proposal popup background color. |
| * @since 3.0 |
| */ |
| private Color fProposalPopupBackgroundColor; |
| /** |
| * Content assist's proposal popup foreground color. |
| * @since 3.0 |
| */ |
| private Color fProposalPopupForegroundColor; |
| /** |
| * <code>true</code> if the find field should receive focus the next time |
| * the dialog is activated, <code>false</code> otherwise. |
| * @since 3.0 |
| */ |
| private boolean fGiveFocusToFindField= true; |
| |
| |
| |
| /** |
| * Creates a new dialog with the given shell as parent. |
| * @param parentShell the parent shell |
| */ |
| public FindReplaceDialog(Shell parentShell) { |
| super(parentShell); |
| |
| fParentShell= null; |
| fTarget= null; |
| |
| fDialogPositionInit= null; |
| fFindHistory= new ArrayList(HISTORY_SIZE - 1); |
| fReplaceHistory= new ArrayList(HISTORY_SIZE - 1); |
| |
| fWrapInit= false; |
| fCaseInit= false; |
| fIsRegExInit= false; |
| fWholeWordInit= false; |
| fIncrementalInit= false; |
| fGlobalInit= true; |
| fForwardInit= true; |
| |
| readConfiguration(); |
| |
| setShellStyle(SWT.CLOSE | SWT.MODELESS | SWT.BORDER | SWT.TITLE); |
| setBlockOnOpen(false); |
| } |
| |
| /** |
| * Returns this dialog's parent shell. |
| * @return the dialog's parent shell |
| */ |
| public Shell getParentShell() { |
| return super.getParentShell(); |
| } |
| |
| |
| /** |
| * 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 boolean okToUse(Control control) { |
| return control != null && !control.isDisposed(); |
| } |
| |
| /* |
| * @see org.eclipse.jface.window.Window#create() |
| */ |
| public void create() { |
| |
| super.create(); |
| |
| Shell shell= getShell(); |
| shell.addShellListener(fActivationListener); |
| if (fLocation != null) |
| shell.setLocation(fLocation); |
| |
| // set help context |
| WorkbenchHelp.setHelp(shell, IAbstractTextEditorHelpContextIds.FIND_REPLACE_DIALOG); |
| |
| // fill in combo contents |
| fFindField.removeModifyListener(fFindModifyListener); |
| updateCombo(fFindField, fFindHistory); |
| fFindField.addModifyListener(fFindModifyListener); |
| updateCombo(fReplaceField, fReplaceHistory); |
| |
| // get find string |
| initFindStringFromSelection(); |
| |
| // set dialog position |
| if (fDialogPositionInit != null) |
| shell.setBounds(fDialogPositionInit); |
| |
| shell.setText(EditorMessages.getString("FindReplace.title")); //$NON-NLS-1$ |
| // shell.setImage(null); |
| } |
| |
| /** |
| * Create the button section of the find/replace dialog. |
| * |
| * @param parent the parent composite |
| * @return the button section |
| */ |
| private Composite createButtonSection(Composite parent) { |
| |
| Composite panel= new Composite(parent, SWT.NULL); |
| GridLayout layout= new GridLayout(); |
| layout.numColumns= -2; |
| layout.makeColumnsEqualWidth= true; |
| panel.setLayout(layout); |
| |
| fFindNextButton= makeButton(panel, "FindReplace.FindNextButton.label", 102, true, new SelectionAdapter() { //$NON-NLS-1$ |
| public void widgetSelected(SelectionEvent e) { |
| if (isIncrementalSearch() && !isRegExSearchAvailableAndChecked()) |
| initIncrementalBaseLocation(); |
| |
| fNeedsInitialFindBeforeReplace= false; |
| performSearch(); |
| updateFindHistory(); |
| fFindNextButton.setFocus(); |
| } |
| }); |
| setGridData(fFindNextButton, GridData.FILL, true, GridData.FILL, false); |
| |
| fReplaceFindButton= makeButton(panel, "FindReplace.ReplaceFindButton.label", 103, false, new SelectionAdapter() { //$NON-NLS-1$ |
| public void widgetSelected(SelectionEvent e) { |
| if (fNeedsInitialFindBeforeReplace) |
| performSearch(); |
| if (performReplaceSelection()) |
| performSearch(); |
| updateFindAndReplaceHistory(); |
| fReplaceFindButton.setFocus(); |
| } |
| }); |
| setGridData(fReplaceFindButton, GridData.FILL, true, GridData.FILL, false); |
| |
| fReplaceSelectionButton= makeButton(panel, "FindReplace.ReplaceSelectionButton.label", 104, false, new SelectionAdapter() { //$NON-NLS-1$ |
| public void widgetSelected(SelectionEvent e) { |
| if (fNeedsInitialFindBeforeReplace) |
| performSearch(); |
| performReplaceSelection(); |
| updateFindAndReplaceHistory(); |
| fFindNextButton.setFocus(); |
| } |
| }); |
| setGridData(fReplaceSelectionButton, GridData.FILL, true, GridData.FILL, false); |
| |
| fReplaceAllButton= makeButton(panel, "FindReplace.ReplaceAllButton.label", 105, false, new SelectionAdapter() { //$NON-NLS-1$ |
| public void widgetSelected(SelectionEvent e) { |
| performReplaceAll(); |
| updateFindAndReplaceHistory(); |
| fFindNextButton.setFocus(); |
| } |
| }); |
| setGridData(fReplaceAllButton, GridData.FILL, true, GridData.FILL, false); |
| |
| // Make the all the buttons the same size as the Remove Selection button. |
| fReplaceAllButton.setEnabled(isEditable()); |
| |
| return panel; |
| } |
| |
| /** |
| * Creates the options configuration section of the find replace dialog. |
| * |
| * @param parent the parent composite |
| * @return the options configuration section |
| */ |
| private Composite createConfigPanel(Composite parent) { |
| |
| Composite panel= new Composite(parent, SWT.NULL); |
| GridLayout layout= new GridLayout(); |
| layout.numColumns= 2; |
| layout.makeColumnsEqualWidth= true; |
| panel.setLayout(layout); |
| |
| Composite directionGroup= createDirectionGroup(panel); |
| setGridData(directionGroup, GridData.FILL, true, GridData.FILL, false); |
| Composite scopeGroup= createScopeGroup(panel); |
| setGridData(scopeGroup, GridData.FILL, true, GridData.FILL, false); |
| |
| Composite optionsGroup= createOptionsGroup(panel); |
| setGridData(optionsGroup, GridData.FILL, true, GridData.FILL, false); |
| GridData data= (GridData) optionsGroup.getLayoutData(); |
| data.horizontalSpan= 2; |
| optionsGroup.setLayoutData(data); |
| |
| return panel; |
| } |
| |
| /* |
| * @see org.eclipse.jface.window.Window#createContents(org.eclipse.swt.widgets.Composite) |
| */ |
| 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); |
| |
| Composite inputPanel= createInputPanel(panel); |
| setGridData(inputPanel, GridData.FILL, true, GridData.CENTER, false); |
| |
| Composite configPanel= createConfigPanel(panel); |
| setGridData(configPanel, GridData.FILL, true, GridData.CENTER, true); |
| |
| Composite buttonPanelB= createButtonSection(panel); |
| setGridData(buttonPanelB, GridData.FILL, true, GridData.CENTER, false); |
| |
| Composite statusBar= createStatusAndCloseButton(panel); |
| setGridData(statusBar, GridData.FILL, true, GridData.CENTER, false); |
| |
| updateButtonState(); |
| |
| applyDialogFont(panel); |
| |
| // Setup content assistants for find and replace combos |
| fProposalPopupBackgroundColor= new Color(getShell().getDisplay(), new RGB(254, 241, 233)); |
| fProposalPopupForegroundColor= new Color(getShell().getDisplay(), new RGB(0, 0, 0)); |
| |
| return panel; |
| } |
| |
| private void setContentAssistsEnablement(boolean enable) { |
| if (enable) { |
| if (fFindContentAssistHandler == null) { |
| fFindContentAssistHandler= ContentAssistHandler.createHandlerForCombo(fFindField, createContentAssistant()); |
| fReplaceContentAssistHandler= ContentAssistHandler.createHandlerForCombo(fReplaceField, createContentAssistant()); |
| } |
| fFindContentAssistHandler.setEnabled(true); |
| fReplaceContentAssistHandler.setEnabled(true); |
| |
| } else { |
| if (fFindContentAssistHandler == null) |
| return; |
| fFindContentAssistHandler.setEnabled(false); |
| fReplaceContentAssistHandler.setEnabled(false); |
| } |
| } |
| |
| /** |
| * Creates the direction defining part of the options defining section |
| * of the find replace 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(EditorMessages.getString("FindReplace.Direction")); //$NON-NLS-1$ |
| GridLayout groupLayout= new GridLayout(); |
| group.setLayout(groupLayout); |
| group.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| |
| SelectionListener selectionListener= new SelectionListener() { |
| public void widgetSelected(SelectionEvent e) { |
| if (isIncrementalSearch() && !isRegExSearchAvailableAndChecked()) |
| initIncrementalBaseLocation(); |
| } |
| |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| }; |
| |
| fForwardRadioButton= new Button(group, SWT.RADIO | SWT.LEFT); |
| fForwardRadioButton.setText(EditorMessages.getString("FindReplace.ForwardRadioButton.label")); //$NON-NLS-1$ |
| setGridData(fForwardRadioButton, GridData.BEGINNING, false, GridData.CENTER, false); |
| fForwardRadioButton.addSelectionListener(selectionListener); |
| |
| Button backwardRadioButton= new Button(group, SWT.RADIO | SWT.LEFT); |
| backwardRadioButton.setText(EditorMessages.getString("FindReplace.BackwardRadioButton.label")); //$NON-NLS-1$ |
| setGridData(backwardRadioButton, GridData.BEGINNING, false, GridData.CENTER, false); |
| backwardRadioButton.addSelectionListener(selectionListener); |
| |
| backwardRadioButton.setSelection(!fForwardInit); |
| fForwardRadioButton.setSelection(fForwardInit); |
| |
| return panel; |
| } |
| |
| /** |
| * Creates the scope defining part of the find replace dialog. |
| * |
| * @param parent the parent composite |
| * @return the scope defining part |
| * @since 2.0 |
| */ |
| private Composite createScopeGroup(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(EditorMessages.getString("FindReplace.Scope")); //$NON-NLS-1$ |
| GridLayout groupLayout= new GridLayout(); |
| group.setLayout(groupLayout); |
| group.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| |
| fGlobalRadioButton= new Button(group, SWT.RADIO | SWT.LEFT); |
| fGlobalRadioButton.setText(EditorMessages.getString("FindReplace.GlobalRadioButton.label")); //$NON-NLS-1$ |
| setGridData(fGlobalRadioButton, GridData.BEGINNING, false, GridData.CENTER, false); |
| fGlobalRadioButton.setSelection(fGlobalInit); |
| fGlobalRadioButton.addSelectionListener(new SelectionListener() { |
| public void widgetSelected(SelectionEvent e) { |
| if (!fGlobalRadioButton.getSelection() || !fUseSelectedLines) |
| return; |
| fUseSelectedLines= false; |
| useSelectedLines(false); |
| } |
| |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| }); |
| |
| fSelectedRangeRadioButton= new Button(group, SWT.RADIO | SWT.LEFT); |
| fSelectedRangeRadioButton.setText(EditorMessages.getString("FindReplace.SelectedRangeRadioButton.label")); //$NON-NLS-1$ |
| setGridData(fSelectedRangeRadioButton, GridData.BEGINNING, false, GridData.CENTER, false); |
| fSelectedRangeRadioButton.setSelection(!fGlobalInit); |
| fUseSelectedLines= !fGlobalInit; |
| fSelectedRangeRadioButton.addSelectionListener(new SelectionListener() { |
| public void widgetSelected(SelectionEvent e) { |
| if (!fSelectedRangeRadioButton.getSelection() || fUseSelectedLines) |
| return; |
| fUseSelectedLines= true; |
| useSelectedLines(true); |
| } |
| |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| }); |
| |
| return panel; |
| } |
| |
| /** |
| * Tells the dialog to perform searches only in the scope given by the actually selected lines. |
| * @param selectedLines <code>true</code> if selected lines should be used |
| * @since 2.0 |
| */ |
| private void useSelectedLines(boolean selectedLines) { |
| if (isIncrementalSearch() && !isRegExSearchAvailableAndChecked()) |
| initIncrementalBaseLocation(); |
| |
| if (fTarget == null || !(fTarget instanceof IFindReplaceTargetExtension)) |
| return; |
| |
| IFindReplaceTargetExtension extensionTarget= (IFindReplaceTargetExtension) fTarget; |
| |
| if (selectedLines) { |
| |
| IRegion scope; |
| if (fOldScope == null) { |
| Point lineSelection= extensionTarget.getLineSelection(); |
| scope= new Region(lineSelection.x, lineSelection.y); |
| } else { |
| scope= fOldScope; |
| fOldScope= null; |
| } |
| |
| int offset= isForwardSearch() |
| ? scope.getOffset() |
| : scope.getOffset() + scope.getLength(); |
| |
| extensionTarget.setSelection(offset, 0); |
| extensionTarget.setScope(scope); |
| } else { |
| fOldScope= extensionTarget.getScope(); |
| extensionTarget.setScope(null); |
| } |
| } |
| |
| /** |
| * Creates the panel where the user specifies the text to search |
| * for and the optional replacement text. |
| * |
| * @param parent the parent composite |
| * @return the input panel |
| */ |
| private Composite createInputPanel(Composite parent) { |
| |
| ModifyListener listener= new ModifyListener() { |
| public void modifyText(ModifyEvent e) { |
| updateButtonState(); |
| } |
| }; |
| |
| 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(EditorMessages.getString("FindReplace.Find.label")); //$NON-NLS-1$ |
| setGridData(findLabel, GridData.BEGINNING, false, GridData.CENTER, false); |
| |
| fFindField= new Combo(panel, SWT.DROP_DOWN | SWT.BORDER); |
| setGridData(fFindField, GridData.FILL, true, GridData.CENTER, false); |
| fFindField.addModifyListener(fFindModifyListener); |
| |
| fReplaceLabel= new Label(panel, SWT.LEFT); |
| fReplaceLabel.setText(EditorMessages.getString("FindReplace.Replace.label")); //$NON-NLS-1$ |
| setGridData(fReplaceLabel, GridData.BEGINNING, false, GridData.CENTER, false); |
| |
| fReplaceField= new Combo(panel, SWT.DROP_DOWN | SWT.BORDER); |
| setGridData(fReplaceField, GridData.FILL, true, GridData.CENTER, false); |
| fReplaceField.addModifyListener(listener); |
| |
| return panel; |
| } |
| |
| /** |
| * Creates the functional options part of the options defining |
| * section of the find replace dialog. |
| * |
| * @param parent the parent composite |
| * @return the options group |
| */ |
| private Composite createOptionsGroup(Composite parent) { |
| |
| Composite panel= new Composite(parent, SWT.NULL); |
| GridLayout layout= new GridLayout(); |
| layout.marginWidth= 0; |
| layout.marginHeight= 0; |
| panel.setLayout(layout); |
| |
| Group group= new Group(panel, SWT.SHADOW_NONE); |
| group.setText(EditorMessages.getString("FindReplace.Options")); //$NON-NLS-1$ |
| GridLayout groupLayout= new GridLayout(); |
| groupLayout.numColumns= 2; |
| groupLayout.makeColumnsEqualWidth= true; |
| group.setLayout(groupLayout); |
| group.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| |
| SelectionListener selectionListener= new SelectionListener() { |
| public void widgetSelected(SelectionEvent e) { |
| storeSettings(); |
| } |
| |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| }; |
| |
| fCaseCheckBox= new Button(group, SWT.CHECK | SWT.LEFT); |
| fCaseCheckBox.setText(EditorMessages.getString("FindReplace.CaseCheckBox.label")); //$NON-NLS-1$ |
| setGridData(fCaseCheckBox, GridData.BEGINNING, false, GridData.CENTER, false); |
| fCaseCheckBox.setSelection(fCaseInit); |
| fCaseCheckBox.addSelectionListener(selectionListener); |
| |
| fWrapCheckBox= new Button(group, SWT.CHECK | SWT.LEFT); |
| fWrapCheckBox.setText(EditorMessages.getString("FindReplace.WrapCheckBox.label")); //$NON-NLS-1$ |
| setGridData(fWrapCheckBox, GridData.BEGINNING, false, GridData.CENTER, false); |
| fWrapCheckBox.setSelection(fWrapInit); |
| fWrapCheckBox.addSelectionListener(selectionListener); |
| |
| fWholeWordCheckBox= new Button(group, SWT.CHECK | SWT.LEFT); |
| fWholeWordCheckBox.setText(EditorMessages.getString("FindReplace.WholeWordCheckBox.label")); //$NON-NLS-1$ |
| setGridData(fWholeWordCheckBox, GridData.BEGINNING, false, GridData.CENTER, false); |
| fWholeWordCheckBox.setSelection(fWholeWordInit); |
| fWholeWordCheckBox.addSelectionListener(selectionListener); |
| |
| fIncrementalCheckBox= new Button(group, SWT.CHECK | SWT.LEFT); |
| fIncrementalCheckBox.setText(EditorMessages.getString("FindReplace.IncrementalCheckBox.label")); //$NON-NLS-1$ |
| setGridData(fIncrementalCheckBox, GridData.BEGINNING, false, GridData.CENTER, false); |
| fIncrementalCheckBox.setSelection(fIncrementalInit); |
| fIncrementalCheckBox.addSelectionListener(new SelectionListener() { |
| public void widgetSelected(SelectionEvent e) { |
| if (isIncrementalSearch() && !isRegExSearch()) |
| initIncrementalBaseLocation(); |
| |
| storeSettings(); |
| } |
| |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| }); |
| |
| fIsRegExCheckBox= new Button(group, SWT.CHECK | SWT.LEFT); |
| fIsRegExCheckBox.setText(EditorMessages.getString("FindReplace.RegExCheckbox.label")); //$NON-NLS-1$ |
| setGridData(fIsRegExCheckBox, GridData.BEGINNING, false, GridData.CENTER, false); |
| ((GridData)fIsRegExCheckBox.getLayoutData()).horizontalSpan= 2; |
| fIsRegExCheckBox.setSelection(fIsRegExInit); |
| fIsRegExCheckBox.addSelectionListener(new SelectionAdapter() { |
| /* |
| * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) |
| */ |
| public void widgetSelected(SelectionEvent e) { |
| boolean newState= fIsRegExCheckBox.getSelection(); |
| fIncrementalCheckBox.setEnabled(!newState); |
| fWholeWordCheckBox.setEnabled(!newState); |
| updateButtonState(); |
| storeSettings(); |
| setContentAssistsEnablement(newState); |
| } |
| }); |
| fWholeWordCheckBox.setEnabled(!isRegExSearchAvailableAndChecked()); |
| fWholeWordCheckBox.addSelectionListener(new SelectionAdapter() { |
| /* |
| * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) |
| */ |
| public void widgetSelected(SelectionEvent e) { |
| updateButtonState(); |
| } |
| }); |
| fIncrementalCheckBox.setEnabled(!isRegExSearchAvailableAndChecked()); |
| 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, GridData.FILL, true, GridData.CENTER, false); |
| |
| String label= EditorMessages.getString("FindReplace.CloseButton.label"); //$NON-NLS-1$ |
| Button closeButton= createButton(panel, 101, label, false); |
| setGridData(closeButton, GridData.END, false, GridData.END, false); |
| |
| return panel; |
| } |
| |
| /* |
| * @see Dialog#buttonPressed |
| */ |
| protected void buttonPressed(int buttonID) { |
| if (buttonID == 101) |
| close(); |
| } |
| |
| |
| |
| // ------- action invocation --------------------------------------- |
| |
| /** |
| * Returns the position of 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 startPosition the position at which to start the search |
| * @param forwardSearch the direction of the search |
| * @param caseSensitive should the search be case sensitive |
| * @param wrapSearch should the search wrap to the start/end if arrived at the end/start |
| * @param wholeWord does the search string represent a complete word |
| * @param regExSearch if <code>true</code> findString represents a regular expression |
| * @return the occurrence of the find string following the options or <code>-1</code> if nothing found |
| * @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) { |
| if (okToUse(getShell()) && !isIncrementalSearch()) |
| getShell().getDisplay().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) { |
| if (okToUse(getShell()) && !isIncrementalSearch()) |
| getShell().getDisplay().beep(); |
| index= findAndSelect(-1, findString, false, caseSensitive, wholeWord, regExSearch); |
| } |
| return index; |
| } |
| return findAndSelect(startPosition - 1, findString, false, caseSensitive, wholeWord, regExSearch); |
| } |
| |
| /** |
| * 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) |
| return ((IFindReplaceTargetExtension3)fTarget).findAndSelect(offset, findString, forwardSearch, caseSensitive, wholeWord, regExSearch); |
| else |
| return fTarget.findAndSelect(offset, findString, forwardSearch, caseSensitive, wholeWord); |
| } |
| |
| /** |
| * Replaces the selection with <code>replaceString</code>. If |
| * <code>regExReplace</code> is <code>true</code>, |
| * <code>replaceString</code> is a regex replace pattern which will get |
| * expanded if the underlying target supports it. Returns the region of the |
| * inserted text; note that the returned selection covers the expanded |
| * pattern in case of regex replace. |
| * |
| * @param replaceString the replace string (or a regex pattern) |
| * @param regExReplace <code>true</code> if <code>replaceString</code> |
| * is a pattern |
| * @return the selection after replacing, i.e. the inserted text |
| * @since 3.0 |
| */ |
| Point replaceSelection(String replaceString, boolean regExReplace) { |
| if (fTarget instanceof IFindReplaceTargetExtension3) |
| ((IFindReplaceTargetExtension3)fTarget).replaceSelection(replaceString, regExReplace); |
| else |
| fTarget.replaceSelection(replaceString); |
| |
| return fTarget.getSelection(); |
| } |
| |
| /** |
| * Returns whether the specified search string can be found using the given options. |
| * |
| * @param findString the string to search for |
| * @param forwardSearch the direction of the search |
| * @param caseSensitive should the search be case sensitive |
| * @param wrapSearch should the search wrap to the start/end if arrived at the end/start |
| * @param wholeWord does the search string represent a complete word |
| * @param incremental is this an incremental search |
| * @param regExSearch if <code>true</code> findString represents a regular expression |
| * @return <code>true</code> if the search 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 incremental, boolean regExSearch) { |
| |
| if (fTarget == null) |
| return false; |
| |
| Point r= null; |
| if (incremental) |
| r= fIncrementalBaseLocation; |
| else |
| r= fTarget.getSelection(); |
| |
| int findReplacePosition= r.x; |
| if (forwardSearch && !fNeedsInitialFindBeforeReplace || !forwardSearch && fNeedsInitialFindBeforeReplace) |
| findReplacePosition += r.y; |
| |
| fNeedsInitialFindBeforeReplace= false; |
| |
| int index= findIndex(findString, findReplacePosition, forwardSearch, caseSensitive, wrapSearch, wholeWord, regExSearch); |
| |
| if (index != -1) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * Returns the dialog's boundaries. |
| * @return the dialog's boundaries |
| */ |
| private Rectangle getDialogBoundaries() { |
| if (okToUse(getShell())) { |
| return getShell().getBounds(); |
| } else { |
| return fDialogPositionInit; |
| } |
| } |
| |
| /** |
| * Returns the dialog's history. |
| * @return the dialog's history |
| */ |
| private List getFindHistory() { |
| return fFindHistory; |
| } |
| |
| // ------- 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 replace history. |
| * @return the dialog's replace history |
| */ |
| private List getReplaceHistory() { |
| return fReplaceHistory; |
| } |
| |
| /** |
| * Retrieves the replacement string from the appropriate text input field and returns it. |
| * @return the replacement string |
| */ |
| private String getReplaceString() { |
| if (okToUse(fReplaceField)) { |
| return fReplaceField.getText(); |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| |
| // ------- init / close --------------------------------------- |
| |
| /** |
| * Returns the actual selection of the find replace target. |
| * @return the selection of the target |
| */ |
| private String getSelectionString() { |
| 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; |
| } |
| |
| /** |
| * @see org.eclipse.jface.window.Window#close() |
| */ |
| 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); |
| } |
| |
| if (fParentShell != null) { |
| fParentShell.removeShellListener(fActivationListener); |
| fParentShell= null; |
| } |
| |
| getShell().removeShellListener(fActivationListener); |
| |
| // store current settings in case of re-open |
| storeSettings(); |
| |
| if (fTarget != null && fTarget instanceof IFindReplaceTargetExtension) |
| ((IFindReplaceTargetExtension) fTarget).endSession(); |
| |
| setContentAssistsEnablement(false); |
| fFindContentAssistHandler= null; |
| fReplaceContentAssistHandler= null; |
| |
| fProposalPopupBackgroundColor.dispose(); |
| fProposalPopupForegroundColor.dispose(); |
| |
| // prevent leaks |
| fActiveShell= null; |
| fTarget= null; |
| |
| } |
| |
| /** |
| * Writes the current selection to the dialog settings. |
| * @since 3.0 |
| */ |
| private void writeSelection() { |
| if (fTarget == null) |
| return; |
| String selection= fTarget.getSelectionText(); |
| if (selection == null) |
| selection= ""; //$NON-NLS-1$ |
| |
| IDialogSettings s= getDialogSettings(); |
| s.put("selection", selection); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Stores the current state in the dialog settings. |
| * @since 2.0 |
| */ |
| private void storeSettings() { |
| fDialogPositionInit= getDialogBoundaries(); |
| fWrapInit= isWrapSearch(); |
| fWholeWordInit= isWholeWordSearch(); |
| fCaseInit= isCaseSensitiveSearch(); |
| fIsRegExInit= isRegExSearch(); |
| fIncrementalInit= isIncrementalSearch(); |
| fForwardInit= isForwardSearch(); |
| |
| writeConfiguration(); |
| } |
| |
| /** |
| * Initializes the string to search for and the appropriate |
| * text inout field based on the selection found in the |
| * action's target. |
| */ |
| private void initFindStringFromSelection() { |
| if (fTarget != null && okToUse(fFindField)) { |
| String selection= getSelectionString(); |
| fFindField.removeModifyListener(fFindModifyListener); |
| if (selection != null) { |
| fFindField.setText(selection); |
| if (!selection.equals(fTarget.getSelectionText())) { |
| useSelectedLines(true); |
| fGlobalRadioButton.setSelection(false); |
| fSelectedRangeRadioButton.setSelection(true); |
| fUseSelectedLines= true; |
| } |
| } else { |
| if ("".equals(fFindField.getText())) { //$NON-NLS-1$ |
| if (fFindHistory.size() > 0) |
| fFindField.setText((String) fFindHistory.get(0)); |
| else |
| fFindField.setText(""); //$NON-NLS-1$ |
| } |
| } |
| fFindField.setSelection(new Point(0, fFindField.getText().length())); |
| fFindField.addModifyListener(fFindModifyListener); |
| } |
| } |
| |
| /** |
| * Initializes the anchor used as starting point for incremental searching. |
| * @since 2.0 |
| */ |
| private void initIncrementalBaseLocation() { |
| if (fTarget != null && isIncrementalSearch() && !isRegExSearchAvailableAndChecked()) { |
| fIncrementalBaseLocation= fTarget.getSelection(); |
| } else { |
| fIncrementalBaseLocation= new Point(0, 0); |
| } |
| } |
| |
| // ------- history --------------------------------------- |
| |
| /** |
| * 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 |
| * @since 3.0 |
| */ |
| private boolean isRegExSearch() { |
| if (okToUse(fIsRegExCheckBox)) { |
| return fIsRegExCheckBox.getSelection(); |
| } |
| return fIsRegExInit; |
| } |
| |
| /** |
| * If the target supports regular expressions search retrieves and returns |
| * regEx option from appropriate check box. |
| * |
| * @return <code>true</code> if regEx is available and checked |
| * @since 3.0 |
| */ |
| private boolean isRegExSearchAvailableAndChecked() { |
| if (okToUse(fIsRegExCheckBox)) { |
| return fIsTargetSupportingRegEx && 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 isWholeWordSearch() { |
| if (okToUse(fWholeWordCheckBox)) { |
| return fWholeWordCheckBox.getSelection(); |
| } |
| return fWholeWordInit; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Retrieves and returns the option incremental search from the appropriate check box. |
| * @return <code>true</code> if incremental search |
| * @since 2.0 |
| */ |
| private boolean isIncrementalSearch() { |
| if (okToUse(fIncrementalCheckBox)) { |
| return fIncrementalCheckBox.getSelection(); |
| } |
| return fIncrementalInit; |
| } |
| |
| /** |
| * Creates a button. |
| * @param parent the parent control |
| * @param key the key to lookup 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 key, int id, boolean dfltButton, SelectionListener listener) { |
| String label= EditorMessages.getString(key); |
| Button b= createButton(parent, id, label, dfltButton); |
| b.addSelectionListener(listener); |
| return b; |
| } |
| |
| /** |
| * Returns the status line manager of the active editor or <code>null</code> if there is no such editor. |
| * @return the status line manager of the active editor |
| */ |
| private IEditorStatusLine getStatusLineManager() { |
| IWorkbenchWindow window= PlatformUI.getWorkbench().getActiveWorkbenchWindow(); |
| if (window == null) |
| return null; |
| |
| IWorkbenchPage page= window.getActivePage(); |
| if (page == null) |
| return null; |
| |
| IEditorPart editor= page.getActiveEditor(); |
| if (editor == null) |
| return null; |
| |
| return (IEditorStatusLine) editor.getAdapter(IEditorStatusLine.class); |
| } |
| |
| /** |
| * Sets the given status message in the status line. |
| * |
| * @param error <code>true</code> if it is an error |
| * @param message the error message |
| */ |
| private void statusMessage(boolean error, String message) { |
| fStatusLabel.setText(message); |
| |
| if (error) |
| fStatusLabel.setForeground(JFaceColors.getErrorText(fStatusLabel.getDisplay())); |
| else |
| fStatusLabel.setForeground(null); |
| |
| IEditorStatusLine statusLine= getStatusLineManager(); |
| if (statusLine != null) |
| statusLine.setMessage(error, message, null); |
| |
| if (error) |
| getShell().getDisplay().beep(); |
| } |
| |
| /** |
| * Sets the given error message in the status line. |
| * @param message the message |
| */ |
| private void statusError(String message) { |
| statusMessage(true, message); |
| } |
| |
| /** |
| * Sets the given message in the status line. |
| * @param message the message |
| */ |
| private void statusMessage(String message) { |
| statusMessage(false, message); |
| } |
| |
| /** |
| * Replaces all occurrences of the user's findString with |
| * the replace string. Indicate to the user the number of replacements |
| * that occur. |
| */ |
| private void performReplaceAll() { |
| |
| int replaceCount= 0; |
| final String replaceString= getReplaceString(); |
| final String findString= getFindString(); |
| |
| if (findString != null && findString.length() > 0) { |
| |
| class ReplaceAllRunnable implements Runnable { |
| public int numberOfOccurrences; |
| public void run() { |
| numberOfOccurrences= replaceAll(findString, replaceString == null ? "" : replaceString, isForwardSearch(), isCaseSensitiveSearch(), isWrapSearch(), isWholeWordSearch() && !isRegExSearchAvailableAndChecked(), isRegExSearchAvailableAndChecked()); //$NON-NLS-1$ |
| } |
| } |
| |
| try { |
| ReplaceAllRunnable runnable= new ReplaceAllRunnable(); |
| BusyIndicator.showWhile(fActiveShell.getDisplay(), runnable); |
| replaceCount= runnable.numberOfOccurrences; |
| |
| if (replaceCount != 0) { |
| if (replaceCount == 1) { // not plural |
| statusMessage(EditorMessages.getString("FindReplace.Status.replacement.label")); //$NON-NLS-1$ |
| } else { |
| String msg= EditorMessages.getString("FindReplace.Status.replacements.label"); //$NON-NLS-1$ |
| msg= MessageFormat.format(msg, new Object[] {String.valueOf(replaceCount)}); |
| statusMessage(msg); |
| } |
| } else { |
| statusMessage(EditorMessages.getString("FindReplace.Status.noMatch.label")); //$NON-NLS-1$ |
| } |
| } catch (PatternSyntaxException ex) { |
| statusError(ex.getLocalizedMessage()); |
| } catch (IllegalStateException ex) { |
| // we don't keep state in this dialog |
| } |
| } |
| writeSelection(); |
| updateButtonState(); |
| } |
| |
| /** |
| * Validates the state of the find/replace target. |
| * @return <code>true</code> if target can be changed, <code>false</code> otherwise |
| * @since 2.1 |
| */ |
| private boolean validateTargetState() { |
| |
| if (fTarget instanceof IFindReplaceTargetExtension2) { |
| IFindReplaceTargetExtension2 extension= (IFindReplaceTargetExtension2) fTarget; |
| if (!extension.validateTargetState()) { |
| statusError(EditorMessages.getString("FindReplaceDialog.read_only")); //$NON-NLS-1$ |
| updateButtonState(); |
| return false; |
| } |
| } |
| return isEditable(); |
| } |
| |
| /** |
| * Replaces the current selection of the target with the user's |
| * replace string. |
| * |
| * @return <code>true</code> if the operation was successful |
| */ |
| private boolean performReplaceSelection() { |
| |
| if (!validateTargetState()) |
| return false; |
| |
| String replaceString= getReplaceString(); |
| if (replaceString == null) |
| replaceString= ""; //$NON-NLS-1$ |
| |
| boolean replaced; |
| try { |
| replaceSelection(replaceString, isRegExSearchAvailableAndChecked()); |
| replaced= true; |
| writeSelection(); |
| } catch (PatternSyntaxException ex) { |
| statusError(ex.getLocalizedMessage()); |
| replaced= false; |
| } catch (IllegalStateException ex) { |
| replaced= false; |
| } |
| |
| updateButtonState(); |
| return replaced; |
| } |
| |
| /** |
| * Locates the user's findString in the text of the target. |
| */ |
| private void performSearch() { |
| performSearch(isIncrementalSearch() && !isRegExSearchAvailableAndChecked()); |
| } |
| |
| /** |
| * Locates the user's findString in the text of the target. |
| * |
| * @param mustInitIncrementalBaseLocation <code>true</code> if base location must be initialized |
| * @since 3.0 |
| */ |
| private void performSearch(boolean mustInitIncrementalBaseLocation) { |
| |
| if (mustInitIncrementalBaseLocation) |
| initIncrementalBaseLocation(); |
| |
| String findString= getFindString(); |
| |
| if (findString != null && findString.length() > 0) { |
| |
| try { |
| boolean somethingFound= findNext(findString, isForwardSearch(), isCaseSensitiveSearch(), isWrapSearch(), isWholeWordSearch() && !isRegExSearchAvailableAndChecked(), isIncrementalSearch() && !isRegExSearchAvailableAndChecked(), isRegExSearchAvailableAndChecked()); |
| if (somethingFound) { |
| statusMessage(""); //$NON-NLS-1$ |
| } else { |
| statusMessage(EditorMessages.getString("FindReplace.Status.noMatch.label")); //$NON-NLS-1$ |
| } |
| } catch (PatternSyntaxException ex) { |
| statusError(ex.getLocalizedMessage()); |
| } catch (IllegalStateException ex) { |
| // we don't keep state in this dialog |
| } |
| } |
| writeSelection(); |
| updateButtonState(); |
| } |
| |
| /** |
| * Replaces all occurrences of the user's findString with |
| * the replace string. Returns the number of replacements |
| * that occur. |
| * |
| * @param findString the string to search for |
| * @param replaceString the replacement string |
| * @param forwardSearch the search direction |
| * @param caseSensitive should the search be case sensitive |
| * @param wrapSearch should search wrap to start/end if end/start is reached |
| * @param wholeWord does the search string represent a complete word |
| * @param regExSearch if <code>true</code> findString represents a regular expression |
| * @return the number of occurrences |
| * |
| * @since 3.0 |
| */ |
| private int replaceAll(String findString, String replaceString, boolean forwardSearch, boolean caseSensitive, boolean wrapSearch, boolean wholeWord, boolean regExSearch) { |
| |
| int replaceCount= 0; |
| int findReplacePosition= 0; |
| |
| if (wrapSearch) { // search the whole text |
| findReplacePosition= 0; |
| forwardSearch= true; |
| } else if (fTarget.getSelectionText() != null) { |
| // the cursor is set to the end or beginning of the selected text |
| Point selection= fTarget.getSelection(); |
| findReplacePosition= selection.x; |
| } |
| |
| if (!validateTargetState()) |
| return replaceCount; |
| |
| if (fTarget instanceof IFindReplaceTargetExtension) |
| ((IFindReplaceTargetExtension) fTarget).setReplaceAllMode(true); |
| |
| try { |
| int index= 0; |
| while (index != -1) { |
| index= findAndSelect(findReplacePosition, findString, forwardSearch, caseSensitive, wholeWord, regExSearch); |
| if (index != -1) { // substring not contained from current position |
| Point selection= replaceSelection(replaceString, regExSearch); |
| replaceCount++; |
| |
| if (forwardSearch) |
| findReplacePosition= selection.x + selection.y; |
| else { |
| findReplacePosition= selection.x; |
| if (findReplacePosition == -1) |
| break; |
| } |
| } |
| } |
| } finally { |
| if (fTarget instanceof IFindReplaceTargetExtension) |
| ((IFindReplaceTargetExtension) fTarget).setReplaceAllMode(false); |
| } |
| |
| return replaceCount; |
| } |
| |
| // ------- 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 void setGridData(Control component, int horizontalAlignment, boolean grabExcessHorizontalSpace, int verticalAlignment, boolean grabExcessVerticalSpace) { |
| GridData gd= new GridData(); |
| gd.horizontalAlignment= horizontalAlignment; |
| gd.grabExcessHorizontalSpace= grabExcessHorizontalSpace; |
| gd.verticalAlignment= verticalAlignment; |
| gd.grabExcessVerticalSpace= grabExcessVerticalSpace; |
| component.setLayoutData(gd); |
| } |
| |
| /** |
| * Updates the enabled state of the buttons. |
| */ |
| private void updateButtonState() { |
| updateButtonState(false); |
| } |
| |
| /** |
| * Updates the enabled state of the buttons. |
| * |
| * @param disableReplace <code>true</code> if replace button must be disabled |
| * @since 3.0 |
| */ |
| private void updateButtonState(boolean disableReplace) { |
| if (okToUse(getShell()) && okToUse(fFindNextButton)) { |
| String selectedText= null; |
| if (fTarget != null) { |
| selectedText= fTarget.getSelectionText(); |
| } |
| |
| boolean selection= (selectedText != null && selectedText.length() > 0); |
| |
| boolean enable= fTarget != null && (fActiveShell == fParentShell || fActiveShell == getShell()); |
| String str= getFindString(); |
| boolean findString= str != null && str.length() > 0 && (isRegExSearchAvailableAndChecked() || !isWholeWordSearch() || isWord(str)); |
| |
| fFindNextButton.setEnabled(enable && findString); |
| fReplaceSelectionButton.setEnabled(!disableReplace && enable && isEditable() && selection && (!fNeedsInitialFindBeforeReplace || !isRegExSearchAvailableAndChecked())); |
| fReplaceFindButton.setEnabled(!disableReplace && enable && isEditable() && findString && selection && (!fNeedsInitialFindBeforeReplace || !isRegExSearchAvailableAndChecked())); |
| fReplaceAllButton.setEnabled(enable && isEditable() && findString); |
| } |
| } |
| |
| /** |
| * 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.0 |
| */ |
| private boolean isWord(String str) { |
| if (str == null) |
| return false; |
| |
| BreakIterator wordIterator= BreakIterator.getWordInstance(); |
| wordIterator.setText(str); |
| int first= wordIterator.first(); |
| if (first > 0) |
| return false; |
| return wordIterator.next() == str.length(); |
| } |
| |
| /** |
| * Updates the given combo with the given content. |
| * @param combo combo to be updated |
| * @param content to be put into the combo |
| */ |
| private void updateCombo(Combo combo, List content) { |
| combo.removeAll(); |
| for (int i= 0; i < content.size(); i++) { |
| combo.add(content.get(i).toString()); |
| } |
| } |
| |
| // ------- open / reopen --------------------------------------- |
| |
| /** |
| * Called after executed find/replace action to update the history. |
| */ |
| private void updateFindAndReplaceHistory() { |
| updateFindHistory(); |
| if (okToUse(fReplaceField)) { |
| updateHistory(fReplaceField, fReplaceHistory); |
| } |
| |
| } |
| |
| /** |
| * 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 void updateHistory(Combo combo, List history) { |
| String findString= combo.getText(); |
| int index= history.indexOf(findString); |
| if (index != 0) { |
| if (index != -1) { |
| history.remove(index); |
| } |
| history.add(0, findString); |
| updateCombo(combo, history); |
| combo.setText(findString); |
| } |
| } |
| |
| /** |
| * Returns whether the target is editable. |
| * @return <code>true</code> if target is editable |
| */ |
| private boolean isEditable() { |
| boolean isEditable= (fTarget == null ? false : fTarget.isEditable()); |
| return fIsTargetEditable && isEditable; |
| } |
| |
| /** |
| * Updates this dialog because of a different target. |
| * @param target the new target |
| * @param isTargetEditable <code>true</code> if the new target can be modifed |
| * @since 2.0 |
| */ |
| public void updateTarget(IFindReplaceTarget target, boolean isTargetEditable) { |
| |
| fIsTargetEditable= isTargetEditable; |
| fNeedsInitialFindBeforeReplace= true; |
| |
| if (target != fTarget) { |
| if (fTarget != null && fTarget instanceof IFindReplaceTargetExtension) |
| ((IFindReplaceTargetExtension) fTarget).endSession(); |
| |
| fTarget= target; |
| if (target != null) |
| fIsTargetSupportingRegEx= target instanceof IFindReplaceTargetExtension3; |
| |
| if (fTarget != null && fTarget instanceof IFindReplaceTargetExtension) { |
| ((IFindReplaceTargetExtension) fTarget).beginSession(); |
| |
| fGlobalInit= true; |
| fGlobalRadioButton.setSelection(fGlobalInit); |
| fSelectedRangeRadioButton.setSelection(!fGlobalInit); |
| fUseSelectedLines= !fGlobalInit; |
| } |
| } |
| |
| if (okToUse(fIsRegExCheckBox)) |
| fIsRegExCheckBox.setEnabled(fIsTargetSupportingRegEx); |
| |
| if (okToUse(fWholeWordCheckBox)) |
| fWholeWordCheckBox.setEnabled(!isRegExSearchAvailableAndChecked()); |
| |
| if (okToUse(fIncrementalCheckBox)) |
| fIncrementalCheckBox.setEnabled(!isRegExSearchAvailableAndChecked()); |
| |
| if (okToUse(fReplaceLabel)) { |
| fReplaceLabel.setEnabled(isEditable()); |
| fReplaceField.setEnabled(isEditable()); |
| initFindStringFromSelection(); |
| initIncrementalBaseLocation(); |
| updateButtonState(); |
| } |
| |
| // see pr 51073 |
| fGiveFocusToFindField= true; |
| |
| setContentAssistsEnablement(isRegExSearchAvailableAndChecked()); |
| } |
| |
| /** |
| * Sets the parent shell of this dialog to be the given shell. |
| * |
| * @param shell the new parent shell |
| */ |
| 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 |
| * between several find/replace dialogs. |
| * |
| * @return the dialog settings to be used |
| */ |
| private IDialogSettings getDialogSettings() { |
| IDialogSettings settings= TextEditorPlugin.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(); |
| |
| try { |
| int x= s.getInt("x"); //$NON-NLS-1$ |
| int y= s.getInt("y"); //$NON-NLS-1$ |
| fLocation= new Point(x, y); |
| } catch (NumberFormatException e) { |
| fLocation= null; |
| } |
| |
| fWrapInit= s.getBoolean("wrap"); //$NON-NLS-1$ |
| fCaseInit= s.getBoolean("casesensitive"); //$NON-NLS-1$ |
| fWholeWordInit= s.getBoolean("wholeword"); //$NON-NLS-1$ |
| fIncrementalInit= s.getBoolean("incremental"); //$NON-NLS-1$ |
| fIsRegExInit= s.getBoolean("isRegEx"); //$NON-NLS-1$ |
| |
| String[] findHistory= s.getArray("findhistory"); //$NON-NLS-1$ |
| if (findHistory != null) { |
| List history= getFindHistory(); |
| history.clear(); |
| for (int i= 0; i < findHistory.length; i++) |
| history.add(findHistory[i]); |
| } |
| |
| String[] replaceHistory= s.getArray("replacehistory"); //$NON-NLS-1$ |
| if (replaceHistory != null) { |
| List history= getReplaceHistory(); |
| history.clear(); |
| for (int i= 0; i < replaceHistory.length; i++) |
| history.add(replaceHistory[i]); |
| } |
| } |
| |
| /** |
| * Stores its current configuration in the dialog store. |
| */ |
| private void writeConfiguration() { |
| IDialogSettings s= getDialogSettings(); |
| |
| Point location= getShell().getLocation(); |
| s.put("x", location.x); //$NON-NLS-1$ |
| s.put("y", location.y); //$NON-NLS-1$ |
| |
| s.put("wrap", fWrapInit); //$NON-NLS-1$ |
| s.put("casesensitive", fCaseInit); //$NON-NLS-1$ |
| s.put("wholeword", fWholeWordInit); //$NON-NLS-1$ |
| s.put("incremental", fIncrementalInit); //$NON-NLS-1$ |
| s.put("isRegEx", fIsRegExInit); //$NON-NLS-1$ |
| List history= getFindHistory(); |
| while (history.size() > 8) |
| history.remove(8); |
| String[] names= new String[history.size()]; |
| history.toArray(names); |
| s.put("findhistory", names); //$NON-NLS-1$ |
| |
| history= getReplaceHistory(); |
| while (history.size() > 8) |
| history.remove(8); |
| names= new String[history.size()]; |
| history.toArray(names); |
| s.put("replacehistory", names); //$NON-NLS-1$ |
| } |
| |
| // ------------- content assistant ----------------- |
| |
| /** |
| * Create a new regex content assistant. |
| * |
| * @return a new configured content assistant |
| * @since 3.0 |
| */ |
| private SubjectControlContentAssistant createContentAssistant() { |
| final SubjectControlContentAssistant contentAssistant= new SubjectControlContentAssistant(); |
| |
| contentAssistant.setRestoreCompletionProposalSize(getSettings("FindReplaceDialog.completion_proposal_size")); //$NON-NLS-1$ |
| |
| IContentAssistProcessor processor= new RegExContentAssistProcessor(); |
| contentAssistant.setContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE); |
| |
| contentAssistant.enableAutoActivation(isRegExSearchAvailableAndChecked()); |
| contentAssistant.setProposalSelectorBackground(fProposalPopupBackgroundColor); |
| contentAssistant.setProposalSelectorForeground(fProposalPopupForegroundColor); |
| |
| contentAssistant.setContextInformationPopupOrientation(IContentAssistant.CONTEXT_INFO_ABOVE); |
| contentAssistant.setInformationControlCreator(new IInformationControlCreator() { |
| /* |
| * @see org.eclipse.jface.text.IInformationControlCreator#createInformationControl(org.eclipse.swt.widgets.Shell) |
| */ |
| public IInformationControl createInformationControl(Shell parent) { |
| return new DefaultInformationControl(parent); |
| }}); |
| |
| return contentAssistant; |
| } |
| |
| private IDialogSettings getSettings(String sectionName) { |
| IDialogSettings pluginDialogSettings= TextEditorPlugin.getDefault().getDialogSettings(); |
| IDialogSettings settings= pluginDialogSettings.getSection(sectionName); |
| if (settings == null) |
| settings= pluginDialogSettings.addNewSection(sectionName); |
| |
| return settings; |
| } |
| } |