Java tutorial
/* * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.find.impl; import com.intellij.CommonBundle; import com.intellij.find.FindBundle; import com.intellij.find.FindModel; import com.intellij.find.FindSettings; import com.intellij.find.actions.ShowUsagesAction; import com.intellij.ide.util.scopeChooser.ScopeChooserCombo; import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.fileTypes.PlainTextFileType; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.util.ProgressIndicatorBase; import com.intellij.openapi.progress.util.ProgressIndicatorUtils; import com.intellij.openapi.progress.util.ReadTask; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.*; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.search.SearchScope; import com.intellij.ui.*; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.table.JBTable; import com.intellij.usageView.UsageInfo; import com.intellij.usages.*; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; import com.intellij.util.Processor; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.mustbe.consulo.RequiredReadAction; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import java.awt.*; import java.awt.event.*; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; public class FindDialog extends DialogWrapper { private static final Logger LOG = Logger.getInstance("#com.intellij.find.impl.FindDialog"); private ComboBox myInputComboBox; private ComboBox myReplaceComboBox; private StateRestoringCheckBox myCbCaseSensitive; private StateRestoringCheckBox myCbPreserveCase; private StateRestoringCheckBox myCbWholeWordsOnly; private ComboBox mySearchContext; private StateRestoringCheckBox myCbRegularExpressions; private JRadioButton myRbGlobal; private JRadioButton myRbSelectedText; private JRadioButton myRbForward; private JRadioButton myRbBackward; private JRadioButton myRbFromCursor; private JRadioButton myRbEntireScope; private JRadioButton myRbProject; private JRadioButton myRbDirectory; private JRadioButton myRbModule; private ComboBox myModuleComboBox; private ComboBox myDirectoryComboBox; private StateRestoringCheckBox myCbWithSubdirectories; private JCheckBox myCbToOpenInNewTab; private FindModel myModel; private Consumer<FindModel> myOkHandler; private FixedSizeButton mySelectDirectoryButton; private StateRestoringCheckBox myUseFileFilter; private ComboBox myFileFilter; private JCheckBox myCbToSkipResultsWhenOneUsage; private final Project myProject; private final Map<EditorTextField, DocumentAdapter> myComboBoxListeners = new HashMap<EditorTextField, DocumentAdapter>(); private Action myFindAllAction; private JRadioButton myRbCustomScope; private ScopeChooserCombo myScopeCombo; protected JLabel myReplacePrompt; private HideableTitledPanel myScopePanel; private static boolean myPreviousResultsExpandedState; private static boolean myPreviousPreviewResultsState = true; private HideableTitledPanel myResultsPreviewPanel; private JBTable myResultsPreviewTable; private volatile ProgressIndicatorBase myResultsPreviewSearchProgress; public FindDialog(@NotNull Project project, @NotNull FindModel model, @NotNull Consumer<FindModel> myOkHandler) { super(project, true); myProject = project; myModel = model; this.myOkHandler = myOkHandler; updateTitle(); setOKButtonText(FindBundle.message("find.button")); init(); initByModel(); updateReplaceVisibility(); if (myResultsPreviewTable != null) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { findSettingsChanged(); } }, ModalityState.any()); } } private void updateTitle() { if (myModel.isReplaceState()) { if (myModel.isMultipleFiles()) { setTitle(FindBundle.message("find.replace.in.project.dialog.title")); } else { setTitle(FindBundle.message("find.replace.text.dialog.title")); } } else { setButtonsMargin(null); if (myModel.isMultipleFiles()) { setTitle(FindBundle.message("find.in.path.dialog.title")); } else { setTitle(FindBundle.message("find.text.dialog.title")); } } } @Override protected void dispose() { if (myResultsPreviewSearchProgress != null) myResultsPreviewSearchProgress.cancel(); for (Map.Entry<EditorTextField, DocumentAdapter> e : myComboBoxListeners.entrySet()) { e.getKey().removeDocumentListener(e.getValue()); } myComboBoxListeners.clear(); if (myScopePanel != null) myPreviousResultsExpandedState = myScopePanel.isExpanded(); if (myResultsPreviewTable != null) myPreviousPreviewResultsState = myResultsPreviewPanel.isExpanded(); super.dispose(); } @Override public JComponent getPreferredFocusedComponent() { return myInputComboBox; } @Override protected String getDimensionServiceKey() { return myModel.isReplaceState() ? "replaceTextDialog" : "findTextDialog"; } @NotNull @Override protected Action[] createActions() { if (!myModel.isMultipleFiles() && !myModel.isReplaceState() && myModel.isFindAllEnabled()) { return new Action[] { getFindAllAction(), getOKAction(), getCancelAction(), getHelpAction() }; } return new Action[] { getOKAction(), getCancelAction(), getHelpAction() }; } @NotNull private Action getFindAllAction() { return myFindAllAction = new AbstractAction(FindBundle.message("find.all.button")) { @Override public void actionPerformed(ActionEvent e) { doOKAction(true); } }; } @Override public JComponent createNorthPanel() { JPanel panel = new JPanel(new GridBagLayout()); JLabel prompt = new JLabel(FindBundle.message("find.text.to.find.label")); panel.add(prompt, new GridBagConstraints(0, 0, 1, 1, 0, 1, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, UIUtil.DEFAULT_VGAP, UIUtil.DEFAULT_HGAP), 0, 0)); myInputComboBox = new ComboBox(300); revealWhitespaces(myInputComboBox); initCombobox(myInputComboBox); myReplaceComboBox = new ComboBox(300); revealWhitespaces(myReplaceComboBox); initCombobox(myReplaceComboBox); final Component editorComponent = myReplaceComboBox.getEditor().getEditorComponent(); editorComponent.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { myReplaceComboBox.getEditor().selectAll(); editorComponent.removeFocusListener(this); } }); panel.add(myInputComboBox, new GridBagConstraints(1, 0, 1, 1, 1, 1, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, UIUtil.DEFAULT_VGAP, 0), 0, 0)); prompt.setLabelFor(myInputComboBox.getEditor().getEditorComponent()); myReplacePrompt = new JLabel(FindBundle.message("find.replace.with.label")); panel.add(myReplacePrompt, new GridBagConstraints(0, 1, 1, 1, 0, 1, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, UIUtil.DEFAULT_VGAP, UIUtil.DEFAULT_HGAP), 0, 0)); panel.add(myReplaceComboBox, new GridBagConstraints(1, 1, 1, 1, 1, 1, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, UIUtil.DEFAULT_VGAP, 0), 0, 0)); myReplacePrompt.setLabelFor(myReplaceComboBox.getEditor().getEditorComponent()); return panel; } private void updateReplaceVisibility() { myReplacePrompt.setVisible(myModel.isReplaceState()); myReplaceComboBox.setVisible(myModel.isReplaceState()); if (myCbToSkipResultsWhenOneUsage != null) { myCbToSkipResultsWhenOneUsage.setVisible(!myModel.isReplaceState()); } myCbPreserveCase.setVisible(myModel.isReplaceState()); } private void revealWhitespaces(@NotNull ComboBox comboBox) { ComboBoxEditor comboBoxEditor = new RevealingSpaceComboboxEditor(myProject, comboBox); comboBox.setEditor(comboBoxEditor); comboBox.setRenderer(new EditorComboBoxRenderer(comboBoxEditor)); } private void initCombobox(@NotNull final ComboBox comboBox) { comboBox.setEditable(true); comboBox.setMaximumRowCount(8); comboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { validateFindButton(); } }); Component editorComponent = comboBox.getEditor().getEditorComponent(); if (editorComponent instanceof EditorTextField) { final EditorTextField etf = (EditorTextField) editorComponent; DocumentAdapter listener = new DocumentAdapter() { @Override public void documentChanged(final DocumentEvent e) { handleComboBoxValueChanged(comboBox); } }; etf.addDocumentListener(listener); myComboBoxListeners.put(etf, listener); } else { editorComponent.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { handleComboBoxValueChanged(comboBox); } }); } } private void handleComboBoxValueChanged(@NotNull ComboBox comboBox) { Object item = comboBox.getEditor().getItem(); if (item != null && !item.equals(comboBox.getSelectedItem())) { int caretPosition = getCaretPosition(comboBox); comboBox.setSelectedItem(item); setCaretPosition(comboBox, caretPosition); } findSettingsChanged(); validateFindButton(); } private void findSettingsChanged() { if (myModel.isMultipleFiles() && myResultsPreviewTable != null) { final ModalityState state = ModalityState.current(); if (state == ModalityState.NON_MODAL) return; // skip initial changes if (myResultsPreviewSearchProgress != null && !myResultsPreviewSearchProgress.isCanceled()) { myResultsPreviewSearchProgress.cancel(); } final DefaultTableModel model = new DefaultTableModel(); model.addColumn("Usages"); myResultsPreviewTable.setModel(model); myResultsPreviewTable.getColumnModel().getColumn(0).setCellRenderer(new MyTableCellRenderer()); final FindModel modelClone = myModel.clone(); applyTo(modelClone, false); ValidationInfo result = getValidationInfo(modelClone); if (result != null) return; // todo final PsiDirectory psiDirectory = FindInProjectUtil.getPsiDirectory(modelClone, myProject); final ProgressIndicatorBase progressIndicatorWhenSearchStarted = new ProgressIndicatorBase(); myResultsPreviewSearchProgress = progressIndicatorWhenSearchStarted; myResultsPreviewTable.getEmptyText().setText("Searching..."); final AtomicInteger resultsCount = new AtomicInteger(); ProgressIndicatorUtils.scheduleWithWriteActionPriority(myResultsPreviewSearchProgress, new ReadTask() { @RequiredReadAction @Override public void computeInReadAction(@NotNull ProgressIndicator indicator) { final UsageViewPresentation presentation = FindInProjectUtil.setupViewPresentation( FindSettings.getInstance().isShowResultsInSeparateView(), modelClone); final boolean showPanelIfOnlyOneUsage = !FindSettings.getInstance().isSkipResultsWithOneUsage(); final FindUsagesProcessPresentation processPresentation = FindInProjectUtil .setupProcessPresentation(myProject, showPanelIfOnlyOneUsage, presentation); FindInProjectUtil.findUsages(modelClone, psiDirectory, myProject, new Processor<UsageInfo>() { @Override public boolean process(final UsageInfo info) { final Usage usage = UsageInfo2UsageAdapter.CONVERTER.fun(info); usage.getPresentation().getIcon(); // cache icon ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { model.addRow(new Object[] { usage }); } }, state); return resultsCount.incrementAndGet() < ShowUsagesAction.USAGES_PAGE_SIZE; } }, processPresentation); if (resultsCount.get() == 0) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { if (progressIndicatorWhenSearchStarted == myResultsPreviewSearchProgress) { myResultsPreviewTable.getEmptyText() .setText(UIBundle.message("message.nothingToShow")); } } }, state); } } @Override public void onCanceled(@NotNull ProgressIndicator indicator) { if (progressIndicatorWhenSearchStarted == myResultsPreviewSearchProgress && resultsCount.get() == 0) { myResultsPreviewTable.getEmptyText().setText("Cancelled"); } } }); } } @NotNull public FindModel getModel() { return myModel; } public void setOkHandler(@NotNull Consumer<FindModel> okHandler) { myOkHandler = okHandler; } public void setModel(@NotNull FindModel model) { myModel = model; updateReplaceVisibility(); updateTitle(); } private static int getCaretPosition(@NotNull JComboBox comboBox) { Component editorComponent = comboBox.getEditor().getEditorComponent(); if (editorComponent instanceof JTextField) { JTextField textField = (JTextField) editorComponent; return textField.getCaretPosition(); } return 0; } private static void setCaretPosition(@NotNull JComboBox comboBox, int position) { Component editorComponent = comboBox.getEditor().getEditorComponent(); if (editorComponent instanceof JTextField) { JTextField textField = (JTextField) editorComponent; textField.setCaretPosition(position); } } private void validateFindButton() { boolean okStatus = canSearchThisString() || myRbDirectory != null && myRbDirectory.isSelected() && StringUtil.isEmpty(getDirectory()); setOKStatus(okStatus); } private boolean canSearchThisString() { return !StringUtil.isEmpty(getStringToFind()) || !myModel.isReplaceState() && !myModel.isFindAllEnabled() && getFileTypeMask() != null; } private void setOKStatus(boolean value) { setOKActionEnabled(value); if (myFindAllAction != null) { myFindAllAction.setEnabled(value); } } @Override public JComponent createCenterPanel() { JPanel optionsPanel = new JPanel(); optionsPanel.setLayout(new GridBagLayout()); GridBagConstraints gbConstraints = new GridBagConstraints(); gbConstraints.weightx = 1; gbConstraints.weighty = 1; gbConstraints.fill = GridBagConstraints.BOTH; gbConstraints.gridwidth = GridBagConstraints.REMAINDER; JPanel topOptionsPanel = new JPanel(); topOptionsPanel.setLayout(new GridLayout(1, 2, UIUtil.DEFAULT_HGAP, 0)); topOptionsPanel.add(createFindOptionsPanel()); optionsPanel.add(topOptionsPanel, gbConstraints); JPanel resultsOptionPanel = null; if (myModel.isMultipleFiles()) { optionsPanel.add(createGlobalScopePanel(), gbConstraints); gbConstraints.weightx = 1; gbConstraints.weighty = 1; gbConstraints.fill = GridBagConstraints.HORIZONTAL; gbConstraints.gridwidth = GridBagConstraints.REMAINDER; optionsPanel.add(createFilterPanel(), gbConstraints); myCbToSkipResultsWhenOneUsage = createCheckbox(FindSettings.getInstance().isSkipResultsWithOneUsage(), FindBundle.message("find.options.skip.results.tab.with.one.occurrence.checkbox")); resultsOptionPanel = createResultsOptionPanel(optionsPanel, gbConstraints); resultsOptionPanel.add(myCbToSkipResultsWhenOneUsage); myCbToSkipResultsWhenOneUsage.setVisible(!myModel.isReplaceState()); if (ApplicationManager.getApplication().isInternal() && myModel.isMultipleFiles()) { gbConstraints.weightx = 1; gbConstraints.weighty = 2; gbConstraints.fill = GridBagConstraints.BOTH; JBScrollPane scrollPane = new JBScrollPane(myResultsPreviewTable = new JBTable(), JBScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JBScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); optionsPanel.add(myResultsPreviewPanel = new HideableTitledPanel("Results preview", scrollPane, myPreviousPreviewResultsState), gbConstraints); myResultsPreviewTable.setShowColumns(false); new DoubleClickListener() { @Override protected boolean onDoubleClick(MouseEvent event) { int row = myResultsPreviewTable.getSelectedRow(); Object valueAt = myResultsPreviewTable.getModel().getValueAt(row, 0); if (valueAt instanceof Usage) { doCancelAction(); ((Usage) valueAt).navigate(true); } return true; } }.installOn(myResultsPreviewTable); } } else { JPanel leftOptionsPanel = new JPanel(); leftOptionsPanel.setLayout(new GridLayout(3, 1, 0, 4)); leftOptionsPanel.add(createDirectionPanel()); leftOptionsPanel.add(createOriginPanel()); leftOptionsPanel.add(createScopePanel()); topOptionsPanel.add(leftOptionsPanel); } if (myModel.isOpenInNewTabVisible()) { myCbToOpenInNewTab = new JCheckBox(FindBundle.message("find.open.in.new.tab.checkbox")); myCbToOpenInNewTab.setFocusable(false); myCbToOpenInNewTab.setSelected(myModel.isOpenInNewTab()); myCbToOpenInNewTab.setEnabled(myModel.isOpenInNewTabEnabled()); if (resultsOptionPanel == null) resultsOptionPanel = createResultsOptionPanel(optionsPanel, gbConstraints); resultsOptionPanel.add(myCbToOpenInNewTab); } return optionsPanel; } private JPanel createResultsOptionPanel(JPanel optionsPanel, GridBagConstraints gbConstraints) { JPanel resultsOptionPanel = new JPanel(); resultsOptionPanel.setLayout(new BoxLayout(resultsOptionPanel, BoxLayout.Y_AXIS)); myScopePanel = new HideableTitledPanel(FindBundle.message("results.options.group"), resultsOptionPanel, myPreviousResultsExpandedState); optionsPanel.add(myScopePanel, gbConstraints); return resultsOptionPanel; } @NotNull private JComponent createFilterPanel() { JPanel filterPanel = new JPanel(); filterPanel.setLayout(new BorderLayout()); filterPanel.setBorder( IdeBorderFactory.createTitledBorder(FindBundle.message("find.filter.file.name.group"), true)); myFileFilter = new ComboBox(100); initCombobox(myFileFilter); filterPanel.add(myUseFileFilter = createCheckbox(FindBundle.message("find.filter.file.mask.checkbox")), BorderLayout.WEST); filterPanel.add(myFileFilter, BorderLayout.CENTER); initFileFilter(myFileFilter, myUseFileFilter); myUseFileFilter.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { findSettingsChanged(); validateFindButton(); } }); return filterPanel; } public static void initFileFilter(@NotNull final JComboBox fileFilter, @NotNull final JCheckBox useFileFilter) { fileFilter.setEditable(true); String[] fileMasks = FindSettings.getInstance().getRecentFileMasks(); for (int i = fileMasks.length - 1; i >= 0; i--) { fileFilter.addItem(fileMasks[i]); } fileFilter.setEnabled(false); useFileFilter.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (useFileFilter.isSelected()) { fileFilter.setEnabled(true); fileFilter.getEditor().selectAll(); fileFilter.getEditor().getEditorComponent().requestFocusInWindow(); } else { fileFilter.setEnabled(false); } } }); } @Override public void doOKAction() { doOKAction(false); } private void doOKAction(boolean findAll) { FindModel validateModel = (FindModel) myModel.clone(); applyTo(validateModel, findAll); ValidationInfo validationInfo = getValidationInfo(validateModel); if (validationInfo == null) { myModel.copyFrom(validateModel); updateFindSettings(); super.doOKAction(); myOkHandler.consume(myModel); } else { String message = validationInfo.message; Messages.showMessageDialog(myProject, message, CommonBundle.getErrorTitle(), Messages.getErrorIcon()); } } private void updateFindSettings() { FindSettings findSettings = FindSettings.getInstance(); findSettings.setCaseSensitive(myModel.isCaseSensitive()); if (myModel.isReplaceState()) { findSettings.setPreserveCase(myModel.isPreserveCase()); } findSettings.setWholeWordsOnly(myModel.isWholeWordsOnly()); findSettings.setInStringLiteralsOnly(myModel.isInStringLiteralsOnly()); findSettings.setInCommentsOnly(myModel.isInCommentsOnly()); findSettings.setExceptComments(myModel.isExceptComments()); findSettings.setExceptStringLiterals(myModel.isExceptStringLiterals()); findSettings.setExceptCommentsAndLiterals(myModel.isExceptCommentsAndStringLiterals()); findSettings.setRegularExpressions(myModel.isRegularExpressions()); if (!myModel.isMultipleFiles()) { findSettings.setForward(myModel.isForward()); findSettings.setFromCursor(myModel.isFromCursor()); findSettings.setGlobal(myModel.isGlobal()); } else { String directoryName = myModel.getDirectoryName(); if (directoryName != null && !directoryName.isEmpty()) { findSettings.setWithSubdirectories(myModel.isWithSubdirectories()); } else if (myRbModule.isSelected()) { } else if (myRbCustomScope.isSelected()) { SearchScope selectedScope = myScopeCombo.getSelectedScope(); String customScopeName = selectedScope == null ? null : selectedScope.getDisplayName(); findSettings.setCustomScope(customScopeName); } } if (myCbToSkipResultsWhenOneUsage != null) { findSettings.setSkipResultsWithOneUsage(isSkipResultsWhenOneUsage()); } findSettings.setFileMask(myModel.getFileFilter()); } @Override protected boolean postponeValidation() { return true; } @Nullable("null means OK") private ValidationInfo getValidationInfo(@NotNull FindModel model) { if (myRbDirectory != null && myRbDirectory.isEnabled() && myRbDirectory.isSelected()) { PsiDirectory directory = FindInProjectUtil.getPsiDirectory(model, myProject); if (directory == null) { return new ValidationInfo(FindBundle.message("find.directory.not.found.error", getDirectory()), myDirectoryComboBox); } } if (!canSearchThisString()) { return new ValidationInfo("String to find is empty", myInputComboBox); } if (myCbRegularExpressions != null && myCbRegularExpressions.isSelected() && myCbRegularExpressions.isEnabled()) { String toFind = getStringToFind(); try { boolean isCaseSensitive = myCbCaseSensitive != null && myCbCaseSensitive.isSelected() && myCbCaseSensitive.isEnabled(); Pattern pattern = Pattern.compile(toFind, isCaseSensitive ? Pattern.MULTILINE : Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); if (pattern.matcher("").matches() && !toFind.endsWith("$") && !toFind.startsWith("^")) { return new ValidationInfo(FindBundle.message("find.empty.match.regular.expression.error"), myInputComboBox); } } catch (PatternSyntaxException e) { return new ValidationInfo( FindBundle.message("find.invalid.regular.expression.error", toFind, e.getDescription()), myInputComboBox); } } final String mask = getFileTypeMask(); if (mask != null) { if (mask.isEmpty()) { return new ValidationInfo(FindBundle.message("find.filter.empty.file.mask.error"), myFileFilter); } if (mask.contains(";")) { return new ValidationInfo("File masks should be comma-separated", myFileFilter); } else { try { FindInProjectUtil.createFileMaskRegExp(mask); // verify that the regexp compiles } catch (PatternSyntaxException ex) { return new ValidationInfo(FindBundle.message("find.filter.invalid.file.mask.error", mask), myFileFilter); } } } return null; } @Override protected ValidationInfo doValidate() { FindModel validateModel = (FindModel) myModel.clone(); applyTo(validateModel, false); ValidationInfo result = getValidationInfo(validateModel); setOKStatus(result == null); return result; } @Override public void doHelpAction() { String id = myModel.isReplaceState() ? myModel.isMultipleFiles() ? HelpID.REPLACE_IN_PATH : HelpID.REPLACE_OPTIONS : myModel.isMultipleFiles() ? HelpID.FIND_IN_PATH : HelpID.FIND_OPTIONS; HelpManager.getInstance().invokeHelp(id); } private boolean isSkipResultsWhenOneUsage() { return myCbToSkipResultsWhenOneUsage != null && myCbToSkipResultsWhenOneUsage.isSelected(); } @NotNull private JPanel createFindOptionsPanel() { JPanel findOptionsPanel = new JPanel(); findOptionsPanel .setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.options.group"), true)); findOptionsPanel.setLayout(new BoxLayout(findOptionsPanel, BoxLayout.Y_AXIS)); myCbCaseSensitive = createCheckbox(FindBundle.message("find.options.case.sensitive")); findOptionsPanel.add(myCbCaseSensitive); ChangeListener l = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { findSettingsChanged(); } }; myCbCaseSensitive.addChangeListener(l); myCbPreserveCase = createCheckbox(FindBundle.message("find.options.replace.preserve.case")); myCbPreserveCase.addChangeListener(l); findOptionsPanel.add(myCbPreserveCase); myCbPreserveCase.setVisible(myModel.isReplaceState()); myCbWholeWordsOnly = createCheckbox(FindBundle.message("find.options.whole.words.only")); myCbWholeWordsOnly.addChangeListener(l); findOptionsPanel.add(myCbWholeWordsOnly); myCbRegularExpressions = createCheckbox(FindBundle.message("find.options.regular.expressions")); myCbRegularExpressions.addChangeListener(l); final JPanel regExPanel = new JPanel(); regExPanel.setAlignmentX(Component.LEFT_ALIGNMENT); regExPanel.setLayout(new BoxLayout(regExPanel, BoxLayout.X_AXIS)); regExPanel.add(myCbRegularExpressions); regExPanel.add(RegExHelpPopup.createRegExLink("[Help]", regExPanel, LOG)); findOptionsPanel.add(regExPanel); mySearchContext = new ComboBox(new Object[] { FindBundle.message("find.context.anywhere.scope.label"), FindBundle.message("find.context.in.comments.scope.label"), FindBundle.message("find.context.in.literals.scope.label"), FindBundle.message("find.context.except.comments.scope.label"), FindBundle.message("find.context.except.literals.scope.label"), FindBundle.message("find.context.except.comments.and.literals.scope.label") }); mySearchContext.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { findSettingsChanged(); } }); final JPanel searchContextPanel = new JPanel(new BorderLayout()); searchContextPanel.setAlignmentX(Component.LEFT_ALIGNMENT); JLabel searchContextLabel = new JLabel(FindBundle.message("find.context.combo.label")); searchContextLabel.setLabelFor(mySearchContext); JPanel panel = new JPanel(); panel.setAlignmentX(Component.LEFT_ALIGNMENT); panel.add(searchContextLabel); searchContextPanel.add(panel, BorderLayout.WEST); panel = new JPanel(new BorderLayout()); panel.add(mySearchContext, BorderLayout.NORTH); searchContextPanel.add(panel, BorderLayout.CENTER); findOptionsPanel.add(searchContextPanel); ActionListener actionListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateControls(); } }; myCbRegularExpressions.addActionListener(actionListener); myCbRegularExpressions.addItemListener(new ItemListener() { @Override public void itemStateChanged(final ItemEvent e) { setupRegExpSetting(); } }); myCbCaseSensitive.addActionListener(actionListener); myCbPreserveCase.addActionListener(actionListener); return findOptionsPanel; } private void setupRegExpSetting() { updateFileTypeForEditorComponent(myInputComboBox); if (myReplaceComboBox != null) updateFileTypeForEditorComponent(myReplaceComboBox); } private void updateFileTypeForEditorComponent(@NotNull ComboBox inputComboBox) { final Component editorComponent = inputComboBox.getEditor().getEditorComponent(); if (editorComponent instanceof EditorTextField) { boolean isRegexp = myCbRegularExpressions.isSelectedWhenSelectable(); FileType fileType = PlainTextFileType.INSTANCE; if (isRegexp) { Language regexpLanguage = Language.findLanguageByID("RegExp"); if (regexpLanguage != null) { LanguageFileType regexpFileType = regexpLanguage.getAssociatedFileType(); if (regexpFileType != null) { fileType = regexpFileType; } } } String fileName = isRegexp ? "a.regexp" : "a.txt"; final PsiFile file = PsiFileFactory.getInstance(myProject).createFileFromText(fileName, fileType, ((EditorTextField) editorComponent).getText(), -1, true); ((EditorTextField) editorComponent).setNewDocumentAndFileType(fileType, PsiDocumentManager.getInstance(myProject).getDocument(file)); } } private void updateControls() { if (myCbRegularExpressions.isSelected()) { myCbWholeWordsOnly.makeUnselectable(false); } else { myCbWholeWordsOnly.makeSelectable(); } if (myModel.isReplaceState()) { if (myCbRegularExpressions.isSelected() || myCbCaseSensitive.isSelected()) { myCbPreserveCase.makeUnselectable(false); } else { myCbPreserveCase.makeSelectable(); } if (myCbPreserveCase.isSelected()) { myCbRegularExpressions.makeUnselectable(false); myCbCaseSensitive.makeUnselectable(false); } else { myCbRegularExpressions.makeSelectable(); myCbCaseSensitive.makeSelectable(); } } if (!myModel.isMultipleFiles()) { myRbFromCursor.setEnabled(myRbGlobal.isSelected()); myRbEntireScope.setEnabled(myRbGlobal.isSelected()); } } @NotNull private JPanel createDirectionPanel() { JPanel directionPanel = new JPanel(); directionPanel .setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.direction.group"), true)); directionPanel.setLayout(new BoxLayout(directionPanel, BoxLayout.Y_AXIS)); myRbForward = new JRadioButton(FindBundle.message("find.direction.forward.radio"), true); directionPanel.add(myRbForward); myRbBackward = new JRadioButton(FindBundle.message("find.direction.backward.radio")); directionPanel.add(myRbBackward); ButtonGroup bgDirection = new ButtonGroup(); bgDirection.add(myRbForward); bgDirection.add(myRbBackward); return directionPanel; } @NotNull private JComponent createGlobalScopePanel() { JPanel scopePanel = new JPanel(); scopePanel.setLayout(new GridBagLayout()); scopePanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.scope.group"), true)); GridBagConstraints gbConstraints = new GridBagConstraints(); gbConstraints.fill = GridBagConstraints.HORIZONTAL; gbConstraints.anchor = GridBagConstraints.WEST; gbConstraints.gridx = 0; gbConstraints.gridy = 0; gbConstraints.gridwidth = 3; gbConstraints.weightx = 1; myRbProject = new JRadioButton(FindBundle.message("find.scope.whole.project.radio"), true); scopePanel.add(myRbProject, gbConstraints); ChangeListener l = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { findSettingsChanged(); } }; myRbProject.addChangeListener(l); gbConstraints.gridx = 0; gbConstraints.gridy++; gbConstraints.weightx = 0; gbConstraints.gridwidth = 1; myRbModule = new JRadioButton(FindBundle.message("find.scope.module.radio"), false); scopePanel.add(myRbModule, gbConstraints); myRbModule.addChangeListener(l); gbConstraints.gridx = 1; gbConstraints.gridwidth = 2; gbConstraints.weightx = 1; Module[] modules = ModuleManager.getInstance(myProject).getModules(); String[] names = new String[modules.length]; for (int i = 0; i < modules.length; i++) { names[i] = modules[i].getName(); } Arrays.sort(names, String.CASE_INSENSITIVE_ORDER); myModuleComboBox = new ComboBox(names); myModuleComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { findSettingsChanged(); } }); scopePanel.add(myModuleComboBox, gbConstraints); if (modules.length == 1) { myModuleComboBox.setVisible(false); myRbModule.setVisible(false); } gbConstraints.gridx = 0; gbConstraints.gridy++; gbConstraints.weightx = 0; gbConstraints.gridwidth = 1; myRbDirectory = new JRadioButton(FindBundle.message("find.scope.directory.radio"), false); scopePanel.add(myRbDirectory, gbConstraints); myRbDirectory.addChangeListener(l); gbConstraints.gridx = 1; gbConstraints.weightx = 1; myDirectoryComboBox = new ComboBox(200); Component editorComponent = myDirectoryComboBox.getEditor().getEditorComponent(); if (editorComponent instanceof JTextField) { JTextField field = (JTextField) editorComponent; field.setColumns(40); } initCombobox(myDirectoryComboBox); myDirectoryComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { findSettingsChanged(); } }); scopePanel.add(myDirectoryComboBox, gbConstraints); gbConstraints.weightx = 0; gbConstraints.gridx = 2; mySelectDirectoryButton = new FixedSizeButton(myDirectoryComboBox); TextFieldWithBrowseButton.MyDoClickAction.addTo(mySelectDirectoryButton, myDirectoryComboBox); mySelectDirectoryButton.setMargin(new Insets(0, 0, 0, 0)); scopePanel.add(mySelectDirectoryButton, gbConstraints); gbConstraints.gridx = 0; gbConstraints.gridy++; gbConstraints.weightx = 1; gbConstraints.gridwidth = 3; gbConstraints.insets = new Insets(0, 16, 0, 0); myCbWithSubdirectories = createCheckbox(true, FindBundle.message("find.scope.directory.recursive.checkbox")); myCbWithSubdirectories.setSelected(true); scopePanel.add(myCbWithSubdirectories, gbConstraints); gbConstraints.gridx = 0; gbConstraints.gridy++; gbConstraints.weightx = 0; gbConstraints.gridwidth = 1; gbConstraints.insets = new Insets(0, 0, 0, 0); myRbCustomScope = new JRadioButton(FindBundle.message("find.scope.custom.radio"), false); scopePanel.add(myRbCustomScope, gbConstraints); gbConstraints.gridx++; gbConstraints.weightx = 1; gbConstraints.gridwidth = 2; myScopeCombo = new ScopeChooserCombo(myProject, true, true, FindSettings.getInstance().getDefaultScopeName()); myRbCustomScope.addChangeListener(l); Disposer.register(myDisposable, myScopeCombo); scopePanel.add(myScopeCombo, gbConstraints); ButtonGroup bgScope = new ButtonGroup(); bgScope.add(myRbDirectory); bgScope.add(myRbProject); bgScope.add(myRbModule); bgScope.add(myRbCustomScope); ActionListener validateAll = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { validateScopeControls(); validateFindButton(); } }; myRbProject.addActionListener(validateAll); myRbCustomScope.addActionListener(validateAll); myRbDirectory.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { validateScopeControls(); validateFindButton(); myDirectoryComboBox.getEditor().getEditorComponent().requestFocusInWindow(); } }); myRbModule.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { validateScopeControls(); validateFindButton(); myModuleComboBox.requestFocusInWindow(); } }); mySelectDirectoryButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); FileChooser.chooseFiles(descriptor, myProject, null, new Consumer<List<VirtualFile>>() { @Override public void consume(final List<VirtualFile> files) { myDirectoryComboBox.setSelectedItem(files.get(0).getPresentableUrl()); } }); } }); return scopePanel; } @NotNull private static StateRestoringCheckBox createCheckbox(@NotNull String message) { final StateRestoringCheckBox cb = new StateRestoringCheckBox(message); cb.setFocusable(false); return cb; } @NotNull private static StateRestoringCheckBox createCheckbox(boolean selected, @NotNull String message) { final StateRestoringCheckBox cb = new StateRestoringCheckBox(message, selected); cb.setFocusable(false); return cb; } private void validateScopeControls() { if (myRbDirectory.isSelected()) { myCbWithSubdirectories.makeSelectable(); } else { myCbWithSubdirectories.makeUnselectable(myCbWithSubdirectories.isSelected()); } myDirectoryComboBox.setEnabled(myRbDirectory.isSelected()); mySelectDirectoryButton.setEnabled(myRbDirectory.isSelected()); myModuleComboBox.setEnabled(myRbModule.isSelected()); myScopeCombo.setEnabled(myRbCustomScope.isSelected()); } @NotNull private JPanel createScopePanel() { JPanel scopePanel = new JPanel(); scopePanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.scope.group"), true)); scopePanel.setLayout(new BoxLayout(scopePanel, BoxLayout.Y_AXIS)); myRbGlobal = new JRadioButton(FindBundle.message("find.scope.global.radio"), true); scopePanel.add(myRbGlobal); myRbSelectedText = new JRadioButton(FindBundle.message("find.scope.selected.text.radio")); scopePanel.add(myRbSelectedText); ButtonGroup bgScope = new ButtonGroup(); bgScope.add(myRbGlobal); bgScope.add(myRbSelectedText); ActionListener actionListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateControls(); } }; myRbGlobal.addActionListener(actionListener); myRbSelectedText.addActionListener(actionListener); return scopePanel; } @NotNull private JPanel createOriginPanel() { JPanel originPanel = new JPanel(); originPanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.origin.group"), true)); originPanel.setLayout(new BoxLayout(originPanel, BoxLayout.Y_AXIS)); myRbFromCursor = new JRadioButton(FindBundle.message("find.origin.from.cursor.radio"), true); originPanel.add(myRbFromCursor); myRbEntireScope = new JRadioButton(FindBundle.message("find.origin.entire.scope.radio")); originPanel.add(myRbEntireScope); ButtonGroup bgOrigin = new ButtonGroup(); bgOrigin.add(myRbFromCursor); bgOrigin.add(myRbEntireScope); return originPanel; } @NotNull private String getStringToFind() { String string = (String) myInputComboBox.getEditor().getItem(); return string == null ? "" : string; } @NotNull private String getStringToReplace() { String item = (String) myReplaceComboBox.getEditor().getItem(); return item == null ? "" : item; } private String getDirectory() { return (String) myDirectoryComboBox.getSelectedItem(); } private static void setStringsToComboBox(@NotNull String[] strings, @NotNull ComboBox combo, String selected) { if (combo.getItemCount() > 0) { combo.removeAllItems(); } if (selected != null && selected.indexOf('\n') < 0) { strings = ArrayUtil.remove(strings, selected); // this ensures that last searched string will be selected if selected == "" if (!selected.isEmpty()) strings = ArrayUtil.append(strings, selected); } for (int i = strings.length - 1; i >= 0; i--) { combo.addItem(strings[i]); } } private void setDirectories(@NotNull List<String> strings, String directoryName) { if (myDirectoryComboBox.getItemCount() > 0) { myReplaceComboBox.removeAllItems(); } if (directoryName != null && !directoryName.isEmpty()) { if (strings.contains(directoryName)) { strings.remove(directoryName); } myDirectoryComboBox.addItem(directoryName); } for (int i = strings.size() - 1; i >= 0; i--) { myDirectoryComboBox.addItem(strings.get(i)); } if (myDirectoryComboBox.getItemCount() == 0) { myDirectoryComboBox.addItem(""); } } private void applyTo(@NotNull FindModel model, boolean findAll) { model.setCaseSensitive(myCbCaseSensitive.isSelected()); if (model.isReplaceState()) { model.setPreserveCase(myCbPreserveCase.isSelected()); } model.setWholeWordsOnly(myCbWholeWordsOnly.isSelected()); String selectedSearchContextInUi = (String) mySearchContext.getSelectedItem(); FindModel.SearchContext searchContext = FindModel.SearchContext.ANY; if (FindBundle.message("find.context.in.literals.scope.label").equals(selectedSearchContextInUi)) { searchContext = FindModel.SearchContext.IN_STRING_LITERALS; } else if (FindBundle.message("find.context.in.comments.scope.label").equals(selectedSearchContextInUi)) { searchContext = FindModel.SearchContext.IN_COMMENTS; } else if (FindBundle.message("find.context.except.comments.scope.label") .equals(selectedSearchContextInUi)) { searchContext = FindModel.SearchContext.EXCEPT_COMMENTS; } else if (FindBundle.message("find.context.except.literals.scope.label") .equals(selectedSearchContextInUi)) { searchContext = FindModel.SearchContext.EXCEPT_STRING_LITERALS; } else if (FindBundle.message("find.context.except.comments.and.literals.scope.label") .equals(selectedSearchContextInUi)) { searchContext = FindModel.SearchContext.EXCEPT_COMMENTS_AND_STRING_LITERALS; } model.setSearchContext(searchContext); model.setRegularExpressions(myCbRegularExpressions.isSelected()); String stringToFind = getStringToFind(); model.setStringToFind(stringToFind); if (model.isReplaceState()) { model.setPromptOnReplace(true); model.setReplaceAll(false); String stringToReplace = getStringToReplace(); model.setStringToReplace(StringUtil.convertLineSeparators(stringToReplace)); } if (!model.isMultipleFiles()) { model.setForward(myRbForward.isSelected()); model.setFromCursor(myRbFromCursor.isSelected()); model.setGlobal(myRbGlobal.isSelected()); } else { if (myCbToOpenInNewTab != null) { model.setOpenInNewTab(myCbToOpenInNewTab.isSelected()); } model.setProjectScope(myRbProject.isSelected()); model.setDirectoryName(null); model.setModuleName(null); model.setCustomScopeName(null); model.setCustomScope(null); model.setCustomScope(false); if (myRbDirectory.isSelected()) { String directory = getDirectory(); model.setDirectoryName(directory == null ? "" : directory); model.setWithSubdirectories(myCbWithSubdirectories.isSelected()); } else if (myRbModule.isSelected()) { model.setModuleName((String) myModuleComboBox.getSelectedItem()); } else if (myRbCustomScope.isSelected()) { SearchScope selectedScope = myScopeCombo.getSelectedScope(); String customScopeName = selectedScope == null ? null : selectedScope.getDisplayName(); model.setCustomScopeName(customScopeName); model.setCustomScope(selectedScope == null ? null : selectedScope); model.setCustomScope(true); } } model.setFindAll(findAll); String mask = getFileTypeMask(); model.setFileFilter(mask); } @Nullable private String getFileTypeMask() { String mask = null; if (myUseFileFilter != null && myUseFileFilter.isSelected()) { mask = (String) myFileFilter.getSelectedItem(); } return mask; } private void initByModel() { myCbCaseSensitive.setSelected(myModel.isCaseSensitive()); myCbWholeWordsOnly.setSelected(myModel.isWholeWordsOnly()); String searchContext = FindBundle.message("find.context.anywhere.scope.label"); if (myModel.isInCommentsOnly()) searchContext = FindBundle.message("find.context.in.comments.scope.label"); else if (myModel.isInStringLiteralsOnly()) searchContext = FindBundle.message("find.context.in.literals.scope.label"); else if (myModel.isExceptStringLiterals()) searchContext = FindBundle.message("find.context.except.literals.scope.label"); else if (myModel.isExceptComments()) searchContext = FindBundle.message("find.context.except.comments.scope.label"); else if (myModel.isExceptCommentsAndStringLiterals()) searchContext = FindBundle.message("find.context.except.comments.and.literals.scope.label"); mySearchContext.setSelectedItem(searchContext); myCbRegularExpressions.setSelected(myModel.isRegularExpressions()); if (myModel.isMultipleFiles()) { final String dirName = myModel.getDirectoryName(); setDirectories(FindSettings.getInstance().getRecentDirectories(), dirName); if (!StringUtil.isEmptyOrSpaces(dirName)) { VirtualFile dir = LocalFileSystem.getInstance().findFileByPath(dirName); if (dir != null) { Module module = ModuleUtilCore.findModuleForFile(dir, myProject); if (module != null) { myModuleComboBox.setSelectedItem(module.getName()); } } } if (myModel.isCustomScope()) { myRbCustomScope.setSelected(true); myScopeCombo.setEnabled(true); myCbWithSubdirectories.setEnabled(false); myDirectoryComboBox.setEnabled(false); mySelectDirectoryButton.setEnabled(false); myModuleComboBox.setEnabled(false); } else if (myModel.isProjectScope()) { myRbProject.setSelected(true); myCbWithSubdirectories.setEnabled(false); myDirectoryComboBox.setEnabled(false); mySelectDirectoryButton.setEnabled(false); myModuleComboBox.setEnabled(false); myScopeCombo.setEnabled(false); } else if (dirName != null) { myRbDirectory.setSelected(true); myCbWithSubdirectories.setEnabled(true); myDirectoryComboBox.setEnabled(true); mySelectDirectoryButton.setEnabled(true); myModuleComboBox.setEnabled(false); myScopeCombo.setEnabled(false); } else if (myModel.getModuleName() != null) { myRbModule.setSelected(true); myCbWithSubdirectories.setEnabled(false); myDirectoryComboBox.setEnabled(false); mySelectDirectoryButton.setEnabled(false); myModuleComboBox.setEnabled(true); myModuleComboBox.setSelectedItem(myModel.getModuleName()); myScopeCombo.setEnabled(false); // force showing even if we have only one module myRbModule.setVisible(true); myModuleComboBox.setVisible(true); } else { assert false : myModel; } myCbWithSubdirectories.setSelected(myModel.isWithSubdirectories()); if (myModel.getFileFilter() != null && !myModel.getFileFilter().isEmpty()) { myFileFilter.setSelectedItem(myModel.getFileFilter()); myFileFilter.setEnabled(true); myUseFileFilter.setSelected(true); } } else { if (myModel.isForward()) { myRbForward.setSelected(true); } else { myRbBackward.setSelected(true); } if (myModel.isFromCursor()) { myRbFromCursor.setSelected(true); } else { myRbEntireScope.setSelected(true); } if (myModel.isGlobal()) { myRbGlobal.setSelected(true); } else { myRbSelectedText.setSelected(true); } } setStringsToComboBox(FindSettings.getInstance().getRecentFindStrings(), myInputComboBox, myModel.getStringToFind()); if (myModel.isReplaceState()) { myCbPreserveCase.setSelected(myModel.isPreserveCase()); setStringsToComboBox(FindSettings.getInstance().getRecentReplaceStrings(), myReplaceComboBox, myModel.getStringToReplace()); } updateControls(); } private static class MyTableCellRenderer extends SimpleColoredComponent implements TableCellRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { clear(); if (value instanceof UsageInfo2UsageAdapter) { UsageInfo2UsageAdapter usageAdapter = (UsageInfo2UsageAdapter) value; UsagePresentation presentation = usageAdapter.getPresentation(); TextChunk[] text = presentation.getText(); append(usageAdapter.getFile().getName() + " ", SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); for (TextChunk textChunk : text) { SimpleTextAttributes simples = textChunk.getSimpleAttributesIgnoreBackground(); append(textChunk.getText(), simples); } } return this; } } }