blob: 4d7eee35a4628e63762e8bd85bc091cfec611fda [file] [log] [blame]
/*
* 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();
}
}