diff --git a/core/org.eclipse.cdt.ui.tests/icons/used/search_ref_obj.gif b/core/org.eclipse.cdt.ui.tests/icons/used/search_ref_obj.gif new file mode 100644 index 00000000000..1b0c43bc1ee Binary files /dev/null and b/core/org.eclipse.cdt.ui.tests/icons/used/search_ref_obj.gif differ diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/testplugin/CTestPlugin.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/testplugin/CTestPlugin.java index 0e71b8c8475..d3390b7f0fe 100644 --- a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/testplugin/CTestPlugin.java +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/testplugin/CTestPlugin.java @@ -14,6 +14,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Plugin; +import org.eclipse.swt.widgets.Display; public class CTestPlugin extends Plugin { @@ -53,5 +54,13 @@ public class CTestPlugin extends Plugin { public static String getPluginId() { return PLUGIN_ID; } + + public static Display getStandardDisplay() { + Display display= Display.getCurrent(); + if (display == null) { + display= Display.getDefault(); + } + return display; + } } \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPPPopulateASTViewAction.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPPPopulateASTViewAction.java index 4189e2d9cac..25001284fb6 100644 --- a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPPPopulateASTViewAction.java +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPPPopulateASTViewAction.java @@ -19,6 +19,7 @@ import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTInitializer; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; import org.eclipse.cdt.core.dom.ast.IASTPointerOperator; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; @@ -39,6 +40,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBas import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVisitor.CPPBaseVisitorAction; import org.eclipse.cdt.internal.core.parser.scanner2.LocationMap.ASTInclusionStatement; +import org.eclipse.core.runtime.IProgressMonitor; /** * @author dsteffle @@ -61,13 +63,22 @@ public class CPPPopulateASTViewAction extends CPPBaseVisitorAction implements IP } TreeParent root = null; + IProgressMonitor monitor = null; - public CPPPopulateASTViewAction(IASTTranslationUnit tu) { + public CPPPopulateASTViewAction(IASTTranslationUnit tu, IProgressMonitor monitor) { root = new TreeParent(tu); + this.monitor = monitor; } - private void addRoot(IASTNode node) { - if (node == null) return; + private int addRoot(IASTNode node) { + if (monitor != null && monitor.isCanceled()) return PROCESS_ABORT; + if (node == null) return PROCESS_CONTINUE; + + IASTNodeLocation[] nodeLocations = node.getNodeLocations(); + if (!(nodeLocations.length > 0 && + nodeLocations[0].getNodeOffset() >= 0 && + nodeLocations[0].getNodeLength() > 0)) + return PROCESS_CONTINUE; TreeParent parent = root.findTreeParentForNode(node); @@ -84,21 +95,22 @@ public class CPPPopulateASTViewAction extends CPPBaseVisitorAction implements IP tree.setFiltersFlag(TreeObject.FLAG_PREPROCESSOR); if (node instanceof IASTPreprocessorIncludeStatement) tree.setFiltersFlag(TreeObject.FLAG_INCLUDE_STATEMENTS); + + return PROCESS_CONTINUE; } /* (non-Javadoc) * @see org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVisitor.CPPBaseVisitorAction#processDeclaration(org.eclipse.cdt.core.dom.ast.IASTDeclaration) */ public int processDeclaration(IASTDeclaration declaration) { - addRoot(declaration); - return PROCESS_CONTINUE; + return addRoot(declaration); } /* (non-Javadoc) * @see org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVisitor.CPPBaseVisitorAction#processDeclarator(org.eclipse.cdt.core.dom.ast.IASTDeclarator) */ public int processDeclarator(IASTDeclarator declarator) { - addRoot(declarator); + int ret = addRoot(declarator); IASTPointerOperator[] ops = declarator.getPointerOperators(); for(int i=0; i includes.length) { IASTPreprocessorIncludeStatement[] newIncludes = new IASTPreprocessorIncludeStatement[includes.length * 2]; for (int j=0; j=0; j--) { + if (monitor != null && monitor.isCanceled()) return; child = root.getChildren()[j]; if (treeIncludes[i] != child && diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPopulateASTViewAction.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPopulateASTViewAction.java index dd9b915a6b1..c0a35faf592 100644 --- a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPopulateASTViewAction.java +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPopulateASTViewAction.java @@ -35,6 +35,7 @@ import org.eclipse.cdt.core.dom.ast.c.ICASTDesignator; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.dom.parser.c.CVisitor.CBaseVisitorAction; import org.eclipse.cdt.internal.core.parser.scanner2.LocationMap.ASTInclusionStatement; +import org.eclipse.core.runtime.IProgressMonitor; /** * @author dsteffle @@ -55,20 +56,23 @@ public class CPopulateASTViewAction extends CBaseVisitorAction implements IPopul processEnumerators = true; } - TreeParent root = null; // TODO what about using a hashtable/hashmap for the tree? + TreeParent root = null; + IProgressMonitor monitor = null; - public CPopulateASTViewAction(IASTTranslationUnit tu) { + public CPopulateASTViewAction(IASTTranslationUnit tu, IProgressMonitor monitor) { root = new TreeParent(tu); + this.monitor = monitor; } - private void addRoot(IASTNode node) { - if (node == null) return; + private int addRoot(IASTNode node) { + if (monitor != null && monitor.isCanceled()) return PROCESS_ABORT; + if (node == null) return PROCESS_CONTINUE; IASTNodeLocation[] nodeLocations = node.getNodeLocations(); if (!(nodeLocations.length > 0 && nodeLocations[0].getNodeOffset() >= 0 && nodeLocations[0].getNodeLength() > 0)) - return; + return PROCESS_CONTINUE; TreeParent parent = root.findTreeParentForNode(node); @@ -85,21 +89,22 @@ public class CPopulateASTViewAction extends CBaseVisitorAction implements IPopul tree.setFiltersFlag(TreeObject.FLAG_PREPROCESSOR); if (node instanceof IASTPreprocessorIncludeStatement) tree.setFiltersFlag(TreeObject.FLAG_INCLUDE_STATEMENTS); + + return PROCESS_CONTINUE; } /* (non-Javadoc) * @see org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVisitor.CPPBaseVisitorAction#processDeclaration(org.eclipse.cdt.core.dom.ast.IASTDeclaration) */ public int processDeclaration(IASTDeclaration declaration) { - addRoot(declaration); - return PROCESS_CONTINUE; + return addRoot(declaration); } /* (non-Javadoc) * @see org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVisitor.CPPBaseVisitorAction#processDeclarator(org.eclipse.cdt.core.dom.ast.IASTDeclarator) */ public int processDeclarator(IASTDeclarator declarator) { - addRoot(declarator); + int ret = addRoot(declarator); IASTPointerOperator[] ops = declarator.getPointerOperators(); for(int i=0; i includes.length) { IASTPreprocessorIncludeStatement[] newIncludes = new IASTPreprocessorIncludeStatement[includes.length * 2]; for (int j=0; j=0; j--) { + if (monitor != null && monitor.isCanceled()) return; child = root.getChildren()[j]; if (treeIncludes[i] != child && diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/DOMAST.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/DOMAST.java index 505499ab004..643eb55ced8 100644 --- a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/DOMAST.java +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/DOMAST.java @@ -48,12 +48,18 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVisitor.CPPBaseVisitorAct import org.eclipse.cdt.internal.ui.editor.CEditor; import org.eclipse.cdt.internal.ui.util.EditorUtility; import org.eclipse.cdt.ui.actions.CustomFiltersActionGroup; +import org.eclipse.cdt.ui.testplugin.CTestPlugin; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IContributionItem; @@ -83,11 +89,9 @@ import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.IEditorReference; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchActionConstants; -import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.DrillDownAdapter; @@ -109,8 +113,10 @@ import org.eclipse.ui.part.ViewPart; */ public class DOMAST extends ViewPart { + private static final String DOM_AST_HAS_NO_CONTENT = "DOM AST has no content"; //$NON-NLS-1$ + private static final String SEARCH_FOR_IASTNAME = "Search for IASTName"; //$NON-NLS-1$ private static final String CLEAR = "Clear"; //$NON-NLS-1$ -private static final String DOMAST_FILTER_GROUP_ID = "org.eclipse.cdt.ui.tests.DOMAST.DOMASTFilterGroup"; //$NON-NLS-1$ + private static final String DOMAST_FILTER_GROUP_ID = "org.eclipse.cdt.ui.tests.DOMAST.DOMASTFilterGroup"; //$NON-NLS-1$ private static final String EXTENSION_CXX = "CXX"; //$NON-NLS-1$ private static final String EXTENSION_CPP = "CPP"; //$NON-NLS-1$ private static final String EXTENSION_CC = "CC"; //$NON-NLS-1$ @@ -134,6 +140,7 @@ private static final String DOMAST_FILTER_GROUP_ID = "org.eclipse.cdt.ui.tests.D private Action expandAllAction; private Action collapseAllAction; private Action clearAction; + private Action searchNamesAction; private IFile file = null; private IEditorPart part = null; private ParserLanguage lang = null; @@ -149,23 +156,46 @@ private static final String DOMAST_FILTER_GROUP_ID = "org.eclipse.cdt.ui.tests.D public class ViewContentProvider implements IStructuredContentProvider, ITreeContentProvider { - private TreeParent invisibleRoot; - private IFile aFile = null; + private static final String POPULATING_AST_VIEW = "Populating AST View"; //$NON-NLS-1$ + private TreeParent invisibleRoot; + private TreeParent tuTreeParent = null; + private IASTTranslationUnit tu = null; - /** - * - */ public ViewContentProvider() { } - /** - * - */ public ViewContentProvider(IFile file) { - this.aFile = file; + this(file, null); } + + public ViewContentProvider(IFile file, Object[] expanded) { + StartInitializingASTView job = new StartInitializingASTView(new InitializeView(POPULATING_AST_VIEW, this, viewer, file), expanded); + job.schedule(); - public void inputChanged(Viewer v, Object oldInput, Object newInput) { + } + + public TreeParent getTUTreeParent() { + if (tuTreeParent == null && invisibleRoot != null) { + for(int i=0; i 0) { + TreeItem[] selection = new TreeItem[1]; + selection[0] = view.getTree().getItems()[0]; + + // select the first item to prevent it from being selected accidentally (and possibly switching editors accidentally) + view.getTree().setSelection(selection); + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) + */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } } class ViewLabelProvider extends LabelProvider { @@ -305,41 +506,20 @@ private static final String DOMAST_FILTER_GROUP_ID = "org.eclipse.cdt.ui.tests.D public void createPartControl(Composite parent) { if (part == null) { - IWorkbenchPage[] pages = PlatformUI.getWorkbench() - .getActiveWorkbenchWindow().getPages(); - - if (pages.length == 0) { - // TODO determine how to hide view if no pages found and part==null - } - - outerLoop: for (int i = 0; i < pages.length; i++) { - IEditorReference[] editorRefs = pages[i].getEditorReferences(); - for (int j = 0; j < editorRefs.length; j++) { - part = editorRefs[j].getEditor(false); - if (part instanceof CEditor) { - // TODO set the language properly if implement the above TODO - lang = ParserLanguage.CPP; - break outerLoop; - } - } - } - - if (PlatformUI.getWorkbench().getActiveWorkbenchWindow() - .getActivePage() != null - && PlatformUI.getWorkbench().getActiveWorkbenchWindow() - .getActivePage().getActiveEditor() != null) - part = PlatformUI.getWorkbench().getActiveWorkbenchWindow() - .getActivePage().getActiveEditor(); + part = getActiveEditor(); + + if (!(part instanceof CEditor)) return; } viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); drillDownAdapter = new DrillDownAdapter(viewer); - if (part instanceof CEditor) - viewer.setContentProvider(new ViewContentProvider(((CEditor) part) - .getInputFile())); - else + if (part instanceof CEditor) { + viewer.setContentProvider(new ViewContentProvider(((CEditor) part).getInputFile())); + setFile(((CEditor) part).getInputFile()); + } else { viewer.setContentProvider(new ViewContentProvider(null)); // don't attempt to create a view based on old file info + } viewer.setLabelProvider(new ViewLabelProvider()); viewer.setInput(getViewSite()); @@ -353,6 +533,7 @@ private static final String DOMAST_FILTER_GROUP_ID = "org.eclipse.cdt.ui.tests.D public void setContentProvider(ViewContentProvider vcp) { viewer.setContentProvider(vcp); + } private void hookContextMenu() { @@ -393,9 +574,6 @@ private static final String DOMAST_FILTER_GROUP_ID = "org.eclipse.cdt.ui.tests.D } private void fillLocalPullDown(IMenuManager manager) { - // manager.add(action1); // TODO determine the groups/filters to use - // manager.add(new Separator()); - // manager.add(action2); } void fillContextMenu(IMenuManager manager) { @@ -408,7 +586,7 @@ private static final String DOMAST_FILTER_GROUP_ID = "org.eclipse.cdt.ui.tests.D } private void fillLocalToolBar(IToolBarManager manager) { - manager.add(expandAllAction); + manager.add(expandAllAction); manager.add(collapseAllAction); manager.add(new Separator()); manager.add(refreshAction); @@ -416,6 +594,8 @@ private static final String DOMAST_FILTER_GROUP_ID = "org.eclipse.cdt.ui.tests.D manager.add(new Separator()); manager.add(clearAction); manager.add(new Separator()); + manager.add(searchNamesAction); + manager.add(new Separator()); drillDownAdapter.addNavigationActions(manager); } @@ -423,19 +603,7 @@ private static final String DOMAST_FILTER_GROUP_ID = "org.eclipse.cdt.ui.tests.D loadActiveEditorAction = new Action() { public void run() { // get the active editor - IEditorPart editor = null; - if (PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null && - PlatformUI.getWorkbench().getActiveWorkbenchWindow().getPages() != null) { - IWorkbenchPage[] pages = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getPages(); - - outerLoop: for(int i=0; iFindReplaceAction + */ +class FindIASTNameDialog extends Dialog { + + private static final String REGULAR_EXPRESSIONS_LABEL = "Regular expressions"; //$NON-NLS-1$ + private static final String WHOLE_WORD_LABEL = "Whole Word"; //$NON-NLS-1$ + private static final String CASE_SENSITIVE_LABEL = "Case Sensitive"; //$NON-NLS-1$ + private static final String OPTIONS_LABEL = "Options"; //$NON-NLS-1$ + private static final String BLANK_STRING = ""; //$NON-NLS-1$ + private static final String NAME_NOT_FOUND = "Name not found."; //$NON-NLS-1$ + private static final String FIND_NEXT_LABEL = "Find Next"; //$NON-NLS-1$ + private static final String FIND_IASTNAME_LABEL = "Find IASTName:"; //$NON-NLS-1$ + /** + * 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 true if the find field had focus, + * false if it did not. + * + * @return true if the find field had focus, + * false 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) { + searchTextChanged = true; + + if (isIncrementalSearch() && !isRegExSearchAvailableAndChecked()) { + if (fFindField.getText().equals(BLANK_STRING) && 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, BLANK_STRING, isForwardSearch(), isCaseSensitiveSearch(), isWholeWordSearch(), isRegExSearchAvailableAndChecked()); //$NON-NLS-1$ + } else { + performSearch(false); + } + } + + updateButtonState(); + } + } + + /** The size of the dialogs search history. */ + private static final int HISTORY_SIZE= 5; + + protected boolean searchTextChanged=false; + 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 IRegion fOldScope; + + private IFindReplaceTarget fTarget; + private Shell fParentShell; + private Shell fActiveShell; + + private final ActivationListener fActivationListener= new ActivationListener(); + private final ModifyListener fFindModifyListener= new FindModifyListener(); + + private Label 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 fFindNextButton; + Combo fFindField, fReplaceField; + private Rectangle fDialogPositionInit; + + private IDialogSettings fDialogSettings; + /** + * Tells whether the target supports regular expressions. + * true if the target supports regular expressions + * @since 3.0 + */ + private boolean fIsTargetSupportingRegEx; + /** + * Tells whether fUseSelectedLines radio is checked. + * @since 3.0 + */ + private boolean fUseSelectedLines; + /** + * true if the find field should receive focus the next time + * the dialog is activated, false otherwise. + * @since 3.0 + */ + private boolean fGiveFocusToFindField= true; + + + + /** + * Creates a new dialog with the given shell as parent. + * @param parentShell the parent shell + */ + public FindIASTNameDialog(Shell parentShell, IFindReplaceTarget target) { + super(parentShell); + + fParentShell= null; + updateTarget(target, false); + + fDialogPositionInit= null; + fFindHistory= 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 true if control can be used. + * + * @param control the control to be checked + * @return true 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(FIND_IASTNAME_LABEL); + // 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= 1; + panel.setLayout(layout); + + fFindNextButton= makeButton(panel, FIND_NEXT_LABEL, 102, true, new SelectionAdapter() { //$NON-NLS-1$ + public void widgetSelected(SelectionEvent e) { + if (fTarget instanceof FindIASTNameTarget && searchTextChanged) { + ((FindIASTNameTarget)fTarget).clearMatchingNames(); + searchTextChanged = false; + } + + if (isIncrementalSearch() && !isRegExSearchAvailableAndChecked()) + initIncrementalBaseLocation(); + + fNeedsInitialFindBeforeReplace= false; + performSearch(); + updateFindHistory(); + fFindNextButton.setFocus(); + } + }); + setGridData(fFindNextButton, GridData.FILL, true, GridData.FILL, false); + + 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= 1; + panel.setLayout(layout); + + Composite directionGroup= createDirectionGroup(panel); + setGridData(directionGroup, 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); + + return panel; + } + + /** + * 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("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("Forward"); //$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("Backward"); //$NON-NLS-1$ + setGridData(backwardRadioButton, GridData.BEGINNING, false, GridData.CENTER, false); + backwardRadioButton.addSelectionListener(selectionListener); + + backwardRadioButton.setSelection(!fForwardInit); + fForwardRadioButton.setSelection(fForwardInit); + + return panel; + } + + /** + * Tells the dialog to perform searches only in the scope given by the actually selected lines. + * @param selectedLines true 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) { + 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("Find Name:"); //$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); + + 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(OPTIONS_LABEL); //$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(CASE_SENSITIVE_LABEL); //$NON-NLS-1$ + setGridData(fCaseCheckBox, GridData.BEGINNING, false, GridData.CENTER, false); + fCaseCheckBox.setSelection(fCaseInit); + fCaseCheckBox.addSelectionListener(selectionListener); + + fWholeWordCheckBox= new Button(group, SWT.CHECK | SWT.LEFT); + fWholeWordCheckBox.setText(WHOLE_WORD_LABEL); //$NON-NLS-1$ + setGridData(fWholeWordCheckBox, GridData.BEGINNING, false, GridData.CENTER, false); + fWholeWordCheckBox.setSelection(fWholeWordInit); + fWholeWordCheckBox.addSelectionListener(selectionListener); + + fIsRegExCheckBox= new Button(group, SWT.CHECK | SWT.LEFT); + fIsRegExCheckBox.setText(REGULAR_EXPRESSIONS_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(); + if (fTarget instanceof FindIASTNameTarget) + ((FindIASTNameTarget)fTarget).clearMatchingNames(); + + updateButtonState(); + storeSettings(); + } + }); + fWholeWordCheckBox.setEnabled(!isRegExSearchAvailableAndChecked()); + fWholeWordCheckBox.addSelectionListener(new SelectionAdapter() { + /* + * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) + */ + public void widgetSelected(SelectionEvent e) { + if (fTarget instanceof FindIASTNameTarget) + ((FindIASTNameTarget)fTarget).clearMatchingNames(); + + updateButtonState(); + } + }); + return panel; + } + + /** + * Creates the status and close section of the dialog. + * + * @param parent the parent composite + * @return the status and close button + */ + private Composite createStatusAndCloseButton(Composite parent) { + + Composite panel= new Composite(parent, SWT.NULL); + GridLayout layout= new GridLayout(); + layout.numColumns= 2; + layout.marginWidth= 0; + layout.marginHeight= 0; + panel.setLayout(layout); + + fStatusLabel= new Label(panel, SWT.LEFT); + setGridData(fStatusLabel, GridData.FILL, true, GridData.CENTER, false); + + String label= "Close"; //$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 -1 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 true findString represents a regular expression + * @return the occurrence of the find string following the options or -1 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 true performs a case sensitive search, false an insensitive search + * @param wholeWord if true only occurrences are reported in which the findString stands as a word by itself + * @param regExSearch if true 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); + return fTarget.findAndSelect(offset, findString, forwardSearch, caseSensitive, wholeWord); + } + + /** + * Replaces the selection with replaceString. If + * regExReplace is true, + * replaceString 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 true if replaceString + * 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 true findString represents a regular expression + * @return true 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(); + 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 BLANK_STRING; //$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(); + + // 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= BLANK_STRING; //$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= isWholeWordSetting(); + 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 (BLANK_STRING.equals(fFindField.getText())) { //$NON-NLS-1$ + if (fFindHistory.size() > 0) + fFindField.setText((String) fFindHistory.get(0)); + else + fFindField.setText(BLANK_STRING); //$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 true 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 true 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 true 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 true 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 true if searching for whole words + */ + private boolean isWholeWordSetting() { + if (okToUse(fWholeWordCheckBox)) { + return fWholeWordCheckBox.getSelection(); + } + return fWholeWordInit; + } + + /** + * Returns true if searching should be restricted to entire + * words, false if not. This is the case if the respective + * checkbox is turned on, regex is off, and the checkbox is enabled, i.e. + * the current find string is an entire word. + * + * @return true if the search is restricted to whole words + */ + private boolean isWholeWordSearch() { + return isWholeWordSetting() && !isRegExSearchAvailableAndChecked() && (okToUse(fWholeWordCheckBox) ? fWholeWordCheckBox.isEnabled() : true); + } + + /** + * Retrieves and returns the option wrap search from the appropriate check box. + * @return true 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 true 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 label, int id, boolean dfltButton, SelectionListener listener) { + Button b= createButton(parent, id, label, dfltButton); + b.addSelectionListener(listener); + return b; + } + + /** + * Returns the status line manager of the active editor or null 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 true 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); + } + + /** + * 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 true 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(), isIncrementalSearch() && !isRegExSearchAvailableAndChecked(), isRegExSearchAvailableAndChecked()); + if (somethingFound) { + statusMessage(BLANK_STRING); //$NON-NLS-1$ + } else { + statusMessage(NAME_NOT_FOUND); //$NON-NLS-1$ + } + } catch (PatternSyntaxException ex) { + statusError(ex.getLocalizedMessage()); + } catch (IllegalStateException ex) { + // we don't keep state in this dialog + } + } + writeSelection(); + updateButtonState(); + } + + // ------- UI creation --------------------------------------- + + /** + * Attaches the given layout specification to the component. + * + * @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. + * + * @since 3.0 + */ + private void updateButtonState() { + if (okToUse(getShell()) && okToUse(fFindNextButton)) { + boolean enable= fTarget != null && (fActiveShell == fParentShell || fActiveShell == getShell()); + String str= getFindString(); + boolean findString= str != null && str.length() > 0; + + boolean wholeWord= isWord(str) && !isRegExSearchAvailableAndChecked(); + fWholeWordCheckBox.setEnabled(wholeWord); + + fFindNextButton.setEnabled(enable && findString); + } + } + + /** + * Tests whether each character in the given + * string is a letter. + * + * @param str + * @return true if the given string is a word + * @since 3.0 + */ + private boolean isWord(String str) { + if (str == null || str.length() == 0) + return false; + + for (int i= 0; i < str.length(); i++) { + if (!Character.isJavaIdentifierPart(str.charAt(i))) + return false; + } + return true; + } + + /** + * 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 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); + } + } + + /** + * Updates this dialog because of a different target. + * @param target the new target + * @param isTargetEditable true if the new target can be modifed + * @since 2.0 + */ + public void updateTarget(IFindReplaceTarget target, boolean 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()); + + // see pr 51073 + fGiveFocusToFindField= true; + } + + /** + * 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]); + } + } + + /** + * 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$ + } + +} diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/FindIASTNameTarget.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/FindIASTNameTarget.java new file mode 100644 index 00000000000..09d9af70af1 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/FindIASTNameTarget.java @@ -0,0 +1,444 @@ +/******************************************************************************* + * Copyright (c) 2005 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.cdt.ui.tests.DOMAST; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.parser.ParserLanguage; +import org.eclipse.cdt.internal.core.dom.parser.ASTNode; +import org.eclipse.cdt.internal.core.dom.parser.c.CVisitor; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVisitor; +import org.eclipse.jface.text.IFindReplaceTarget; +import org.eclipse.jface.text.IFindReplaceTargetExtension3; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.TreeItem; + +/** + * @author dsteffle + */ +public class FindIASTNameTarget implements IFindReplaceTarget, IFindReplaceTargetExtension3 { + + IASTTranslationUnit tu = null; + TreeParent tuTreeParent = null; + ParserLanguage lang = null; + TreeViewer viewer = null; + TreeObject startingNode = null; + IASTName[] matchingNames = null; + boolean wasForward = true; + int index = 0; + + static protected class CNameCollector extends CVisitor.CBaseVisitorAction { + private static final int REGULAR_NAME_ADD = -1; + private static final String BLANK_STRING = ""; //$NON-NLS-1$ + { + processNames = true; + } + public List nameList = new ArrayList(); + + String findString = null; + boolean caseSensitive = true; + boolean wholeWord = true; + boolean regExSearch = false; + + public CNameCollector(String findString, boolean caseSensitive, boolean wholeWord, boolean regExSearch) { + this.findString = findString; + this.caseSensitive = caseSensitive; + this.wholeWord = wholeWord; + this.regExSearch = regExSearch; + } + + public int processName( IASTName name, int offset) { + if (name.toString() == null || name.toString() == BLANK_STRING) return PROCESS_CONTINUE; + String searchString = null; + String match = null; + boolean addName = false; + + if (caseSensitive) { + searchString = findString; + match = name.toString(); + } else { + searchString = findString.toUpperCase(); + match = name.toString().toUpperCase(); + } + + if (regExSearch) { + if (match.matches(searchString)) + addName = true; + } else if (!wholeWord) { + if (match.indexOf(searchString) >= 0) + addName = true; + } else { + if (match.equals(searchString)) + addName = true; + } + + if (addName) { + if (offset >= 0) + nameList.add(offset, name); + else + nameList.add( name ); + } + + return PROCESS_CONTINUE; + } + + public int processName( IASTName name ){ + return processName(name, REGULAR_NAME_ADD); + } + public IASTName getName( int idx ){ + if( idx < 0 || idx >= nameList.size() ) + return null; + return (IASTName) nameList.get( idx ); + } + public int size() { return nameList.size(); } + + private void mergeName(IASTName name) { + if (name instanceof ASTNode) { + int offset = ((ASTNode)name).getOffset(); + for( int i=0; i offset) { + processName(name, i); + return; + } + } + // if couldn't find the proper place to put the name, then add default + processName(name); + } + } + + public IASTName[] getNameArray(IASTPreprocessorStatement[] statements) { + // first merge all of the preprocessor names into the array list + for(int i=0; i= 0) + addName = true; + } else { + if (match.equals(searchString)) + addName = true; + } + + if (addName) { + if (index >= 0) + nameList.add(index, name); + else + nameList.add( name ); + } + + return PROCESS_CONTINUE; + } + + public int processName( IASTName name ){ + return processName(name, REGULAR_NAME_ADD); + } + public IASTName getName( int idx ){ + if( idx < 0 || idx >= nameList.size() ) + return null; + return (IASTName) nameList.get( idx ); + } + public int size() { return nameList.size(); } + + private void mergeName(IASTName name) { + if (name instanceof ASTNode) { + int offset = ((ASTNode)name).getOffset(); + for( int i=0; i offset) { + processName(name, i); + return; + } + } + // if couldn't find the proper place to put the name, then add default + processName(name); + } + } + + public IASTName[] getNameArray(IASTPreprocessorStatement[] statements) { + // first merge all of the preprocessor names into the array list + for(int i=0; i=0 && index < matchingNames.length && matchingNames[index] != null) + return matchingNames[index++]; + } else { + if (wasForward) { + wasForward = false; + index-=2; + } + + if (index >= 0 && index < matchingNames.length && matchingNames[index] != null) + return matchingNames[index--]; + } + + return null; + } + + private TreeItem expandTreeToTreeObject(TreeItem[] treeItems, TreeObject treeObj) { + for (int i=0; i 0 && + ((searchForward && index < matchingNames.length) || + (!searchForward && index >= 0))) { + foundName = findNextMatchingName( findString, searchForward, caseSensitive, wholeWord, regExSearch ); + treeNode = tuTreeParent.findTreeObjectForIASTName(foundName); + + if (treeNode != null && treeNode.getParent() != null) { + // found a matching TreeObject, so expand the tree to that object + treeItem = expandTreeToTreeObject(treeNode); // TODO Devin test this + } + } + + // select the node that was found (and is now displayed) + if (treeItem != null) { + TreeItem[] items = new TreeItem[1]; + items[0] = treeItem; + treeItem.getParent().setSelection(items); + + return 0; + } + + return -1; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IFindReplaceTargetExtension3#replaceSelection(java.lang.String, boolean) + */ + public void replaceSelection(String text, boolean regExReplace) { + // TODO Auto-generated method stub + + } + +} diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/IPopulateDOMASTAction.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/IPopulateDOMASTAction.java index 0caff99dcad..b077a16d3b1 100644 --- a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/IPopulateDOMASTAction.java +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/IPopulateDOMASTAction.java @@ -10,9 +10,15 @@ **********************************************************************/ package org.eclipse.cdt.ui.tests.DOMAST; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; +import org.eclipse.cdt.core.dom.ast.IASTProblem; + /** * @author dsteffle */ public interface IPopulateDOMASTAction { public TreeParent getTree(); + public void mergePreprocessorStatements(IASTPreprocessorStatement[] statements); + public void mergePreprocessorProblems(IASTProblem[] problems); + public void groupIncludes(IASTPreprocessorStatement[] statements); } diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/TreeParent.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/TreeParent.java index 86110fbe966..4980ec92166 100644 --- a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/TreeParent.java +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/TreeParent.java @@ -13,15 +13,15 @@ package org.eclipse.cdt.ui.tests.DOMAST; import java.util.ArrayList; import java.util.Iterator; +import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; -import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; /** * @author dsteffle */ public class TreeParent extends TreeObject { - private ArrayList children; // TODO might want to use a HashTable/HashMap or wrap one so finding the parent node is faster + private ArrayList children; public TreeParent(IASTNode node) { super(node); @@ -168,5 +168,67 @@ public class TreeParent extends TreeObject { return null; // nothing found } + + /** + * Returns the TreeParent that corresponds to the IASTName. This is based on string and offset equality. + * + * @param node + * @return + */ + public TreeParent findTreeObjectForIASTName(IASTName name) { + if (name == null) return null; + + Iterator itr = children.iterator(); + while (itr.hasNext()) { + Object o = itr.next(); + if (o != null && o instanceof TreeParent) { + if (treeParentHasName((TreeParent)o, name)) return (TreeParent)o; + + if ( ((TreeParent)o).hasChildren() ){ + TreeParent tree = findTreeObjectForIASTName( ((TreeParent)o).getChildren(), name ); + if (tree != null) return tree; + } + } + } + + return null; // nothing found + } + + /** + * Returns the TreeParent that corresponds to the IASTName. This is based on string and offset equality. + * + * @param trees + * @param node + * @return + */ + private TreeParent findTreeObjectForIASTName(TreeObject[] trees, IASTName name) { + for (int i=0; i