0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.ui.texteditor;
0011:
0012: import java.util.ArrayList;
0013: import java.util.HashSet;
0014: import java.util.List;
0015: import java.util.Set;
0016: import java.util.regex.PatternSyntaxException;
0017:
0018: import org.eclipse.swt.SWT;
0019: import org.eclipse.swt.custom.BusyIndicator;
0020: import org.eclipse.swt.events.ModifyEvent;
0021: import org.eclipse.swt.events.ModifyListener;
0022: import org.eclipse.swt.events.SelectionAdapter;
0023: import org.eclipse.swt.events.SelectionEvent;
0024: import org.eclipse.swt.events.SelectionListener;
0025: import org.eclipse.swt.events.ShellAdapter;
0026: import org.eclipse.swt.events.ShellEvent;
0027: import org.eclipse.swt.graphics.Point;
0028: import org.eclipse.swt.graphics.Rectangle;
0029: import org.eclipse.swt.layout.GridData;
0030: import org.eclipse.swt.layout.GridLayout;
0031: import org.eclipse.swt.widgets.Button;
0032: import org.eclipse.swt.widgets.Combo;
0033: import org.eclipse.swt.widgets.Composite;
0034: import org.eclipse.swt.widgets.Control;
0035: import org.eclipse.swt.widgets.Group;
0036: import org.eclipse.swt.widgets.Label;
0037: import org.eclipse.swt.widgets.Shell;
0038:
0039: import org.eclipse.jface.dialogs.Dialog;
0040: import org.eclipse.jface.dialogs.IDialogSettings;
0041: import org.eclipse.jface.fieldassist.ComboContentAdapter;
0042: import org.eclipse.jface.fieldassist.FieldDecoration;
0043: import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
0044: import org.eclipse.jface.resource.JFaceColors;
0045:
0046: import org.eclipse.jface.text.IFindReplaceTarget;
0047: import org.eclipse.jface.text.IFindReplaceTargetExtension;
0048: import org.eclipse.jface.text.IFindReplaceTargetExtension3;
0049: import org.eclipse.jface.text.IRegion;
0050: import org.eclipse.jface.text.Region;
0051: import org.eclipse.jface.text.TextUtilities;
0052:
0053: import org.eclipse.ui.IEditorPart;
0054: import org.eclipse.ui.IWorkbenchPage;
0055: import org.eclipse.ui.IWorkbenchWindow;
0056: import org.eclipse.ui.PlatformUI;
0057: import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;
0058: import org.eclipse.ui.internal.texteditor.NLSUtility;
0059: import org.eclipse.ui.internal.texteditor.SWTUtil;
0060: import org.eclipse.ui.internal.texteditor.TextEditorPlugin;
0061:
0062: /**
0063: * Find/Replace dialog. The dialog is opened on a particular
0064: * target but can be re-targeted. Internally used by the <code>FindReplaceAction</code>
0065: */
0066: class FindReplaceDialog extends Dialog {
0067:
0068: /**
0069: * Updates the find replace dialog on activation changes.
0070: */
0071: class ActivationListener extends ShellAdapter {
0072: /*
0073: * @see ShellListener#shellActivated(ShellEvent)
0074: */
0075: public void shellActivated(ShellEvent e) {
0076: fActiveShell = (Shell) e.widget;
0077: updateButtonState();
0078:
0079: if (fGiveFocusToFindField && getShell() == fActiveShell
0080: && okToUse(fFindField))
0081: fFindField.setFocus();
0082:
0083: }
0084:
0085: /*
0086: * @see ShellListener#shellDeactivated(ShellEvent)
0087: */
0088: public void shellDeactivated(ShellEvent e) {
0089: fGiveFocusToFindField = false;
0090:
0091: storeSettings();
0092:
0093: fGlobalRadioButton.setSelection(true);
0094: fSelectedRangeRadioButton.setSelection(false);
0095: fUseSelectedLines = false;
0096:
0097: if (fTarget != null
0098: && (fTarget instanceof IFindReplaceTargetExtension))
0099: ((IFindReplaceTargetExtension) fTarget).setScope(null);
0100:
0101: fOldScope = null;
0102:
0103: fActiveShell = null;
0104: updateButtonState();
0105: }
0106: }
0107:
0108: /**
0109: * Modify listener to update the search result in case of incremental search.
0110: * @since 2.0
0111: */
0112: private class FindModifyListener implements ModifyListener {
0113:
0114: /*
0115: * @see ModifyListener#modifyText(ModifyEvent)
0116: */
0117: public void modifyText(ModifyEvent e) {
0118: if (isIncrementalSearch()
0119: && !isRegExSearchAvailableAndChecked()) {
0120: if (fFindField.getText().equals("") && fTarget != null) { //$NON-NLS-1$
0121: // empty selection at base location
0122: int offset = fIncrementalBaseLocation.x;
0123:
0124: if (isForwardSearch()
0125: && !fNeedsInitialFindBeforeReplace
0126: || !isForwardSearch()
0127: && fNeedsInitialFindBeforeReplace)
0128: offset = offset + fIncrementalBaseLocation.y;
0129:
0130: fNeedsInitialFindBeforeReplace = false;
0131: findAndSelect(
0132: offset,
0133: "", isForwardSearch(), isCaseSensitiveSearch(), isWholeWordSearch(), isRegExSearchAvailableAndChecked()); //$NON-NLS-1$
0134: } else {
0135: performSearch(false, false);
0136: }
0137: }
0138:
0139: updateButtonState(!isIncrementalSearch());
0140: }
0141: }
0142:
0143: /** The size of the dialogs search history. */
0144: private static final int HISTORY_SIZE = 5;
0145:
0146: private Point fIncrementalBaseLocation;
0147: private boolean fWrapInit, fCaseInit, fWholeWordInit, fForwardInit,
0148: fGlobalInit, fIncrementalInit;
0149: /**
0150: * Tells whether an initial find operation is needed
0151: * before the replace operation.
0152: * @since 3.0
0153: */
0154: private boolean fNeedsInitialFindBeforeReplace;
0155: /**
0156: * Initial value for telling whether the search string is a regular expression.
0157: * @since 3.0
0158: */
0159: boolean fIsRegExInit;
0160:
0161: private List fFindHistory;
0162: private List fReplaceHistory;
0163: private IRegion fOldScope;
0164:
0165: private boolean fIsTargetEditable;
0166: private IFindReplaceTarget fTarget;
0167: private Shell fParentShell;
0168: private Shell fActiveShell;
0169:
0170: private final ActivationListener fActivationListener = new ActivationListener();
0171: private final ModifyListener fFindModifyListener = new FindModifyListener();
0172:
0173: private Label fReplaceLabel, fStatusLabel;
0174: private Button fForwardRadioButton, fGlobalRadioButton,
0175: fSelectedRangeRadioButton;
0176: private Button fCaseCheckBox, fWrapCheckBox, fWholeWordCheckBox,
0177: fIncrementalCheckBox;
0178:
0179: /**
0180: * Checkbox for selecting whether the search string is a regular expression.
0181: * @since 3.0
0182: */
0183: private Button fIsRegExCheckBox;
0184:
0185: private Button fReplaceSelectionButton, fReplaceFindButton,
0186: fFindNextButton, fReplaceAllButton;
0187: private Combo fFindField, fReplaceField;
0188:
0189: /**
0190: * Find and replace command adapters.
0191: * @since 3.3
0192: */
0193: private ContentAssistCommandAdapter fContentAssistFindField,
0194: fContentAssistReplaceField;
0195:
0196: private Rectangle fDialogPositionInit;
0197:
0198: private IDialogSettings fDialogSettings;
0199: /**
0200: * Tells whether the target supports regular expressions.
0201: * <code>true</code> if the target supports regular expressions
0202: * @since 3.0
0203: */
0204: private boolean fIsTargetSupportingRegEx;
0205: /**
0206: * Tells whether fUseSelectedLines radio is checked.
0207: * @since 3.0
0208: */
0209: private boolean fUseSelectedLines;
0210: /**
0211: * <code>true</code> if the find field should receive focus the next time
0212: * the dialog is activated, <code>false</code> otherwise.
0213: * @since 3.0
0214: */
0215: private boolean fGiveFocusToFindField = true;
0216:
0217: /**
0218: * Creates a new dialog with the given shell as parent.
0219: * @param parentShell the parent shell
0220: */
0221: public FindReplaceDialog(Shell parentShell) {
0222: super (parentShell);
0223:
0224: fParentShell = null;
0225: fTarget = null;
0226:
0227: fDialogPositionInit = null;
0228: fFindHistory = new ArrayList(HISTORY_SIZE - 1);
0229: fReplaceHistory = new ArrayList(HISTORY_SIZE - 1);
0230:
0231: fWrapInit = false;
0232: fCaseInit = false;
0233: fIsRegExInit = false;
0234: fWholeWordInit = false;
0235: fIncrementalInit = false;
0236: fGlobalInit = true;
0237: fForwardInit = true;
0238:
0239: readConfiguration();
0240:
0241: setShellStyle(SWT.CLOSE | SWT.MODELESS | SWT.BORDER | SWT.TITLE
0242: | SWT.RESIZE);
0243: setBlockOnOpen(false);
0244: }
0245:
0246: /**
0247: * Returns this dialog's parent shell.
0248: * @return the dialog's parent shell
0249: */
0250: public Shell getParentShell() {
0251: return super .getParentShell();
0252: }
0253:
0254: /**
0255: * Returns <code>true</code> if control can be used.
0256: *
0257: * @param control the control to be checked
0258: * @return <code>true</code> if control can be used
0259: */
0260: private boolean okToUse(Control control) {
0261: return control != null && !control.isDisposed();
0262: }
0263:
0264: /*
0265: * @see org.eclipse.jface.window.Window#create()
0266: */
0267: public void create() {
0268:
0269: super .create();
0270:
0271: Shell shell = getShell();
0272: shell.addShellListener(fActivationListener);
0273:
0274: // set help context
0275: PlatformUI.getWorkbench().getHelpSystem().setHelp(shell,
0276: IAbstractTextEditorHelpContextIds.FIND_REPLACE_DIALOG);
0277:
0278: // fill in combo contents
0279: fFindField.removeModifyListener(fFindModifyListener);
0280: updateCombo(fFindField, fFindHistory);
0281: fFindField.addModifyListener(fFindModifyListener);
0282: updateCombo(fReplaceField, fReplaceHistory);
0283:
0284: // get find string
0285: initFindStringFromSelection();
0286:
0287: // set dialog position
0288: if (fDialogPositionInit != null)
0289: shell.setBounds(fDialogPositionInit);
0290:
0291: shell.setText(EditorMessages.FindReplace_title);
0292: // shell.setImage(null);
0293: }
0294:
0295: /**
0296: * Create the button section of the find/replace dialog.
0297: *
0298: * @param parent the parent composite
0299: * @return the button section
0300: */
0301: private Composite createButtonSection(Composite parent) {
0302:
0303: Composite panel = new Composite(parent, SWT.NONE);
0304: GridLayout layout = new GridLayout();
0305: layout.numColumns = -2; // this is intended
0306: panel.setLayout(layout);
0307:
0308: fFindNextButton = makeButton(panel,
0309: EditorMessages.FindReplace_FindNextButton_label, 102,
0310: true, new SelectionAdapter() {
0311: public void widgetSelected(SelectionEvent e) {
0312: if (isIncrementalSearch()
0313: && !isRegExSearchAvailableAndChecked())
0314: initIncrementalBaseLocation();
0315:
0316: fNeedsInitialFindBeforeReplace = false;
0317: performSearch();
0318: updateFindHistory();
0319: fFindNextButton.setFocus();
0320: }
0321: });
0322: setGridData(fFindNextButton, SWT.FILL, true, SWT.FILL, false);
0323:
0324: fReplaceFindButton = makeButton(panel,
0325: EditorMessages.FindReplace_ReplaceFindButton_label,
0326: 103, false, new SelectionAdapter() {
0327: public void widgetSelected(SelectionEvent e) {
0328: if (fNeedsInitialFindBeforeReplace)
0329: performSearch();
0330: if (performReplaceSelection())
0331: performSearch();
0332: updateFindAndReplaceHistory();
0333: fReplaceFindButton.setFocus();
0334: }
0335: });
0336: setGridData(fReplaceFindButton, SWT.FILL, false, SWT.FILL,
0337: false);
0338:
0339: fReplaceSelectionButton = makeButton(
0340: panel,
0341: EditorMessages.FindReplace_ReplaceSelectionButton_label,
0342: 104, false, new SelectionAdapter() {
0343: public void widgetSelected(SelectionEvent e) {
0344: if (fNeedsInitialFindBeforeReplace)
0345: performSearch();
0346: performReplaceSelection();
0347: updateFindAndReplaceHistory();
0348: fFindNextButton.setFocus();
0349: }
0350: });
0351: setGridData(fReplaceSelectionButton, SWT.FILL, false, SWT.FILL,
0352: false);
0353:
0354: fReplaceAllButton = makeButton(panel,
0355: EditorMessages.FindReplace_ReplaceAllButton_label, 105,
0356: false, new SelectionAdapter() {
0357: public void widgetSelected(SelectionEvent e) {
0358: performReplaceAll();
0359: updateFindAndReplaceHistory();
0360: fFindNextButton.setFocus();
0361: }
0362: });
0363: setGridData(fReplaceAllButton, SWT.FILL, true, SWT.FILL, false);
0364:
0365: // Make the all the buttons the same size as the Remove Selection button.
0366: fReplaceAllButton.setEnabled(isEditable());
0367:
0368: return panel;
0369: }
0370:
0371: /**
0372: * Creates the options configuration section of the find replace dialog.
0373: *
0374: * @param parent the parent composite
0375: * @return the options configuration section
0376: */
0377: private Composite createConfigPanel(Composite parent) {
0378:
0379: Composite panel = new Composite(parent, SWT.NONE);
0380: GridLayout layout = new GridLayout();
0381: layout.numColumns = 2;
0382: layout.makeColumnsEqualWidth = true;
0383: panel.setLayout(layout);
0384:
0385: Composite directionGroup = createDirectionGroup(panel);
0386: setGridData(directionGroup, SWT.FILL, true, SWT.FILL, false);
0387:
0388: Composite scopeGroup = createScopeGroup(panel);
0389: setGridData(scopeGroup, SWT.FILL, true, SWT.FILL, false);
0390:
0391: Composite optionsGroup = createOptionsGroup(panel);
0392: setGridData(optionsGroup, SWT.FILL, true, SWT.FILL, true);
0393: ((GridData) optionsGroup.getLayoutData()).horizontalSpan = 2;
0394:
0395: return panel;
0396: }
0397:
0398: /*
0399: * @see org.eclipse.jface.window.Window#createContents(org.eclipse.swt.widgets.Composite)
0400: */
0401: protected Control createContents(Composite parent) {
0402:
0403: Composite panel = new Composite(parent, SWT.NULL);
0404: GridLayout layout = new GridLayout();
0405: layout.numColumns = 1;
0406: layout.makeColumnsEqualWidth = true;
0407: panel.setLayout(layout);
0408: panel
0409: .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
0410: true));
0411:
0412: Composite inputPanel = createInputPanel(panel);
0413: setGridData(inputPanel, SWT.FILL, true, SWT.TOP, false);
0414:
0415: Composite configPanel = createConfigPanel(panel);
0416: setGridData(configPanel, SWT.FILL, true, SWT.TOP, true);
0417:
0418: Composite buttonPanelB = createButtonSection(panel);
0419: setGridData(buttonPanelB, SWT.RIGHT, true, SWT.BOTTOM, false);
0420:
0421: Composite statusBar = createStatusAndCloseButton(panel);
0422: setGridData(statusBar, SWT.FILL, true, SWT.BOTTOM, false);
0423:
0424: updateButtonState();
0425:
0426: applyDialogFont(panel);
0427:
0428: return panel;
0429: }
0430:
0431: private void setContentAssistsEnablement(boolean enable) {
0432: fContentAssistFindField.setEnabled(enable);
0433: fContentAssistReplaceField.setEnabled(enable);
0434: }
0435:
0436: /**
0437: * Creates the direction defining part of the options defining section
0438: * of the find replace dialog.
0439: *
0440: * @param parent the parent composite
0441: * @return the direction defining part
0442: */
0443: private Composite createDirectionGroup(Composite parent) {
0444:
0445: Composite panel = new Composite(parent, SWT.NONE);
0446: GridLayout layout = new GridLayout();
0447: layout.marginWidth = 0;
0448: layout.marginHeight = 0;
0449: panel.setLayout(layout);
0450:
0451: Group group = new Group(panel, SWT.SHADOW_ETCHED_IN);
0452: group.setText(EditorMessages.FindReplace_Direction);
0453: GridLayout groupLayout = new GridLayout();
0454: group.setLayout(groupLayout);
0455: group
0456: .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
0457: true));
0458:
0459: SelectionListener selectionListener = new SelectionListener() {
0460: public void widgetSelected(SelectionEvent e) {
0461: if (isIncrementalSearch()
0462: && !isRegExSearchAvailableAndChecked())
0463: initIncrementalBaseLocation();
0464: }
0465:
0466: public void widgetDefaultSelected(SelectionEvent e) {
0467: }
0468: };
0469:
0470: fForwardRadioButton = new Button(group, SWT.RADIO | SWT.LEFT);
0471: fForwardRadioButton
0472: .setText(EditorMessages.FindReplace_ForwardRadioButton_label);
0473: setGridData(fForwardRadioButton, SWT.LEFT, false, SWT.CENTER,
0474: false);
0475: fForwardRadioButton.addSelectionListener(selectionListener);
0476:
0477: Button backwardRadioButton = new Button(group, SWT.RADIO
0478: | SWT.LEFT);
0479: backwardRadioButton
0480: .setText(EditorMessages.FindReplace_BackwardRadioButton_label);
0481: setGridData(backwardRadioButton, SWT.LEFT, false, SWT.CENTER,
0482: false);
0483: backwardRadioButton.addSelectionListener(selectionListener);
0484:
0485: backwardRadioButton.setSelection(!fForwardInit);
0486: fForwardRadioButton.setSelection(fForwardInit);
0487:
0488: return panel;
0489: }
0490:
0491: /**
0492: * Creates the scope defining part of the find replace dialog.
0493: *
0494: * @param parent the parent composite
0495: * @return the scope defining part
0496: * @since 2.0
0497: */
0498: private Composite createScopeGroup(Composite parent) {
0499:
0500: Composite panel = new Composite(parent, SWT.NONE);
0501: GridLayout layout = new GridLayout();
0502: layout.marginWidth = 0;
0503: layout.marginHeight = 0;
0504: panel.setLayout(layout);
0505:
0506: Group group = new Group(panel, SWT.SHADOW_ETCHED_IN);
0507: group.setText(EditorMessages.FindReplace_Scope);
0508: GridLayout groupLayout = new GridLayout();
0509: group.setLayout(groupLayout);
0510: group
0511: .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
0512: true));
0513:
0514: fGlobalRadioButton = new Button(group, SWT.RADIO | SWT.LEFT);
0515: fGlobalRadioButton
0516: .setText(EditorMessages.FindReplace_GlobalRadioButton_label);
0517: setGridData(fGlobalRadioButton, SWT.LEFT, false, SWT.CENTER,
0518: false);
0519: fGlobalRadioButton.setSelection(fGlobalInit);
0520: fGlobalRadioButton
0521: .addSelectionListener(new SelectionListener() {
0522: public void widgetSelected(SelectionEvent e) {
0523: if (!fGlobalRadioButton.getSelection()
0524: || !fUseSelectedLines)
0525: return;
0526: fUseSelectedLines = false;
0527: useSelectedLines(false);
0528: }
0529:
0530: public void widgetDefaultSelected(SelectionEvent e) {
0531: }
0532: });
0533:
0534: fSelectedRangeRadioButton = new Button(group, SWT.RADIO
0535: | SWT.LEFT);
0536: fSelectedRangeRadioButton
0537: .setText(EditorMessages.FindReplace_SelectedRangeRadioButton_label);
0538: setGridData(fSelectedRangeRadioButton, SWT.LEFT, false,
0539: SWT.CENTER, false);
0540: fSelectedRangeRadioButton.setSelection(!fGlobalInit);
0541: fUseSelectedLines = !fGlobalInit;
0542: fSelectedRangeRadioButton
0543: .addSelectionListener(new SelectionListener() {
0544: public void widgetSelected(SelectionEvent e) {
0545: if (!fSelectedRangeRadioButton.getSelection()
0546: || fUseSelectedLines)
0547: return;
0548: fUseSelectedLines = true;
0549: useSelectedLines(true);
0550: }
0551:
0552: public void widgetDefaultSelected(SelectionEvent e) {
0553: }
0554: });
0555:
0556: return panel;
0557: }
0558:
0559: /**
0560: * Tells the dialog to perform searches only in the scope given by the actually selected lines.
0561: * @param selectedLines <code>true</code> if selected lines should be used
0562: * @since 2.0
0563: */
0564: private void useSelectedLines(boolean selectedLines) {
0565: if (isIncrementalSearch()
0566: && !isRegExSearchAvailableAndChecked())
0567: initIncrementalBaseLocation();
0568:
0569: if (fTarget == null
0570: || !(fTarget instanceof IFindReplaceTargetExtension))
0571: return;
0572:
0573: IFindReplaceTargetExtension extensionTarget = (IFindReplaceTargetExtension) fTarget;
0574:
0575: if (selectedLines) {
0576:
0577: IRegion scope;
0578: if (fOldScope == null) {
0579: Point lineSelection = extensionTarget
0580: .getLineSelection();
0581: scope = new Region(lineSelection.x, lineSelection.y);
0582: } else {
0583: scope = fOldScope;
0584: fOldScope = null;
0585: }
0586:
0587: int offset = isForwardSearch() ? scope.getOffset() : scope
0588: .getOffset()
0589: + scope.getLength();
0590:
0591: extensionTarget.setSelection(offset, 0);
0592: extensionTarget.setScope(scope);
0593: } else {
0594: fOldScope = extensionTarget.getScope();
0595: extensionTarget.setScope(null);
0596: }
0597: }
0598:
0599: /**
0600: * Creates the panel where the user specifies the text to search
0601: * for and the optional replacement text.
0602: *
0603: * @param parent the parent composite
0604: * @return the input panel
0605: */
0606: private Composite createInputPanel(Composite parent) {
0607:
0608: ModifyListener listener = new ModifyListener() {
0609: public void modifyText(ModifyEvent e) {
0610: updateButtonState();
0611: }
0612: };
0613:
0614: Composite panel = new Composite(parent, SWT.NULL);
0615: GridLayout layout = new GridLayout();
0616: layout.numColumns = 2;
0617: panel.setLayout(layout);
0618:
0619: Label findLabel = new Label(panel, SWT.LEFT);
0620: findLabel.setText(EditorMessages.FindReplace_Find_label);
0621: setGridData(findLabel, SWT.LEFT, false, SWT.CENTER, false);
0622:
0623: // Create the find content assist field
0624: ComboContentAdapter contentAdapter = new ComboContentAdapter();
0625: RegExContentProposalProvider findProposer = new RegExContentProposalProvider(
0626: true);
0627: fFindField = new Combo(panel, SWT.DROP_DOWN | SWT.BORDER);
0628: fContentAssistFindField = new ContentAssistCommandAdapter(
0629: fFindField,
0630: contentAdapter,
0631: findProposer,
0632: ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS,
0633: new char[] { '\\', '[', '(' }, true);
0634: setGridData(fFindField, SWT.FILL, true, SWT.CENTER, false);
0635: addDecorationMargin(fFindField);
0636: fFindField.addModifyListener(fFindModifyListener);
0637:
0638: fReplaceLabel = new Label(panel, SWT.LEFT);
0639: fReplaceLabel.setText(EditorMessages.FindReplace_Replace_label);
0640: setGridData(fReplaceLabel, SWT.LEFT, false, SWT.CENTER, false);
0641:
0642: // Create the replace content assist field
0643: RegExContentProposalProvider replaceProposer = new RegExContentProposalProvider(
0644: false);
0645: fReplaceField = new Combo(panel, SWT.DROP_DOWN | SWT.BORDER);
0646: fContentAssistReplaceField = new ContentAssistCommandAdapter(
0647: fReplaceField,
0648: contentAdapter,
0649: replaceProposer,
0650: ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS,
0651: new char[] { '$', '\\' }, true);
0652: setGridData(fReplaceField, SWT.FILL, true, SWT.CENTER, false);
0653: addDecorationMargin(fReplaceField);
0654: fReplaceField.addModifyListener(listener);
0655:
0656: return panel;
0657: }
0658:
0659: /**
0660: * Creates the functional options part of the options defining
0661: * section of the find replace dialog.
0662: *
0663: * @param parent the parent composite
0664: * @return the options group
0665: */
0666: private Composite createOptionsGroup(Composite parent) {
0667:
0668: Composite panel = new Composite(parent, SWT.NONE);
0669: GridLayout layout = new GridLayout();
0670: layout.marginWidth = 0;
0671: layout.marginHeight = 0;
0672: panel.setLayout(layout);
0673:
0674: Group group = new Group(panel, SWT.SHADOW_NONE);
0675: group.setText(EditorMessages.FindReplace_Options);
0676: GridLayout groupLayout = new GridLayout();
0677: groupLayout.numColumns = 2;
0678: groupLayout.makeColumnsEqualWidth = true;
0679: group.setLayout(groupLayout);
0680: group
0681: .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
0682: true));
0683:
0684: SelectionListener selectionListener = new SelectionListener() {
0685: public void widgetSelected(SelectionEvent e) {
0686: storeSettings();
0687: }
0688:
0689: public void widgetDefaultSelected(SelectionEvent e) {
0690: }
0691: };
0692:
0693: fCaseCheckBox = new Button(group, SWT.CHECK | SWT.LEFT);
0694: fCaseCheckBox
0695: .setText(EditorMessages.FindReplace_CaseCheckBox_label);
0696: setGridData(fCaseCheckBox, SWT.LEFT, false, SWT.CENTER, false);
0697: fCaseCheckBox.setSelection(fCaseInit);
0698: fCaseCheckBox.addSelectionListener(selectionListener);
0699:
0700: fWrapCheckBox = new Button(group, SWT.CHECK | SWT.LEFT);
0701: fWrapCheckBox
0702: .setText(EditorMessages.FindReplace_WrapCheckBox_label);
0703: setGridData(fWrapCheckBox, SWT.LEFT, false, SWT.CENTER, false);
0704: fWrapCheckBox.setSelection(fWrapInit);
0705: fWrapCheckBox.addSelectionListener(selectionListener);
0706:
0707: fWholeWordCheckBox = new Button(group, SWT.CHECK | SWT.LEFT);
0708: fWholeWordCheckBox
0709: .setText(EditorMessages.FindReplace_WholeWordCheckBox_label);
0710: setGridData(fWholeWordCheckBox, SWT.LEFT, false, SWT.CENTER,
0711: false);
0712: fWholeWordCheckBox.setSelection(fWholeWordInit);
0713: fWholeWordCheckBox.addSelectionListener(selectionListener);
0714:
0715: fIncrementalCheckBox = new Button(group, SWT.CHECK | SWT.LEFT);
0716: fIncrementalCheckBox
0717: .setText(EditorMessages.FindReplace_IncrementalCheckBox_label);
0718: setGridData(fIncrementalCheckBox, SWT.LEFT, false, SWT.CENTER,
0719: false);
0720: fIncrementalCheckBox.setSelection(fIncrementalInit);
0721: fIncrementalCheckBox
0722: .addSelectionListener(new SelectionListener() {
0723: public void widgetSelected(SelectionEvent e) {
0724: if (isIncrementalSearch() && !isRegExSearch())
0725: initIncrementalBaseLocation();
0726:
0727: storeSettings();
0728: }
0729:
0730: public void widgetDefaultSelected(SelectionEvent e) {
0731: }
0732: });
0733:
0734: fIsRegExCheckBox = new Button(group, SWT.CHECK | SWT.LEFT);
0735: fIsRegExCheckBox
0736: .setText(EditorMessages.FindReplace_RegExCheckbox_label);
0737: setGridData(fIsRegExCheckBox, SWT.LEFT, false, SWT.CENTER,
0738: false);
0739: ((GridData) fIsRegExCheckBox.getLayoutData()).horizontalSpan = 2;
0740: fIsRegExCheckBox.setSelection(fIsRegExInit);
0741: fIsRegExCheckBox.addSelectionListener(new SelectionAdapter() {
0742: /*
0743: * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
0744: */
0745: public void widgetSelected(SelectionEvent e) {
0746: boolean newState = fIsRegExCheckBox.getSelection();
0747: fIncrementalCheckBox.setEnabled(!newState);
0748: updateButtonState();
0749: storeSettings();
0750: setContentAssistsEnablement(newState);
0751: }
0752: });
0753: fWholeWordCheckBox
0754: .setEnabled(!isRegExSearchAvailableAndChecked());
0755: fWholeWordCheckBox.addSelectionListener(new SelectionAdapter() {
0756: /*
0757: * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
0758: */
0759: public void widgetSelected(SelectionEvent e) {
0760: updateButtonState();
0761: }
0762: });
0763: fIncrementalCheckBox
0764: .setEnabled(!isRegExSearchAvailableAndChecked());
0765: return panel;
0766: }
0767:
0768: /**
0769: * Creates the status and close section of the dialog.
0770: *
0771: * @param parent the parent composite
0772: * @return the status and close button
0773: */
0774: private Composite createStatusAndCloseButton(Composite parent) {
0775:
0776: Composite panel = new Composite(parent, SWT.NULL);
0777: GridLayout layout = new GridLayout();
0778: layout.numColumns = 2;
0779: layout.marginWidth = 0;
0780: layout.marginHeight = 0;
0781: panel.setLayout(layout);
0782:
0783: fStatusLabel = new Label(panel, SWT.LEFT);
0784: setGridData(fStatusLabel, SWT.FILL, true, SWT.CENTER, false);
0785:
0786: String label = EditorMessages.FindReplace_CloseButton_label;
0787: Button closeButton = createButton(panel, 101, label, false);
0788: setGridData(closeButton, SWT.RIGHT, false, SWT.BOTTOM, false);
0789:
0790: return panel;
0791: }
0792:
0793: /*
0794: * @see Dialog#buttonPressed
0795: */
0796: protected void buttonPressed(int buttonID) {
0797: if (buttonID == 101)
0798: close();
0799: }
0800:
0801: // ------- action invocation ---------------------------------------
0802:
0803: /**
0804: * Returns the position of the specified search string, or <code>-1</code> if the string can
0805: * not be found when searching using the given options.
0806: *
0807: * @param findString the string to search for
0808: * @param startPosition the position at which to start the search
0809: * @param forwardSearch the direction of the search
0810: * @param caseSensitive should the search be case sensitive
0811: * @param wrapSearch should the search wrap to the start/end if arrived at the end/start
0812: * @param wholeWord does the search string represent a complete word
0813: * @param regExSearch if <code>true</code> findString represents a regular expression
0814: * @param beepOnWrap if <code>true</code> beeps when search needs to wrap
0815: * @return the occurrence of the find string following the options or <code>-1</code> if nothing found
0816: * @since 3.0
0817: */
0818: private int findIndex(String findString, int startPosition,
0819: boolean forwardSearch, boolean caseSensitive,
0820: boolean wrapSearch, boolean wholeWord, boolean regExSearch,
0821: boolean beepOnWrap) {
0822:
0823: if (forwardSearch) {
0824: int index = findAndSelect(startPosition, findString, true,
0825: caseSensitive, wholeWord, regExSearch);
0826: if (index == -1) {
0827: if (beepOnWrap && okToUse(getShell()))
0828: getShell().getDisplay().beep();
0829:
0830: if (wrapSearch)
0831: index = findAndSelect(-1, findString, true,
0832: caseSensitive, wholeWord, regExSearch);
0833:
0834: }
0835: return index;
0836: }
0837:
0838: // backward
0839: int index = startPosition == 0 ? -1 : findAndSelect(
0840: startPosition - 1, findString, false, caseSensitive,
0841: wholeWord, regExSearch);
0842: if (index == -1) {
0843: if (beepOnWrap && okToUse(getShell()))
0844: getShell().getDisplay().beep();
0845:
0846: if (wrapSearch)
0847: index = findAndSelect(-1, findString, false,
0848: caseSensitive, wholeWord, regExSearch);
0849: }
0850: return index;
0851: }
0852:
0853: /**
0854: * Searches for a string starting at the given offset and using the specified search
0855: * directives. If a string has been found it is selected and its start offset is
0856: * returned.
0857: *
0858: * @param offset the offset at which searching starts
0859: * @param findString the string which should be found
0860: * @param forwardSearch the direction of the search
0861: * @param caseSensitive <code>true</code> performs a case sensitive search, <code>false</code> an insensitive search
0862: * @param wholeWord if <code>true</code> only occurrences are reported in which the findString stands as a word by itself
0863: * @param regExSearch if <code>true</code> findString represents a regular expression
0864: * @return the position of the specified string, or -1 if the string has not been found
0865: * @since 3.0
0866: */
0867: private int findAndSelect(int offset, String findString,
0868: boolean forwardSearch, boolean caseSensitive,
0869: boolean wholeWord, boolean regExSearch) {
0870: if (fTarget instanceof IFindReplaceTargetExtension3)
0871: return ((IFindReplaceTargetExtension3) fTarget)
0872: .findAndSelect(offset, findString, forwardSearch,
0873: caseSensitive, wholeWord, regExSearch);
0874: return fTarget.findAndSelect(offset, findString, forwardSearch,
0875: caseSensitive, wholeWord);
0876: }
0877:
0878: /**
0879: * Replaces the selection with <code>replaceString</code>. If
0880: * <code>regExReplace</code> is <code>true</code>,
0881: * <code>replaceString</code> is a regex replace pattern which will get
0882: * expanded if the underlying target supports it. Returns the region of the
0883: * inserted text; note that the returned selection covers the expanded
0884: * pattern in case of regex replace.
0885: *
0886: * @param replaceString the replace string (or a regex pattern)
0887: * @param regExReplace <code>true</code> if <code>replaceString</code>
0888: * is a pattern
0889: * @return the selection after replacing, i.e. the inserted text
0890: * @since 3.0
0891: */
0892: Point replaceSelection(String replaceString, boolean regExReplace) {
0893: if (fTarget instanceof IFindReplaceTargetExtension3)
0894: ((IFindReplaceTargetExtension3) fTarget).replaceSelection(
0895: replaceString, regExReplace);
0896: else
0897: fTarget.replaceSelection(replaceString);
0898:
0899: return fTarget.getSelection();
0900: }
0901:
0902: /**
0903: * Returns whether the specified search string can be found using the given options.
0904: *
0905: * @param findString the string to search for
0906: * @param forwardSearch the direction of the search
0907: * @param caseSensitive should the search be case sensitive
0908: * @param wrapSearch should the search wrap to the start/end if arrived at the end/start
0909: * @param wholeWord does the search string represent a complete word
0910: * @param incremental is this an incremental search
0911: * @param regExSearch if <code>true</code> findString represents a regular expression
0912: * @param beepOnWrap if <code>true</code> beeps when search needs to wrap
0913: * @return <code>true</code> if the search string can be found using the given options
0914: *
0915: * @since 3.0
0916: */
0917: private boolean findNext(String findString, boolean forwardSearch,
0918: boolean caseSensitive, boolean wrapSearch,
0919: boolean wholeWord, boolean incremental,
0920: boolean regExSearch, boolean beepOnWrap) {
0921:
0922: if (fTarget == null)
0923: return false;
0924:
0925: Point r = null;
0926: if (incremental)
0927: r = fIncrementalBaseLocation;
0928: else
0929: r = fTarget.getSelection();
0930:
0931: int findReplacePosition = r.x;
0932: if (forwardSearch && !fNeedsInitialFindBeforeReplace
0933: || !forwardSearch && fNeedsInitialFindBeforeReplace)
0934: findReplacePosition += r.y;
0935:
0936: fNeedsInitialFindBeforeReplace = false;
0937:
0938: int index = findIndex(findString, findReplacePosition,
0939: forwardSearch, caseSensitive, wrapSearch, wholeWord,
0940: regExSearch, beepOnWrap);
0941:
0942: if (index != -1)
0943: return true;
0944:
0945: return false;
0946: }
0947:
0948: /**
0949: * Returns the dialog's boundaries.
0950: * @return the dialog's boundaries
0951: */
0952: private Rectangle getDialogBoundaries() {
0953: if (okToUse(getShell()))
0954: return getShell().getBounds();
0955: return fDialogPositionInit;
0956: }
0957:
0958: /**
0959: * Returns the dialog's history.
0960: * @return the dialog's history
0961: */
0962: private List getFindHistory() {
0963: return fFindHistory;
0964: }
0965:
0966: // ------- accessors ---------------------------------------
0967:
0968: /**
0969: * Retrieves the string to search for from the appropriate text input field and returns it.
0970: * @return the search string
0971: */
0972: private String getFindString() {
0973: if (okToUse(fFindField)) {
0974: return fFindField.getText();
0975: }
0976: return ""; //$NON-NLS-1$
0977: }
0978:
0979: /**
0980: * Returns the dialog's replace history.
0981: * @return the dialog's replace history
0982: */
0983: private List getReplaceHistory() {
0984: return fReplaceHistory;
0985: }
0986:
0987: /**
0988: * Retrieves the replacement string from the appropriate text input field and returns it.
0989: * @return the replacement string
0990: */
0991: private String getReplaceString() {
0992: if (okToUse(fReplaceField)) {
0993: return fReplaceField.getText();
0994: }
0995: return ""; //$NON-NLS-1$
0996: }
0997:
0998: // ------- init / close ---------------------------------------
0999:
1000: /**
1001: * Returns the first line of the given selection.
1002: *
1003: * @param selection the selection
1004: * @return the first line of the selection
1005: */
1006: private String getFirstLine(String selection) {
1007: if (selection.length() > 0) {
1008: int[] info = TextUtilities.indexOf(
1009: TextUtilities.DELIMITERS, selection, 0);
1010: if (info[0] > 0)
1011: return selection.substring(0, info[0]);
1012: else if (info[0] == -1)
1013: return selection;
1014: }
1015: return ""; //$NON-NLS-1$
1016: }
1017:
1018: /**
1019: * @see org.eclipse.jface.window.Window#close()
1020: */
1021: public boolean close() {
1022: handleDialogClose();
1023: return super .close();
1024: }
1025:
1026: /**
1027: * Removes focus changed listener from browser and stores settings for re-open.
1028: */
1029: private void handleDialogClose() {
1030:
1031: // remove listeners
1032: if (okToUse(fFindField)) {
1033: fFindField.removeModifyListener(fFindModifyListener);
1034: }
1035:
1036: if (fParentShell != null) {
1037: fParentShell.removeShellListener(fActivationListener);
1038: fParentShell = null;
1039: }
1040:
1041: getShell().removeShellListener(fActivationListener);
1042:
1043: // store current settings in case of re-open
1044: storeSettings();
1045:
1046: if (fTarget != null
1047: && fTarget instanceof IFindReplaceTargetExtension)
1048: ((IFindReplaceTargetExtension) fTarget).endSession();
1049:
1050: // prevent leaks
1051: fActiveShell = null;
1052: fTarget = null;
1053:
1054: }
1055:
1056: /**
1057: * Writes the current selection to the dialog settings.
1058: * @since 3.0
1059: */
1060: private void writeSelection() {
1061: if (fTarget == null)
1062: return;
1063:
1064: IDialogSettings s = getDialogSettings();
1065: s.put("selection", fTarget.getSelectionText()); //$NON-NLS-1$
1066: }
1067:
1068: /**
1069: * Stores the current state in the dialog settings.
1070: * @since 2.0
1071: */
1072: private void storeSettings() {
1073: fDialogPositionInit = getDialogBoundaries();
1074: fWrapInit = isWrapSearch();
1075: fWholeWordInit = isWholeWordSetting();
1076: fCaseInit = isCaseSensitiveSearch();
1077: fIsRegExInit = isRegExSearch();
1078: fIncrementalInit = isIncrementalSearch();
1079: fForwardInit = isForwardSearch();
1080:
1081: writeConfiguration();
1082: }
1083:
1084: /**
1085: * Initializes the string to search for and the appropriate
1086: * text in the Find field based on the selection found in the
1087: * action's target.
1088: */
1089: private void initFindStringFromSelection() {
1090: if (fTarget != null && okToUse(fFindField)) {
1091: String fullSelection = fTarget.getSelectionText();
1092: boolean isRegEx = isRegExSearchAvailableAndChecked();
1093: fFindField.removeModifyListener(fFindModifyListener);
1094: if (fullSelection.length() > 0) {
1095: String firstLine = getFirstLine(fullSelection);
1096: String pattern = isRegEx ? escapeForRegExPattern(fullSelection)
1097: : firstLine;
1098: fFindField.setText(pattern);
1099: if (!firstLine.equals(fullSelection)) {
1100: // multiple lines selected
1101: useSelectedLines(true);
1102: fGlobalRadioButton.setSelection(false);
1103: fSelectedRangeRadioButton.setSelection(true);
1104: fUseSelectedLines = true;
1105: }
1106: } else {
1107: if ("".equals(fFindField.getText())) { //$NON-NLS-1$
1108: if (fFindHistory.size() > 0)
1109: fFindField
1110: .setText((String) fFindHistory.get(0));
1111: else
1112: fFindField.setText(""); //$NON-NLS-1$
1113: }
1114: }
1115: fFindField.setSelection(new Point(0, fFindField.getText()
1116: .length()));
1117: fFindField.addModifyListener(fFindModifyListener);
1118: }
1119: }
1120:
1121: /**
1122: * Escapes special characters in the string, such that the resulting pattern
1123: * matches the given string.
1124: *
1125: * @param string the string to escape
1126: * @return a regex pattern that matches the given string
1127: */
1128: public static String escapeForRegExPattern(String string) {
1129: //implements https://bugs.eclipse.org/bugs/show_bug.cgi?id=44422
1130:
1131: StringBuffer pattern = new StringBuffer(string.length() + 16);
1132: int length = string.length();
1133: if (length > 0 && string.charAt(0) == '^')
1134: pattern.append('\\');
1135: for (int i = 0; i < length; i++) {
1136: char ch = string.charAt(i);
1137: switch (ch) {
1138: case '\\':
1139: case '(':
1140: case ')':
1141: case '[':
1142: case ']':
1143: case '{':
1144: case '}':
1145: case '.':
1146: case '?':
1147: case '*':
1148: case '+':
1149: case '|':
1150: pattern.append('\\').append(ch);
1151: break;
1152:
1153: case '\n':
1154: pattern.append("\\n"); //$NON-NLS-1$
1155: break;
1156: case '\r':
1157: pattern.append("\\r"); //$NON-NLS-1$
1158: break;
1159: case '\t':
1160: pattern.append("\\t"); //$NON-NLS-1$
1161: break;
1162: case '\f':
1163: pattern.append("\\f"); //$NON-NLS-1$
1164: break;
1165: case 0x07:
1166: pattern.append("\\a"); //$NON-NLS-1$
1167: break;
1168: case 0x1B:
1169: pattern.append("\\e"); //$NON-NLS-1$
1170: break;
1171:
1172: default:
1173: if (0 <= ch && ch < 0x20) {
1174: pattern.append("\\x"); //$NON-NLS-1$
1175: pattern.append(Integer.toHexString(ch)
1176: .toUpperCase());
1177: } else {
1178: pattern.append(ch);
1179: }
1180: }
1181: }
1182: if (length > 0 && string.charAt(length - 1) == '$')
1183: pattern.insert(pattern.length() - 1, '\\');
1184: return pattern.toString();
1185: }
1186:
1187: /**
1188: * Initializes the anchor used as starting point for incremental searching.
1189: * @since 2.0
1190: */
1191: private void initIncrementalBaseLocation() {
1192: if (fTarget != null && isIncrementalSearch()
1193: && !isRegExSearchAvailableAndChecked()) {
1194: fIncrementalBaseLocation = fTarget.getSelection();
1195: } else {
1196: fIncrementalBaseLocation = new Point(0, 0);
1197: }
1198: }
1199:
1200: // ------- history ---------------------------------------
1201:
1202: /**
1203: * Retrieves and returns the option case sensitivity from the appropriate check box.
1204: * @return <code>true</code> if case sensitive
1205: */
1206: private boolean isCaseSensitiveSearch() {
1207: if (okToUse(fCaseCheckBox)) {
1208: return fCaseCheckBox.getSelection();
1209: }
1210: return fCaseInit;
1211: }
1212:
1213: /**
1214: * Retrieves and returns the regEx option from the appropriate check box.
1215: *
1216: * @return <code>true</code> if case sensitive
1217: * @since 3.0
1218: */
1219: private boolean isRegExSearch() {
1220: if (okToUse(fIsRegExCheckBox)) {
1221: return fIsRegExCheckBox.getSelection();
1222: }
1223: return fIsRegExInit;
1224: }
1225:
1226: /**
1227: * If the target supports regular expressions search retrieves and returns
1228: * regEx option from appropriate check box.
1229: *
1230: * @return <code>true</code> if regEx is available and checked
1231: * @since 3.0
1232: */
1233: private boolean isRegExSearchAvailableAndChecked() {
1234: if (okToUse(fIsRegExCheckBox)) {
1235: return fIsTargetSupportingRegEx
1236: && fIsRegExCheckBox.getSelection();
1237: }
1238: return fIsRegExInit;
1239: }
1240:
1241: /**
1242: * Retrieves and returns the option search direction from the appropriate check box.
1243: * @return <code>true</code> if searching forward
1244: */
1245: private boolean isForwardSearch() {
1246: if (okToUse(fForwardRadioButton)) {
1247: return fForwardRadioButton.getSelection();
1248: }
1249: return fForwardInit;
1250: }
1251:
1252: /**
1253: * Retrieves and returns the option search whole words from the appropriate check box.
1254: * @return <code>true</code> if searching for whole words
1255: */
1256: private boolean isWholeWordSetting() {
1257: if (okToUse(fWholeWordCheckBox)) {
1258: return fWholeWordCheckBox.getSelection();
1259: }
1260: return fWholeWordInit;
1261: }
1262:
1263: /**
1264: * Returns <code>true</code> if searching should be restricted to entire
1265: * words, <code>false</code> if not. This is the case if the respective
1266: * checkbox is turned on, regex is off, and the checkbox is enabled, i.e.
1267: * the current find string is an entire word.
1268: *
1269: * @return <code>true</code> if the search is restricted to whole words
1270: */
1271: private boolean isWholeWordSearch() {
1272: return isWholeWordSetting()
1273: && !isRegExSearchAvailableAndChecked()
1274: && (okToUse(fWholeWordCheckBox) ? fWholeWordCheckBox
1275: .isEnabled() : true);
1276: }
1277:
1278: /**
1279: * Retrieves and returns the option wrap search from the appropriate check box.
1280: * @return <code>true</code> if wrapping while searching
1281: */
1282: private boolean isWrapSearch() {
1283: if (okToUse(fWrapCheckBox)) {
1284: return fWrapCheckBox.getSelection();
1285: }
1286: return fWrapInit;
1287: }
1288:
1289: /**
1290: * Retrieves and returns the option incremental search from the appropriate check box.
1291: * @return <code>true</code> if incremental search
1292: * @since 2.0
1293: */
1294: private boolean isIncrementalSearch() {
1295: if (okToUse(fIncrementalCheckBox)) {
1296: return fIncrementalCheckBox.getSelection();
1297: }
1298: return fIncrementalInit;
1299: }
1300:
1301: /**
1302: * Creates a button.
1303: * @param parent the parent control
1304: * @param label the button label
1305: * @param id the button id
1306: * @param dfltButton is this button the default button
1307: * @param listener a button pressed listener
1308: * @return the new button
1309: */
1310: private Button makeButton(Composite parent, String label, int id,
1311: boolean dfltButton, SelectionListener listener) {
1312: Button b = createButton(parent, id, label, dfltButton);
1313: b.addSelectionListener(listener);
1314: return b;
1315: }
1316:
1317: /**
1318: * Returns the status line manager of the active editor or <code>null</code> if there is no such editor.
1319: * @return the status line manager of the active editor
1320: */
1321: private IEditorStatusLine getStatusLineManager() {
1322: IWorkbenchWindow window = PlatformUI.getWorkbench()
1323: .getActiveWorkbenchWindow();
1324: if (window == null)
1325: return null;
1326:
1327: IWorkbenchPage page = window.getActivePage();
1328: if (page == null)
1329: return null;
1330:
1331: IEditorPart editor = page.getActiveEditor();
1332: if (editor == null)
1333: return null;
1334:
1335: return (IEditorStatusLine) editor
1336: .getAdapter(IEditorStatusLine.class);
1337: }
1338:
1339: /**
1340: * Sets the given status message in the status line.
1341: *
1342: * @param error <code>true</code> if it is an error
1343: * @param message the error message
1344: */
1345: private void statusMessage(boolean error, String message) {
1346: fStatusLabel.setText(message);
1347:
1348: if (error)
1349: fStatusLabel.setForeground(JFaceColors
1350: .getErrorText(fStatusLabel.getDisplay()));
1351: else
1352: fStatusLabel.setForeground(null);
1353:
1354: IEditorStatusLine statusLine = getStatusLineManager();
1355: if (statusLine != null)
1356: statusLine.setMessage(error, message, null);
1357:
1358: if (error)
1359: getShell().getDisplay().beep();
1360: }
1361:
1362: /**
1363: * Sets the given error message in the status line.
1364: * @param message the message
1365: */
1366: private void statusError(String message) {
1367: statusMessage(true, message);
1368: }
1369:
1370: /**
1371: * Sets the given message in the status line.
1372: * @param message the message
1373: */
1374: private void statusMessage(String message) {
1375: statusMessage(false, message);
1376: }
1377:
1378: /**
1379: * Replaces all occurrences of the user's findString with
1380: * the replace string. Indicate to the user the number of replacements
1381: * that occur.
1382: */
1383: private void performReplaceAll() {
1384:
1385: int replaceCount = 0;
1386: final String replaceString = getReplaceString();
1387: final String findString = getFindString();
1388:
1389: if (findString != null && findString.length() > 0) {
1390:
1391: class ReplaceAllRunnable implements Runnable {
1392: public int numberOfOccurrences;
1393:
1394: public void run() {
1395: numberOfOccurrences = replaceAll(
1396: findString,
1397: replaceString == null ? "" : replaceString, isForwardSearch(), isCaseSensitiveSearch(), isWrapSearch(), isWholeWordSearch(), isRegExSearchAvailableAndChecked()); //$NON-NLS-1$
1398: }
1399: }
1400:
1401: try {
1402: ReplaceAllRunnable runnable = new ReplaceAllRunnable();
1403: BusyIndicator.showWhile(fActiveShell.getDisplay(),
1404: runnable);
1405: replaceCount = runnable.numberOfOccurrences;
1406:
1407: if (replaceCount != 0) {
1408: if (replaceCount == 1) { // not plural
1409: statusMessage(EditorMessages.FindReplace_Status_replacement_label);
1410: } else {
1411: String msg = EditorMessages.FindReplace_Status_replacements_label;
1412: msg = NLSUtility.format(msg, String
1413: .valueOf(replaceCount));
1414: statusMessage(msg);
1415: }
1416: } else {
1417: statusMessage(EditorMessages.FindReplace_Status_noMatch_label);
1418: }
1419: } catch (PatternSyntaxException ex) {
1420: statusError(ex.getLocalizedMessage());
1421: } catch (IllegalStateException ex) {
1422: // we don't keep state in this dialog
1423: }
1424: }
1425: writeSelection();
1426: updateButtonState();
1427: }
1428:
1429: /**
1430: * Validates the state of the find/replace target.
1431: * @return <code>true</code> if target can be changed, <code>false</code> otherwise
1432: * @since 2.1
1433: */
1434: private boolean validateTargetState() {
1435:
1436: if (fTarget instanceof IFindReplaceTargetExtension2) {
1437: IFindReplaceTargetExtension2 extension = (IFindReplaceTargetExtension2) fTarget;
1438: if (!extension.validateTargetState()) {
1439: statusError(EditorMessages.FindReplaceDialog_read_only);
1440: updateButtonState();
1441: return false;
1442: }
1443: }
1444: return isEditable();
1445: }
1446:
1447: /**
1448: * Replaces the current selection of the target with the user's
1449: * replace string.
1450: *
1451: * @return <code>true</code> if the operation was successful
1452: */
1453: private boolean performReplaceSelection() {
1454:
1455: if (!validateTargetState())
1456: return false;
1457:
1458: String replaceString = getReplaceString();
1459: if (replaceString == null)
1460: replaceString = ""; //$NON-NLS-1$
1461:
1462: boolean replaced;
1463: try {
1464: replaceSelection(replaceString,
1465: isRegExSearchAvailableAndChecked());
1466: replaced = true;
1467: writeSelection();
1468: } catch (PatternSyntaxException ex) {
1469: statusError(ex.getLocalizedMessage());
1470: replaced = false;
1471: } catch (IllegalStateException ex) {
1472: replaced = false;
1473: }
1474:
1475: updateButtonState();
1476: return replaced;
1477: }
1478:
1479: /**
1480: * Locates the user's findString in the text of the target.
1481: */
1482: private void performSearch() {
1483: performSearch(isIncrementalSearch()
1484: && !isRegExSearchAvailableAndChecked(), true);
1485: }
1486:
1487: /**
1488: * Locates the user's findString in the text of the target.
1489: *
1490: * @param mustInitIncrementalBaseLocation <code>true</code> if base location must be initialized
1491: * @param beepOnWrap if <code>true</code> beeps when search needs to wrap
1492: * @since 3.0
1493: */
1494: private void performSearch(boolean mustInitIncrementalBaseLocation,
1495: boolean beepOnWrap) {
1496:
1497: if (mustInitIncrementalBaseLocation)
1498: initIncrementalBaseLocation();
1499:
1500: String findString = getFindString();
1501: boolean somethingFound = false;
1502:
1503: if (findString != null && findString.length() > 0) {
1504:
1505: try {
1506: somethingFound = findNext(findString,
1507: isForwardSearch(), isCaseSensitiveSearch(),
1508: isWrapSearch(), isWholeWordSearch(),
1509: isIncrementalSearch()
1510: && !isRegExSearchAvailableAndChecked(),
1511: isRegExSearchAvailableAndChecked(), beepOnWrap);
1512: if (somethingFound) {
1513: statusMessage(""); //$NON-NLS-1$
1514: } else {
1515: statusMessage(EditorMessages.FindReplace_Status_noMatch_label);
1516: }
1517: } catch (PatternSyntaxException ex) {
1518: statusError(ex.getLocalizedMessage());
1519: } catch (IllegalStateException ex) {
1520: // we don't keep state in this dialog
1521: }
1522: }
1523: writeSelection();
1524: updateButtonState(!somethingFound);
1525: }
1526:
1527: /**
1528: * Replaces all occurrences of the user's findString with
1529: * the replace string. Returns the number of replacements
1530: * that occur.
1531: *
1532: * @param findString the string to search for
1533: * @param replaceString the replacement string
1534: * @param forwardSearch the search direction
1535: * @param caseSensitive should the search be case sensitive
1536: * @param wrapSearch should search wrap to start/end if end/start is reached
1537: * @param wholeWord does the search string represent a complete word
1538: * @param regExSearch if <code>true</code> findString represents a regular expression
1539: * @return the number of occurrences
1540: *
1541: * @since 3.0
1542: */
1543: private int replaceAll(String findString, String replaceString,
1544: boolean forwardSearch, boolean caseSensitive,
1545: boolean wrapSearch, boolean wholeWord, boolean regExSearch) {
1546:
1547: int replaceCount = 0;
1548: int findReplacePosition = 0;
1549:
1550: if (wrapSearch) { // search the whole text
1551: findReplacePosition = 0;
1552: forwardSearch = true;
1553: } else {
1554: // the cursor is set to the end or beginning of the selected text
1555: Point selection = fTarget.getSelection();
1556: findReplacePosition = selection.x;
1557: }
1558:
1559: if (!validateTargetState())
1560: return replaceCount;
1561:
1562: if (fTarget instanceof IFindReplaceTargetExtension)
1563: ((IFindReplaceTargetExtension) fTarget)
1564: .setReplaceAllMode(true);
1565:
1566: try {
1567: int index = 0;
1568: while (index != -1) {
1569: index = findAndSelect(findReplacePosition, findString,
1570: forwardSearch, caseSensitive, wholeWord,
1571: regExSearch);
1572: if (index != -1) { // substring not contained from current position
1573: Point selection = replaceSelection(replaceString,
1574: regExSearch);
1575: replaceCount++;
1576:
1577: if (forwardSearch)
1578: findReplacePosition = selection.x + selection.y;
1579: else {
1580: findReplacePosition = selection.x - 1;
1581: if (findReplacePosition == -1)
1582: break;
1583: }
1584: }
1585: }
1586: } finally {
1587: if (fTarget instanceof IFindReplaceTargetExtension)
1588: ((IFindReplaceTargetExtension) fTarget)
1589: .setReplaceAllMode(false);
1590: }
1591:
1592: return replaceCount;
1593: }
1594:
1595: // ------- UI creation ---------------------------------------
1596:
1597: /**
1598: * Attaches the given layout specification to the <code>component</code>.
1599: *
1600: * @param component the component
1601: * @param horizontalAlignment horizontal alignment
1602: * @param grabExcessHorizontalSpace grab excess horizontal space
1603: * @param verticalAlignment vertical alignment
1604: * @param grabExcessVerticalSpace grab excess vertical space
1605: */
1606: private void setGridData(Control component,
1607: int horizontalAlignment, boolean grabExcessHorizontalSpace,
1608: int verticalAlignment, boolean grabExcessVerticalSpace) {
1609: GridData gd;
1610: if (component instanceof Button
1611: && (((Button) component).getStyle() & SWT.PUSH) != 0) {
1612: SWTUtil.setButtonDimensionHint((Button) component);
1613: gd = (GridData) component.getLayoutData();
1614: } else {
1615: gd = new GridData();
1616: component.setLayoutData(gd);
1617: gd.horizontalAlignment = horizontalAlignment;
1618: gd.grabExcessHorizontalSpace = grabExcessHorizontalSpace;
1619: }
1620: gd.verticalAlignment = verticalAlignment;
1621: gd.grabExcessVerticalSpace = grabExcessVerticalSpace;
1622: }
1623:
1624: /**
1625: * Adds enough space in the control's layout data margin for the content assist
1626: * decoration.
1627: * @param control the control that needs a margin
1628: * @since 3.3
1629: */
1630: private void addDecorationMargin(Control control) {
1631: Object layoutData = control.getLayoutData();
1632: if (!(layoutData instanceof GridData))
1633: return;
1634: GridData gd = (GridData) layoutData;
1635: FieldDecoration dec = FieldDecorationRegistry.getDefault()
1636: .getFieldDecoration(
1637: FieldDecorationRegistry.DEC_CONTENT_PROPOSAL);
1638: gd.horizontalIndent = dec.getImage().getBounds().width;
1639: }
1640:
1641: /**
1642: * Updates the enabled state of the buttons.
1643: */
1644: private void updateButtonState() {
1645: updateButtonState(false);
1646: }
1647:
1648: /**
1649: * Updates the enabled state of the buttons.
1650: *
1651: * @param disableReplace <code>true</code> if replace button must be disabled
1652: * @since 3.0
1653: */
1654: private void updateButtonState(boolean disableReplace) {
1655: if (okToUse(getShell()) && okToUse(fFindNextButton)) {
1656:
1657: boolean selection = false;
1658: if (fTarget != null)
1659: selection = fTarget.getSelectionText().length() > 0;
1660:
1661: boolean enable = fTarget != null
1662: && (fActiveShell == fParentShell || fActiveShell == getShell());
1663: String str = getFindString();
1664: boolean findString = str != null && str.length() > 0;
1665:
1666: fWholeWordCheckBox.setEnabled(isWord(str)
1667: && !isRegExSearchAvailableAndChecked());
1668:
1669: fFindNextButton.setEnabled(enable && findString);
1670: fReplaceSelectionButton
1671: .setEnabled(!disableReplace
1672: && enable
1673: && isEditable()
1674: && selection
1675: && (!fNeedsInitialFindBeforeReplace || !isRegExSearchAvailableAndChecked()));
1676: fReplaceFindButton
1677: .setEnabled(!disableReplace
1678: && enable
1679: && isEditable()
1680: && findString
1681: && selection
1682: && (!fNeedsInitialFindBeforeReplace || !isRegExSearchAvailableAndChecked()));
1683: fReplaceAllButton.setEnabled(enable && isEditable()
1684: && findString);
1685: }
1686: }
1687:
1688: /**
1689: * Tests whether each character in the given
1690: * string is a letter.
1691: *
1692: * @param str
1693: * @return <code>true</code> if the given string is a word
1694: * @since 3.0
1695: */
1696: private boolean isWord(String str) {
1697: if (str == null || str.length() == 0)
1698: return false;
1699:
1700: for (int i = 0; i < str.length(); i++) {
1701: if (!Character.isJavaIdentifierPart(str.charAt(i)))
1702: return false;
1703: }
1704: return true;
1705: }
1706:
1707: /**
1708: * Updates the given combo with the given content.
1709: * @param combo combo to be updated
1710: * @param content to be put into the combo
1711: */
1712: private void updateCombo(Combo combo, List content) {
1713: combo.removeAll();
1714: for (int i = 0; i < content.size(); i++) {
1715: combo.add(content.get(i).toString());
1716: }
1717: }
1718:
1719: // ------- open / reopen ---------------------------------------
1720:
1721: /**
1722: * Called after executed find/replace action to update the history.
1723: */
1724: private void updateFindAndReplaceHistory() {
1725: updateFindHistory();
1726: if (okToUse(fReplaceField)) {
1727: updateHistory(fReplaceField, fReplaceHistory);
1728: }
1729:
1730: }
1731:
1732: /**
1733: * Called after executed find action to update the history.
1734: */
1735: private void updateFindHistory() {
1736: if (okToUse(fFindField)) {
1737: fFindField.removeModifyListener(fFindModifyListener);
1738: updateHistory(fFindField, fFindHistory);
1739: fFindField.addModifyListener(fFindModifyListener);
1740: }
1741: }
1742:
1743: /**
1744: * Updates the combo with the history.
1745: * @param combo to be updated
1746: * @param history to be put into the combo
1747: */
1748: private void updateHistory(Combo combo, List history) {
1749: String findString = combo.getText();
1750: int index = history.indexOf(findString);
1751: if (index != 0) {
1752: if (index != -1) {
1753: history.remove(index);
1754: }
1755: history.add(0, findString);
1756: updateCombo(combo, history);
1757: combo.setText(findString);
1758: }
1759: }
1760:
1761: /**
1762: * Returns whether the target is editable.
1763: * @return <code>true</code> if target is editable
1764: */
1765: private boolean isEditable() {
1766: boolean isEditable = (fTarget == null ? false : fTarget
1767: .isEditable());
1768: return fIsTargetEditable && isEditable;
1769: }
1770:
1771: /**
1772: * Updates this dialog because of a different target.
1773: * @param target the new target
1774: * @param isTargetEditable <code>true</code> if the new target can be modified
1775: * @param initializeFindString <code>true</code> if the find string of this dialog should be initialized based on the viewer's selection
1776: * @since 2.0
1777: */
1778: public void updateTarget(IFindReplaceTarget target,
1779: boolean isTargetEditable, boolean initializeFindString) {
1780:
1781: fIsTargetEditable = isTargetEditable;
1782: fNeedsInitialFindBeforeReplace = true;
1783:
1784: if (target != fTarget) {
1785: if (fTarget != null
1786: && fTarget instanceof IFindReplaceTargetExtension)
1787: ((IFindReplaceTargetExtension) fTarget).endSession();
1788:
1789: fTarget = target;
1790: if (fTarget != null)
1791: fIsTargetSupportingRegEx = fTarget instanceof IFindReplaceTargetExtension3;
1792:
1793: if (fTarget instanceof IFindReplaceTargetExtension) {
1794: ((IFindReplaceTargetExtension) fTarget).beginSession();
1795:
1796: fGlobalInit = true;
1797: fGlobalRadioButton.setSelection(fGlobalInit);
1798: fSelectedRangeRadioButton.setSelection(!fGlobalInit);
1799: fUseSelectedLines = !fGlobalInit;
1800: }
1801: }
1802:
1803: if (okToUse(fIsRegExCheckBox))
1804: fIsRegExCheckBox.setEnabled(fIsTargetSupportingRegEx);
1805:
1806: if (okToUse(fWholeWordCheckBox))
1807: fWholeWordCheckBox
1808: .setEnabled(!isRegExSearchAvailableAndChecked());
1809:
1810: if (okToUse(fIncrementalCheckBox))
1811: fIncrementalCheckBox
1812: .setEnabled(!isRegExSearchAvailableAndChecked());
1813:
1814: if (okToUse(fReplaceLabel)) {
1815: fReplaceLabel.setEnabled(isEditable());
1816: fReplaceField.setEnabled(isEditable());
1817: if (initializeFindString) {
1818: initFindStringFromSelection();
1819: fGiveFocusToFindField = true;
1820: }
1821: initIncrementalBaseLocation();
1822: updateButtonState();
1823: }
1824:
1825: setContentAssistsEnablement(isRegExSearchAvailableAndChecked());
1826: }
1827:
1828: /**
1829: * Sets the parent shell of this dialog to be the given shell.
1830: *
1831: * @param shell the new parent shell
1832: */
1833: public void setParentShell(Shell shell) {
1834: if (shell != fParentShell) {
1835:
1836: if (fParentShell != null)
1837: fParentShell.removeShellListener(fActivationListener);
1838:
1839: fParentShell = shell;
1840: fParentShell.addShellListener(fActivationListener);
1841: }
1842:
1843: fActiveShell = shell;
1844: }
1845:
1846: //--------------- configuration handling --------------
1847:
1848: /**
1849: * Returns the dialog settings object used to share state
1850: * between several find/replace dialogs.
1851: *
1852: * @return the dialog settings to be used
1853: */
1854: private IDialogSettings getDialogSettings() {
1855: IDialogSettings settings = TextEditorPlugin.getDefault()
1856: .getDialogSettings();
1857: fDialogSettings = settings.getSection(getClass().getName());
1858: if (fDialogSettings == null)
1859: fDialogSettings = settings.addNewSection(getClass()
1860: .getName());
1861: return fDialogSettings;
1862: }
1863:
1864: /*
1865: * @see org.eclipse.jface.dialogs.Dialog#getDialogBoundsSettings()
1866: * @since 3.2
1867: */
1868: protected IDialogSettings getDialogBoundsSettings() {
1869: String sectionName = getClass().getName() + "_dialogBounds"; //$NON-NLS-1$
1870: IDialogSettings settings = TextEditorPlugin.getDefault()
1871: .getDialogSettings();
1872: IDialogSettings section = settings.getSection(sectionName);
1873: if (section == null)
1874: section = settings.addNewSection(sectionName);
1875: return section;
1876: }
1877:
1878: /*
1879: * @see org.eclipse.jface.dialogs.Dialog#getDialogBoundsStrategy()
1880: * @since 3.2
1881: */
1882: protected int getDialogBoundsStrategy() {
1883: return DIALOG_PERSISTLOCATION | DIALOG_PERSISTSIZE;
1884: }
1885:
1886: /**
1887: * Initializes itself from the dialog settings with the same state
1888: * as at the previous invocation.
1889: */
1890: private void readConfiguration() {
1891: IDialogSettings s = getDialogSettings();
1892:
1893: fWrapInit = s.getBoolean("wrap"); //$NON-NLS-1$
1894: fCaseInit = s.getBoolean("casesensitive"); //$NON-NLS-1$
1895: fWholeWordInit = s.getBoolean("wholeword"); //$NON-NLS-1$
1896: fIncrementalInit = s.getBoolean("incremental"); //$NON-NLS-1$
1897: fIsRegExInit = s.getBoolean("isRegEx"); //$NON-NLS-1$
1898:
1899: String[] findHistory = s.getArray("findhistory"); //$NON-NLS-1$
1900: if (findHistory != null) {
1901: List history = getFindHistory();
1902: history.clear();
1903: for (int i = 0; i < findHistory.length; i++)
1904: history.add(findHistory[i]);
1905: }
1906:
1907: String[] replaceHistory = s.getArray("replacehistory"); //$NON-NLS-1$
1908: if (replaceHistory != null) {
1909: List history = getReplaceHistory();
1910: history.clear();
1911: for (int i = 0; i < replaceHistory.length; i++)
1912: history.add(replaceHistory[i]);
1913: }
1914: }
1915:
1916: /**
1917: * Stores its current configuration in the dialog store.
1918: */
1919: private void writeConfiguration() {
1920: IDialogSettings s = getDialogSettings();
1921:
1922: s.put("wrap", fWrapInit); //$NON-NLS-1$
1923: s.put("casesensitive", fCaseInit); //$NON-NLS-1$
1924: s.put("wholeword", fWholeWordInit); //$NON-NLS-1$
1925: s.put("incremental", fIncrementalInit); //$NON-NLS-1$
1926: s.put("isRegEx", fIsRegExInit); //$NON-NLS-1$
1927:
1928: List history = getFindHistory();
1929: String findString = getFindString();
1930: if (findString.length() > 0)
1931: history.add(0, findString);
1932: writeHistory(history, s, "findhistory"); //$NON-NLS-1$
1933:
1934: history = getReplaceHistory();
1935: String replaceString = getReplaceString();
1936: if (replaceString.length() > 0)
1937: history.add(0, replaceString);
1938: writeHistory(history, s, "replacehistory"); //$NON-NLS-1$
1939: }
1940:
1941: /**
1942: * Writes the given history into the given dialog store.
1943: *
1944: * @param history the history
1945: * @param settings the dialog settings
1946: * @param sectionName the section name
1947: * @since 3.2
1948: */
1949: private void writeHistory(List history, IDialogSettings settings,
1950: String sectionName) {
1951: int itemCount = history.size();
1952: Set distinctItems = new HashSet(itemCount);
1953: for (int i = 0; i < itemCount; i++) {
1954: String item = (String) history.get(i);
1955: if (distinctItems.contains(item)) {
1956: history.remove(i--);
1957: itemCount--;
1958: } else {
1959: distinctItems.add(item);
1960: }
1961: }
1962:
1963: while (history.size() > 8)
1964: history.remove(8);
1965:
1966: String[] names = new String[history.size()];
1967: history.toArray(names);
1968: settings.put(sectionName, names);
1969:
1970: }
1971: }
|