| /* |
| * 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; |
| |
| import com.intellij.featureStatistics.FeatureUsageTracker; |
| import com.intellij.find.editorHeaderActions.*; |
| import com.intellij.find.impl.FindManagerImpl; |
| import com.intellij.find.impl.livePreview.LivePreviewController; |
| import com.intellij.find.impl.livePreview.SearchResults; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.ide.ui.UISettings; |
| import com.intellij.openapi.actionSystem.*; |
| import com.intellij.openapi.actionSystem.ex.CustomComponentAction; |
| import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.event.SelectionEvent; |
| import com.intellij.openapi.editor.event.SelectionListener; |
| import com.intellij.openapi.editor.impl.EditorHeaderComponent; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Getter; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.openapi.util.SystemInfo; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.wm.IdeFocusManager; |
| import com.intellij.ui.*; |
| import com.intellij.ui.components.JBList; |
| import com.intellij.ui.components.JBScrollPane; |
| import com.intellij.ui.components.labels.LinkLabel; |
| import com.intellij.ui.components.labels.LinkListener; |
| import com.intellij.ui.components.panels.NonOpaquePanel; |
| import com.intellij.ui.speedSearch.SpeedSearchSupply; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.DocumentListener; |
| import javax.swing.text.JTextComponent; |
| import java.awt.*; |
| import java.awt.event.*; |
| import java.util.Arrays; |
| import java.util.regex.Pattern; |
| |
| /** |
| * @author max, andrey.zaytsev |
| */ |
| public class EditorSearchComponent extends EditorHeaderComponent implements DataProvider, SelectionListener, SearchResults.SearchResultsListener { |
| |
| private JLabel myMatchInfoLabel; |
| private LinkLabel myClickToHighlightLabel; |
| private final Project myProject; |
| private ActionToolbar myActionsToolbar; |
| |
| @NotNull |
| public Editor getEditor() { |
| return myEditor; |
| } |
| |
| @NotNull |
| private final Editor myEditor; |
| |
| public JTextComponent getSearchField() { |
| return mySearchField; |
| } |
| |
| private final JBSplitter mySplitPane = new JBSplitter(false); |
| private final JPanel myLeftComponent = new JPanel(new BorderLayout()); |
| private final JPanel myRightComponent = new JPanel(new BorderLayout()); |
| |
| { |
| mySplitPane.setBorder(IdeBorderFactory.createEmptyBorder(1, 0, 2, 0)); |
| mySplitPane.setHonorComponentsMinimumSize(true); |
| mySplitPane.setProportion(0.25f); |
| mySplitPane.setAndLoadSplitterProportionKey("FindSplitterProportion"); |
| mySplitPane.setOpaque(false); |
| mySplitPane.getDivider().setOpaque(false); |
| myLeftComponent.setOpaque(false); |
| myRightComponent.setOpaque(false); |
| |
| mySplitPane.setFirstComponent(myLeftComponent); |
| mySplitPane.setSecondComponent(myRightComponent); |
| add(mySplitPane, BorderLayout.NORTH); |
| } |
| |
| private JTextComponent mySearchField; |
| private JComponent mySearchRootComponent; |
| |
| public JTextComponent getReplaceField() { |
| return myReplaceField; |
| } |
| |
| private JTextComponent myReplaceField; |
| private JComponent myReplaceRootComponent; |
| |
| private MyUndoProvider mySearchUndo; |
| private MyUndoProvider myReplaceUndo; |
| |
| private final Getter<JTextComponent> mySearchFieldGetter = new Getter<JTextComponent>() { |
| @Override |
| public JTextComponent get() { |
| return mySearchField; |
| } |
| }; |
| |
| private final Getter<JTextComponent> myReplaceFieldGetter = new Getter<JTextComponent>() { |
| @Override |
| public JTextComponent get() { |
| return myReplaceField; |
| } |
| }; |
| |
| private final Color myDefaultBackground; |
| |
| private JButton myReplaceButton; |
| private JButton myReplaceAllButton; |
| private JButton myExcludeButton; |
| |
| public static final Color COMPLETION_BACKGROUND_COLOR = new Color(235, 244, 254); |
| private static final Color FOCUS_CATCHER_COLOR = new Color(0x9999ff); |
| |
| private JComponent myToolbarComponent; |
| |
| private final LivePreviewController myLivePreviewController; |
| private final SearchResults mySearchResults; |
| |
| private final FindModel myFindModel; |
| private JPanel myReplacementPane; |
| |
| public JComponent getToolbarComponent() { |
| return myToolbarComponent; |
| } |
| |
| private void updateReplaceButton() { |
| if (myReplaceButton != null) { |
| myReplaceButton.setEnabled(canReplaceCurrent()); |
| } |
| } |
| |
| public void restoreFindModel() { |
| final FindModel model = FindManager.getInstance(myProject).getPreviousFindModel(); |
| if (model != null) { |
| myFindModel.copyFrom(model); |
| updateUIWithFindModel(); |
| } |
| } |
| |
| private static FindModel createDefaultFindModel(Project p, Editor e) { |
| FindModel findModel = new FindModel(); |
| findModel.copyFrom(FindManager.getInstance(p).getFindInFileModel()); |
| if (e.getSelectionModel().hasSelection()) { |
| String selectedText = e.getSelectionModel().getSelectedText(); |
| if (selectedText != null) { |
| findModel.setStringToFind(selectedText); |
| } |
| } |
| findModel.setPromptOnReplace(false); |
| return findModel; |
| } |
| |
| public EditorSearchComponent(@NotNull Editor editor, Project project) { |
| this(editor, project, createDefaultFindModel(project, editor)); |
| } |
| |
| @Override |
| @Nullable |
| public Object getData(@NonNls final String dataId) { |
| if (SpeedSearchSupply.SPEED_SEARCH_CURRENT_QUERY.is(dataId)) { |
| return mySearchField.getText(); |
| } |
| if (CommonDataKeys.EDITOR_EVEN_IF_INACTIVE.is(dataId)) { |
| return myEditor; |
| } |
| return null; |
| } |
| |
| @Override |
| public void searchResultsUpdated(SearchResults sr) { |
| if (mySearchField.getText().isEmpty()) { |
| updateUIWithEmptyResults(); |
| } else { |
| int count = sr.getMatchesCount(); |
| if (count <= mySearchResults.getMatchesLimit()) { |
| myClickToHighlightLabel.setVisible(false); |
| |
| if (count > 0) { |
| setRegularBackground(); |
| if (count > 1) { |
| myMatchInfoLabel.setText(count + " matches"); |
| } |
| else { |
| myMatchInfoLabel.setText("1 match"); |
| } |
| } |
| else { |
| setNotFoundBackground(); |
| myMatchInfoLabel.setText("No matches "); |
| boldMatchInfo(); |
| } |
| } |
| else { |
| setRegularBackground(); |
| myMatchInfoLabel.setText("More than " + mySearchResults.getMatchesLimit() + " matches"); |
| myClickToHighlightLabel.setVisible(true); |
| boldMatchInfo(); |
| } |
| } |
| |
| updateExcludeStatus(); |
| } |
| |
| @Override |
| public void cursorMoved() { |
| updateExcludeStatus(); |
| } |
| |
| @Override |
| public void updateFinished() { |
| } |
| |
| public EditorSearchComponent(@NotNull final Editor editor, final Project project, FindModel findModel) { |
| myFindModel = findModel; |
| |
| myProject = project; |
| myEditor = editor; |
| |
| mySearchResults = new SearchResults(myEditor, myProject); |
| myLivePreviewController = new LivePreviewController(mySearchResults, this); |
| |
| myDefaultBackground = new JTextField().getBackground(); |
| |
| configureLeadPanel(); |
| |
| new SwitchToFind(this); |
| new SwitchToReplace(this); |
| |
| myFindModel.addObserver(new FindModel.FindModelObserver() { |
| @Override |
| public void findModelChanged(FindModel findModel) { |
| String stringToFind = myFindModel.getStringToFind(); |
| if (!wholeWordsApplicable(stringToFind)) { |
| myFindModel.setWholeWordsOnly(false); |
| } |
| updateUIWithFindModel(); |
| updateResults(true); |
| syncFindModels(FindManager.getInstance(myProject).getFindInFileModel(), myFindModel); |
| } |
| }); |
| |
| updateUIWithFindModel(); |
| |
| if (ApplicationManager.getApplication().isUnitTestMode()) { |
| initLivePreview(); |
| } |
| } |
| |
| private void configureLeadPanel() { |
| JPanel myLeadPanel = createLeadPane(); |
| myRightComponent.add(myLeadPanel, BorderLayout.WEST); |
| |
| if (mySearchUndo != null) { |
| mySearchUndo.dispose(); |
| } |
| |
| Ref<JComponent> ref = Ref.create(); |
| mySearchField = createTextField(BorderLayout.NORTH, ref); |
| mySearchRootComponent = ref.get(); |
| |
| SearchTextField searchTextField = (ref.get() instanceof SearchTextField) ? (SearchTextField)ref.get() : null; |
| if (searchTextField != null) { |
| setupHistoryToSearchField(searchTextField, FindSettings.getInstance().getRecentFindStrings()); |
| } |
| |
| mySearchUndo = new MyUndoProvider(mySearchField); |
| |
| setupSearchFieldListener(); |
| |
| if (myActionsToolbar == null) { |
| initToolbar(); |
| } |
| |
| JPanel centerPanel = new JPanel(new BorderLayout()); |
| centerPanel.setOpaque(false); |
| centerPanel.add(myToolbarComponent, BorderLayout.CENTER); |
| |
| myRightComponent.add(centerPanel, BorderLayout.CENTER); |
| |
| if (secondaryActionsAvailable()) { |
| if (myToolbarComponent instanceof ActionToolbarImpl) { |
| new ShowMoreOptions(myToolbarComponent, mySearchField); |
| } |
| } |
| |
| |
| JPanel tailPanel = new NonOpaquePanel(new BorderLayout(5, 0)); |
| JPanel tailContainer = new NonOpaquePanel(new BorderLayout(5, 0)); |
| tailContainer.add(tailPanel, BorderLayout.EAST); |
| centerPanel.add(tailContainer, BorderLayout.EAST); |
| |
| JLabel closeLabel = new JLabel(" ", AllIcons.Actions.Cross, SwingConstants.RIGHT); |
| closeLabel.addMouseListener(new MouseAdapter() { |
| @Override |
| public void mousePressed(final MouseEvent e) { |
| close(); |
| } |
| }); |
| |
| closeLabel.setToolTipText("Close search bar (Escape)"); |
| |
| tailPanel.add(closeLabel, BorderLayout.EAST); |
| |
| Utils.setSmallerFont(mySearchField); |
| mySearchField.registerKeyboardAction(new ActionListener() { |
| @Override |
| public void actionPerformed(final ActionEvent e) { |
| if (StringUtil.isEmpty(mySearchField.getText())) { |
| close(); |
| } |
| else { |
| requestFocus(myEditor.getContentComponent()); |
| addTextToRecent(EditorSearchComponent.this.mySearchField); |
| } |
| } |
| }, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, SystemInfo.isMac ? InputEvent.META_DOWN_MASK : InputEvent.CTRL_DOWN_MASK), |
| JComponent.WHEN_FOCUSED); |
| |
| final String initialText = myFindModel.getStringToFind(); |
| |
| ApplicationManager.getApplication().invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| setInitialText(initialText); |
| } |
| }); |
| |
| new RestorePreviousSettingsAction(this, mySearchField); |
| |
| new VariantsCompletionAction(this, mySearchFieldGetter); // It registers a shortcut set automatically on construction |
| Utils.setSmallerFontForChildren(myToolbarComponent); |
| } |
| |
| private void setupHistoryToSearchField(SearchTextField field, String[] strings) { |
| field.setHistorySize(20); |
| field.setHistory(ContainerUtil.reverse(Arrays.asList(strings))); |
| } |
| |
| private void initToolbar() { |
| DefaultActionGroup actionGroup = new DefaultActionGroup("search bar", false); |
| actionGroup.add(new ShowHistoryAction(mySearchFieldGetter, this)); |
| actionGroup.add(new PrevOccurrenceAction(this, mySearchFieldGetter)); |
| actionGroup.add(new NextOccurrenceAction(this, mySearchFieldGetter)); |
| actionGroup.add(new AddOccurrenceAction(this)); |
| actionGroup.add(new RemoveOccurrenceAction(this)); |
| actionGroup.add(new SelectAllAction(this)); |
| actionGroup.add(new FindAllAction(this)); |
| actionGroup.add(new ToggleMultiline(this)); |
| actionGroup.add(new ToggleMatchCase(this)); |
| actionGroup.add(new ToggleRegex(this)); |
| |
| myMatchInfoLabel = new JLabel(); |
| |
| myClickToHighlightLabel = new LinkLabel("Click to highlight", null, new LinkListener() { |
| @Override |
| public void linkSelected(LinkLabel aSource, Object aLinkData) { |
| setMatchesLimit(Integer.MAX_VALUE); |
| updateResults(true); |
| } |
| }); |
| myClickToHighlightLabel.setVisible(false); |
| |
| myActionsToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, actionGroup, true); |
| myActionsToolbar.setSecondaryActionsTooltip("More Options(" + ShowMoreOptions.SHORT_CUT + ")"); |
| |
| actionGroup.addAction(new ToggleWholeWordsOnlyAction(this)); |
| if (secondaryActionsAvailable()) { |
| actionGroup.addAction(new ToggleInCommentsAction(this)).setAsSecondary(true); |
| actionGroup.addAction(new ToggleInLiteralsOnlyAction(this)).setAsSecondary(true); |
| actionGroup.addAction(new ToggleExceptCommentsAction(this)).setAsSecondary(true); |
| actionGroup.addAction(new ToggleExceptLiteralsAction(this)).setAsSecondary(true); |
| actionGroup.addAction(new ToggleExceptCommentsAndLiteralsAction(this)).setAsSecondary(true); |
| } |
| actionGroup.addAction(new TogglePreserveCaseAction(this)); |
| actionGroup.addAction(new ToggleSelectionOnlyAction(this)); |
| |
| class MyCustomComponentDoNothingAction extends AnAction implements CustomComponentAction { |
| private final JComponent c; |
| |
| MyCustomComponentDoNothingAction(JComponent c) { |
| this.c = c; |
| c.setBorder(IdeBorderFactory.createEmptyBorder(new Insets(0, 10, 0, 0))); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| } |
| |
| @Override |
| public JComponent createCustomComponent(Presentation presentation) { |
| return c; |
| } |
| } |
| actionGroup.add(new MyCustomComponentDoNothingAction(myMatchInfoLabel)); |
| actionGroup.add(new MyCustomComponentDoNothingAction(myClickToHighlightLabel)); |
| |
| myActionsToolbar.setLayoutPolicy(ActionToolbar.AUTO_LAYOUT_POLICY); |
| myToolbarComponent = myActionsToolbar.getComponent(); |
| myToolbarComponent.setBorder(null); |
| myToolbarComponent.setOpaque(false); |
| } |
| |
| public void selectAllText() { |
| mySearchField.selectAll(); |
| } |
| |
| private boolean secondaryActionsAvailable() { |
| return FindManagerImpl.ourHasSearchInCommentsAndLiterals; |
| } |
| |
| private void setupSearchFieldListener() { |
| mySearchField.getDocument().addDocumentListener(new DocumentListener() { |
| @Override |
| public void insertUpdate(javax.swing.event.DocumentEvent documentEvent) { |
| searchFieldDocumentChanged(); |
| } |
| |
| @Override |
| public void removeUpdate(javax.swing.event.DocumentEvent documentEvent) { |
| searchFieldDocumentChanged(); |
| } |
| |
| @Override |
| public void changedUpdate(javax.swing.event.DocumentEvent documentEvent) { |
| searchFieldDocumentChanged(); |
| } |
| }); |
| } |
| |
| private void searchFieldDocumentChanged() { |
| setMatchesLimit(LivePreviewController.MATCHES_LIMIT); |
| String text = mySearchField.getText(); |
| myFindModel.setStringToFind(text); |
| if (!StringUtil.isEmpty(text)) { |
| updateResults(true); |
| } |
| else { |
| nothingToSearchFor(); |
| } |
| } |
| |
| public boolean isRegexp() { |
| return myFindModel.isRegularExpressions(); |
| } |
| |
| public void setRegexp(boolean val) { |
| myFindModel.setRegularExpressions(val); |
| } |
| |
| public FindModel getFindModel() { |
| return myFindModel; |
| } |
| |
| private static void syncFindModels(FindModel to, FindModel from) { |
| to.setCaseSensitive(from.isCaseSensitive()); |
| to.setWholeWordsOnly(from.isWholeWordsOnly()); |
| to.setRegularExpressions(from.isRegularExpressions()); |
| to.setSearchContext(from.getSearchContext()); |
| if (from.isReplaceState()) { |
| to.setPreserveCase(from.isPreserveCase()); |
| } |
| } |
| |
| private void updateUIWithFindModel() { |
| boolean needToResetFocus = false; |
| myActionsToolbar.updateActionsImmediately(); |
| |
| if ((myFindModel.isMultiline() && mySearchField instanceof JTextField) || (!myFindModel.isMultiline() && mySearchField instanceof JTextArea)) { |
| needToResetFocus = mySearchField.hasFocus(); |
| myLeftComponent.removeAll(); |
| myRightComponent.removeAll(); |
| myReplaceRootComponent = null; |
| mySearchRootComponent = null; |
| configureLeadPanel(); |
| if (myReplacementPane != null) { |
| myReplacementPane = null; |
| } |
| } |
| |
| String stringToFind = myFindModel.getStringToFind(); |
| |
| if (!StringUtil.equals(stringToFind, mySearchField.getText())) { |
| mySearchField.setText(stringToFind); |
| } |
| |
| myLivePreviewController.setTrackingSelection(!myFindModel.isGlobal()); |
| |
| if (myFindModel.isReplaceState() && myReplacementPane == null) { |
| configureReplacementPane(); |
| } else if (!myFindModel.isReplaceState() && myReplacementPane != null) { |
| |
| if (myReplaceRootComponent != null) { |
| myLeftComponent.remove(myReplaceRootComponent); |
| myReplaceRootComponent = null; |
| myReplaceField = null; |
| } |
| |
| myRightComponent.remove(myReplacementPane); |
| myReplacementPane = null; |
| } |
| if (myFindModel.isReplaceState()) { |
| String stringToReplace = myFindModel.getStringToReplace(); |
| if (!StringUtil.equals(stringToReplace, myReplaceField.getText())) { |
| myReplaceField.setText(stringToReplace); |
| } |
| updateExcludeStatus(); |
| } |
| |
| updateReplaceButton(); |
| Utils.setSmallerFontForChildren(myToolbarComponent); |
| revalidate(); |
| if (needToResetFocus) mySearchField.requestFocusInWindow(); |
| } |
| |
| private static boolean wholeWordsApplicable(String stringToFind) { |
| return !stringToFind.startsWith(" ") && |
| !stringToFind.startsWith("\t") && |
| !stringToFind.endsWith(" ") && |
| !stringToFind.endsWith("\t"); |
| } |
| |
| private void setMatchesLimit(int value) { |
| mySearchResults.setMatchesLimit(value); |
| } |
| |
| private void configureReplacementPane() { |
| myReplacementPane = new NonOpaquePanel(new FlowLayout(FlowLayout.LEFT, 5, 0)); |
| |
| if (myReplaceUndo != null) { |
| myReplaceUndo.dispose(); |
| } |
| |
| Ref<JComponent> ref = Ref.create(); |
| myReplaceField = createTextField(BorderLayout.SOUTH, ref); |
| myReplaceRootComponent = ref.get(); |
| |
| SearchTextField searchTextField = ref.get() instanceof SearchTextField ? (SearchTextField)ref.get() : null; |
| if (searchTextField != null) { |
| setupHistoryToSearchField(searchTextField, FindSettings.getInstance().getRecentReplaceStrings()); |
| } |
| myReplaceUndo = new MyUndoProvider(myReplaceField); |
| |
| revalidate(); |
| |
| DocumentListener replaceFieldListener = new DocumentListener() { |
| @Override |
| public void insertUpdate(javax.swing.event.DocumentEvent documentEvent) { |
| replaceFieldDocumentChanged(); |
| } |
| |
| @Override |
| public void removeUpdate(javax.swing.event.DocumentEvent documentEvent) { |
| replaceFieldDocumentChanged(); |
| } |
| |
| @Override |
| public void changedUpdate(javax.swing.event.DocumentEvent documentEvent) { |
| replaceFieldDocumentChanged(); |
| } |
| }; |
| myReplaceField.getDocument().addDocumentListener(replaceFieldListener); |
| |
| if (!getFindModel().isMultiline()) { |
| new ReplaceOnEnterAction(this, myReplaceField); |
| } |
| |
| myReplaceField.setText(myFindModel.getStringToReplace()); |
| myRightComponent.add(myReplacementPane, BorderLayout.SOUTH); |
| |
| myReplaceButton = new JButton("Replace"); |
| myReplaceButton.setFocusable(false); |
| myReplaceButton.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent actionEvent) { |
| replaceCurrent(); |
| } |
| }); |
| |
| myReplaceAllButton = new JButton("Replace all"); |
| myReplaceAllButton.setFocusable(false); |
| myReplaceAllButton.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent actionEvent) { |
| myLivePreviewController.performReplaceAll(); |
| } |
| }); |
| |
| myExcludeButton = new JButton(""); |
| myExcludeButton.setFocusable(false); |
| myExcludeButton.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent actionEvent) { |
| myLivePreviewController.exclude(); |
| moveCursor(SearchResults.Direction.DOWN); |
| } |
| }); |
| |
| if (!UISettings.getInstance().DISABLE_MNEMONICS_IN_CONTROLS) { |
| myReplaceButton.setMnemonic('p'); |
| myReplaceAllButton.setMnemonic('a'); |
| myExcludeButton.setMnemonic('l'); |
| } |
| |
| |
| ActionGroup actionsGroup = new DefaultActionGroup(new ShowHistoryAction(myReplaceFieldGetter, this)); |
| final ActionToolbar tb = ActionManager.getInstance().createActionToolbar("ReplaceBar", actionsGroup, true); |
| tb.setLayoutPolicy(ActionToolbar.AUTO_LAYOUT_POLICY); |
| final JComponent tbComponent = tb.getComponent(); |
| tbComponent.setOpaque(false); |
| tbComponent.setBorder(null); |
| myReplacementPane.add(tbComponent); |
| |
| myReplacementPane.add(myReplaceButton); |
| |
| myReplacementPane.add(myReplaceAllButton); |
| myReplacementPane.add(myExcludeButton); |
| |
| setSmallerFontAndOpaque(myReplaceButton); |
| setSmallerFontAndOpaque(myReplaceAllButton); |
| setSmallerFontAndOpaque(myExcludeButton); |
| |
| Utils.setSmallerFont(myReplaceField); |
| new VariantsCompletionAction(this, myReplaceFieldGetter); |
| new NextOccurrenceAction(this, myReplaceFieldGetter); |
| new PrevOccurrenceAction(this, myReplaceFieldGetter); |
| } |
| |
| private void replaceFieldDocumentChanged() { |
| setMatchesLimit(LivePreviewController.MATCHES_LIMIT); |
| myFindModel.setStringToReplace(myReplaceField.getText()); |
| } |
| |
| private boolean canReplaceCurrent() { |
| return myLivePreviewController != null && myLivePreviewController.canReplace(); |
| } |
| |
| public void replaceCurrent() { |
| if (mySearchResults.getCursor() != null) { |
| myLivePreviewController.performReplace(); |
| } |
| } |
| |
| private void updateExcludeStatus() { |
| if (myExcludeButton != null && mySearchResults != null) { |
| FindResult cursor = mySearchResults.getCursor(); |
| myExcludeButton.setText(cursor == null || !mySearchResults.isExcluded(cursor) ? "Exclude" : "Include"); |
| myReplaceAllButton.setEnabled(mySearchResults.hasMatches()); |
| myExcludeButton.setEnabled(cursor != null); |
| updateReplaceButton(); |
| } |
| } |
| |
| private static JPanel createLeadPane() { |
| return new NonOpaquePanel(new BorderLayout()); |
| } |
| |
| public void showHistory(final boolean byClickingToolbarButton, JTextComponent textField) { |
| FeatureUsageTracker.getInstance().triggerFeatureUsed("find.recent.search"); |
| FindSettings settings = FindSettings.getInstance(); |
| String[] recent = textField == mySearchField ? settings.getRecentFindStrings() : settings.getRecentReplaceStrings(); |
| final boolean toShowAd = textField == mySearchField && textField.getText().isEmpty() && FindManager.getInstance(myProject).getPreviousFindModel() != null; |
| Utils.showCompletionPopup(byClickingToolbarButton ? myToolbarComponent : null, |
| new JBList((Object[])ArrayUtil.reverseArray(recent)), |
| "Recent " + (textField == mySearchField ? "Searches" : "Replaces"), |
| textField, |
| toShowAd ? RestorePreviousSettingsAction.getAd() : null); |
| } |
| |
| private String gerRestoreFindModelAd() { |
| return "Use " + KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0) + " to restore your last search/replace settings"; |
| } |
| |
| private void paintBorderOfTextField(Graphics g) { |
| if (!(UIUtil.isUnderAquaLookAndFeel() || UIUtil.isUnderGTKLookAndFeel() || UIUtil.isUnderNimbusLookAndFeel()) && |
| isFocusOwner()) { |
| final Rectangle bounds = getBounds(); |
| g.setColor(FOCUS_CATCHER_COLOR); |
| g.drawRect(0, 0, bounds.width - 1, bounds.height - 1); |
| } |
| } |
| |
| private JTextComponent createTextField(Object constraint, Ref<JComponent> componentRef) { |
| final JTextComponent editorTextField; |
| if (myFindModel.isMultiline()) { |
| editorTextField = new JTextArea("") { |
| @Override |
| protected void paintBorder(final Graphics g) { |
| super.paintBorder(g); |
| paintBorderOfTextField(g); |
| } |
| }; |
| ((JTextArea)editorTextField).setColumns(25); |
| ((JTextArea)editorTextField).setRows(3); |
| final JScrollPane scrollPane = new JBScrollPane(editorTextField, |
| ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, |
| ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); |
| myLeftComponent.add(scrollPane, constraint); |
| componentRef.set(scrollPane); |
| } |
| else { |
| SearchTextField stf = new SearchTextField(true); |
| stf.setOpaque(false); |
| editorTextField = stf.getTextEditor(); |
| if (UIUtil.isUnderGTKLookAndFeel()) { |
| editorTextField.setOpaque(false); |
| } |
| myLeftComponent.add(stf, constraint); |
| componentRef.set(stf); |
| } |
| editorTextField.setMinimumSize(new Dimension(200, -1)); |
| editorTextField.putClientProperty("AuxEditorComponent", Boolean.TRUE); |
| |
| editorTextField.addFocusListener(new FocusListener() { |
| @Override |
| public void focusGained(final FocusEvent e) { |
| editorTextField.repaint(); |
| } |
| |
| @Override |
| public void focusLost(final FocusEvent e) { |
| editorTextField.repaint(); |
| } |
| }); |
| new CloseOnESCAction(this, editorTextField); |
| return editorTextField; |
| } |
| |
| |
| public void setInitialText(final String initialText) { |
| final String text = initialText != null ? initialText : ""; |
| if (text.contains("\n")) { |
| myFindModel.setMultiline(true); |
| } |
| setTextInField(text); |
| mySearchField.selectAll(); |
| } |
| |
| private void requestFocus(Component c) { |
| IdeFocusManager.getInstance(myProject).requestFocus(c, true); |
| } |
| |
| public void searchBackward() { |
| moveCursor(SearchResults.Direction.UP); |
| addTextToRecent(mySearchField); |
| } |
| |
| public void searchForward() { |
| moveCursor(SearchResults.Direction.DOWN); |
| addTextToRecent(mySearchField); |
| } |
| |
| public void addTextToRecent(JTextComponent textField) { |
| final String text = textField.getText(); |
| if (text.length() > 0) { |
| if (textField == mySearchField) { |
| FindSettings.getInstance().addStringToFind(text); |
| if (mySearchRootComponent instanceof SearchTextField) { |
| ((SearchTextField)mySearchRootComponent).addCurrentTextToHistory(); |
| } |
| } else { |
| FindSettings.getInstance().addStringToReplace(text); |
| if (myReplaceRootComponent instanceof SearchTextField) { |
| ((SearchTextField)myReplaceRootComponent).addCurrentTextToHistory(); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void selectionChanged(SelectionEvent e) { |
| updateResults(false); |
| } |
| |
| private void moveCursor(SearchResults.Direction direction) { |
| myLivePreviewController.moveCursor(direction); |
| } |
| |
| private static void setSmallerFontAndOpaque(final JComponent component) { |
| Utils.setSmallerFont(component); |
| component.setOpaque(false); |
| } |
| |
| @Override |
| public void requestFocus() { |
| mySearchField.setSelectionStart(0); |
| mySearchField.setSelectionEnd(mySearchField.getText().length()); |
| requestFocus(mySearchField); |
| } |
| |
| public void close() { |
| IdeFocusManager.getInstance(myProject).requestFocus(myEditor.getContentComponent(), false); |
| |
| myLivePreviewController.dispose(); |
| |
| if (mySearchUndo != null) { |
| mySearchUndo.dispose(); |
| } |
| if (myReplaceUndo != null){ |
| myReplaceUndo.dispose(); |
| } |
| myEditor.setHeaderComponent(null); |
| } |
| |
| @Override |
| public void addNotify() { |
| super.addNotify(); |
| initLivePreview(); |
| } |
| |
| private void initLivePreview() { |
| myLivePreviewController.on(); |
| |
| myLivePreviewController.setUserActivityDelay(0); |
| updateResults(false); |
| myLivePreviewController.setUserActivityDelay(LivePreviewController.USER_ACTIVITY_TRIGGERING_DELAY); |
| |
| mySearchResults.addListener(this); |
| } |
| |
| @Override |
| public void removeNotify() { |
| super.removeNotify(); |
| |
| myLivePreviewController.off(); |
| mySearchResults.removeListener(this); |
| |
| addTextToRecent(mySearchField); |
| if (myReplaceField != null) { |
| addTextToRecent(myReplaceField); |
| } |
| } |
| |
| private void updateResults(final boolean allowedToChangedEditorSelection) { |
| myMatchInfoLabel.setFont(myMatchInfoLabel.getFont().deriveFont(Font.PLAIN)); |
| final String text = myFindModel.getStringToFind(); |
| if (text.length() == 0) { |
| nothingToSearchFor(); |
| } |
| else { |
| |
| if (myFindModel.isRegularExpressions()) { |
| try { |
| Pattern.compile(text); |
| } |
| catch (Exception e) { |
| setNotFoundBackground(); |
| myMatchInfoLabel.setText("Incorrect regular expression"); |
| boldMatchInfo(); |
| myClickToHighlightLabel.setVisible(false); |
| mySearchResults.clear(); |
| return; |
| } |
| } |
| |
| |
| final FindManager findManager = FindManager.getInstance(myProject); |
| if (allowedToChangedEditorSelection) { |
| findManager.setFindWasPerformed(); |
| FindModel copy = new FindModel(); |
| copy.copyFrom(myFindModel); |
| copy.setReplaceState(false); |
| findManager.setFindNextModel(copy); |
| } |
| if (myLivePreviewController != null) { |
| myLivePreviewController.updateInBackground(myFindModel, allowedToChangedEditorSelection); |
| } |
| } |
| } |
| |
| private void nothingToSearchFor() { |
| updateUIWithEmptyResults(); |
| if (mySearchResults != null) { |
| mySearchResults.clear(); |
| } |
| } |
| |
| private void updateUIWithEmptyResults() { |
| setRegularBackground(); |
| myMatchInfoLabel.setText(""); |
| myClickToHighlightLabel.setVisible(false); |
| } |
| |
| private void boldMatchInfo() { |
| Font font = myMatchInfoLabel.getFont(); |
| if (!font.isBold()) { |
| myMatchInfoLabel.setFont(font.deriveFont(Font.BOLD)); |
| } |
| } |
| |
| private void setRegularBackground() { |
| mySearchField.setBackground(myDefaultBackground); |
| } |
| |
| private void setNotFoundBackground() { |
| mySearchField.setBackground(LightColors.RED); |
| } |
| |
| public String getTextInField() { |
| return mySearchField.getText(); |
| } |
| |
| public void setTextInField(final String text) { |
| mySearchField.setText(text); |
| myFindModel.setStringToFind(text); |
| } |
| |
| public boolean hasMatches() { |
| return mySearchResults != null && mySearchResults.hasMatches(); |
| } |
| |
| @Override |
| public Insets getInsets() { |
| Insets insets = super.getInsets(); |
| if (UIUtil.isUnderGTKLookAndFeel() || UIUtil.isUnderNimbusLookAndFeel()) { |
| insets.top += 1; |
| insets.bottom += 2; |
| } |
| return insets; |
| } |
| |
| public void selectAllOccurrences() { |
| FindUtil.selectSearchResultsInEditor(myEditor, mySearchResults.getOccurrences().iterator(), -1); |
| } |
| |
| public void removeOccurrence() { |
| mySearchResults.prevOccurrence(true); |
| } |
| |
| public void addNextOccurrence() { |
| mySearchResults.nextOccurrence(true); |
| } |
| |
| private static class MyUndoProvider extends TextComponentUndoProvider { |
| private boolean myEnabled = true; |
| public MyUndoProvider(JTextComponent textComponent) { |
| super(textComponent); |
| textComponent.getDocument().addDocumentListener(new com.intellij.ui.DocumentAdapter() { |
| @Override |
| protected void textChanged(javax.swing.event.DocumentEvent e) { |
| myEnabled = true; |
| } |
| }); |
| } |
| |
| @Override |
| protected boolean canUndo() { |
| return super.canUndo() && myEnabled; |
| } |
| |
| @Override |
| protected boolean canRedo() { |
| return super.canRedo() && myEnabled; |
| } |
| |
| public void disable() { |
| myEnabled = false; |
| myUndoManager.discardAllEdits(); |
| } |
| } |
| |
| public void clearUndoInTextFields() { |
| myReplaceUndo.disable(); |
| mySearchUndo.disable(); |
| } |
| } |