| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.netbeans.modules.editor.search; |
| |
| import java.awt.*; |
| import java.awt.event.*; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.util.List; |
| import java.util.*; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import javax.swing.*; |
| import javax.swing.event.PopupMenuEvent; |
| import javax.swing.event.PopupMenuListener; |
| import javax.swing.text.BadLocationException; |
| import javax.swing.text.JTextComponent; |
| import org.netbeans.editor.GuardedException; |
| import org.openide.awt.Mnemonics; |
| import org.openide.util.NbBundle; |
| import org.openide.util.WeakListeners; |
| |
| public final class ReplaceBar extends JPanel implements PropertyChangeListener { |
| |
| private static ReplaceBar replacebarInstance = null; |
| private static final Logger LOG = Logger.getLogger(ReplaceBar.class.getName()); |
| private SearchBar searchBar; |
| private final JComboBox<String> replaceComboBox; |
| private final JTextComponent replaceTextField; |
| private final JButton replaceButton; |
| private final JButton replaceAllButton; |
| private final JLabel replaceLabel; |
| private final JCheckBox preserveCaseCheckBox; |
| private final JCheckBox backwardsCheckBox; |
| private final FocusTraversalPolicy searchBarFocusTraversalPolicy; |
| private final List<Component> focusList = new ArrayList<>(); |
| private boolean popupMenuWasCanceled = false; |
| |
| public static ReplaceBar getInstance(SearchBar searchBar) { |
| if (replacebarInstance == null) { |
| replacebarInstance = new ReplaceBar(searchBar); |
| } |
| if (replacebarInstance.getSearchBar() != searchBar) { |
| replacebarInstance.setSearchBar(searchBar); |
| } |
| return replacebarInstance; |
| } |
| |
| private ReplaceBar(SearchBar searchBar) { |
| setSearchBar(searchBar); |
| addEscapeKeystrokeFocusBackTo(this); |
| |
| setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); |
| setFocusCycleRoot(true); |
| setForeground(UIManager.getColor("textText")); //NOI18N |
| |
| // padding at the end of the toolbar |
| add(Box.createHorizontalStrut(8)); //spacer in the beginnning of the toolbar |
| SearchComboBox<String> scb = new SearchComboBox<>(); |
| replaceComboBox = scb; |
| replaceComboBox.addPopupMenuListener(new ReplacePopupMenuListener()); |
| replaceTextField = scb.getEditorPane(); |
| replaceTextField.setToolTipText(NbBundle.getMessage(ReplaceBar.class, "TOOLTIP_ReplaceText")); // NOI18N |
| replaceTextField.addFocusListener(new FocusAdapter() { |
| |
| @Override |
| public void focusGained(FocusEvent e) { |
| getSearchBar().lostFocusOnTextField(); |
| } |
| }); |
| addEnterKeystrokeReplaceTo(replaceTextField); |
| addShiftEnterReplaceAllTo(replaceTextField); |
| |
| replaceLabel = new JLabel(); |
| Mnemonics.setLocalizedText(replaceLabel, NbBundle.getMessage(ReplaceBar.class, "CTL_Replace")); // NOI18N |
| replaceLabel.setLabelFor(replaceTextField); |
| add(replaceLabel); |
| add(replaceComboBox); |
| |
| final JToolBar.Separator leftSeparator = new JToolBar.Separator(); |
| leftSeparator.setOrientation(SwingConstants.VERTICAL); |
| add(leftSeparator); |
| |
| replaceButton = SearchButton.createButton("org/netbeans/modules/editor/search/resources/replace.png", "CTL_ReplaceNext"); // NOI18N |
| replaceButton.setToolTipText(NbBundle.getMessage(ReplaceBar.class, "TOOLTIP_ReplaceText")); // NOI18N |
| replaceButton.setEnabled(!getSearchBar().getIncSearchTextField().getText().isEmpty()); |
| replaceButton.addActionListener(new ActionListener() { |
| |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| replace(); |
| } |
| }); |
| add(replaceButton); |
| |
| replaceAllButton = SearchButton.createButton("org/netbeans/modules/editor/search/resources/replace_all.png", "CTL_ReplaceAll"); // NOI18N |
| replaceAllButton.setToolTipText(NbBundle.getMessage(ReplaceBar.class, "TOOLTIP_ReplaceText")); // NOI18N |
| replaceAllButton.setEnabled(!getSearchBar().getIncSearchTextField().getText().isEmpty()); |
| replaceAllButton.addActionListener(new ActionListener() { |
| |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| replaceAll(); |
| } |
| }); |
| add(replaceAllButton); |
| |
| changeButtonsSizeAsSearchBarButtons(); |
| searchBar.addCheckBoxesActions(replaceTextField); |
| |
| final JToolBar.Separator rightSeparator = new JToolBar.Separator(); |
| rightSeparator.setOrientation(SwingConstants.VERTICAL); |
| add(rightSeparator); |
| |
| backwardsCheckBox = searchBar.createCheckBox("CTL_BackwardsReplace", EditorFindSupport.FIND_BACKWARD_SEARCH); // NOI18N |
| add(backwardsCheckBox); |
| preserveCaseCheckBox = searchBar.createCheckBox("CTL_PreserveCase", EditorFindSupport.FIND_PRESERVE_CASE); // NOI18N |
| preserveCaseCheckBox.setToolTipText(NbBundle.getMessage(ReplaceBar.class, "TOOLTIP_PreserveCase")); // NOI18N |
| add(preserveCaseCheckBox); |
| |
| backwardsCheckBox.setSelected(searchBar.getFindSupportValue(EditorFindSupport.FIND_BACKWARD_SEARCH)); |
| preserveCaseCheckBox.setSelected(searchBar.getFindSupportValue(EditorFindSupport.FIND_PRESERVE_CASE)); |
| preserveCaseCheckBox.setEnabled(!searchBar.getRegExp() && !searchBar.getFindSupportValue(EditorFindSupport.FIND_MATCH_CASE)); |
| |
| // padding at the end of the toolbar |
| add(Box.createHorizontalGlue()); |
| |
| focusList.clear(); |
| focusList.add(searchBar.getIncSearchTextField()); |
| focusList.add(replaceTextField); |
| searchBarFocusTraversalPolicy = new ListFocusTraversalPolicy(focusList); |
| |
| EditorFindSupport.getInstance().addPropertyChangeListener(WeakListeners.propertyChange(this, EditorFindSupport.getInstance())); |
| setVisible(false); |
| } |
| |
| private SearchBar getSearchBar() { |
| return searchBar; |
| } |
| |
| private void setSearchBar(SearchBar searchBar) { |
| this.searchBar = searchBar; |
| } |
| |
| public JButton getReplaceButton() { |
| return replaceButton; |
| } |
| |
| public JButton getReplaceAllButton() { |
| return replaceAllButton; |
| } |
| |
| public JTextComponent getReplaceTextField() { |
| return replaceTextField; |
| } |
| |
| @Override |
| public Dimension getPreferredSize() { |
| return super.getPreferredSize(); |
| } |
| |
| private void changeButtonsSizeAsSearchBarButtons() { |
| int replaceBarButtonsSize = replaceButton.getPreferredSize().width + replaceAllButton.getPreferredSize().width; |
| int searchBarButtonsSize = searchBar.getFindNextButton().getPreferredSize().width + searchBar.getFindPreviousButton().getPreferredSize().width; |
| int diffButtonsSize = (searchBarButtonsSize - replaceBarButtonsSize) / 2; |
| int diffEven = diffButtonsSize % 2 == 0 ? 0 : 1; |
| if (diffButtonsSize > 0) { |
| replaceButton.setPreferredSize(new Dimension(replaceButton.getPreferredSize().width + diffButtonsSize + diffEven, replaceButton.getPreferredSize().height)); |
| replaceAllButton.setPreferredSize(new Dimension(replaceAllButton.getPreferredSize().width + diffButtonsSize, replaceAllButton.getPreferredSize().height)); |
| } else { |
| searchBar.getFindNextButton().setPreferredSize(new Dimension(searchBar.getFindNextButton().getPreferredSize().width - diffButtonsSize + diffEven, searchBar.getFindNextButton().getPreferredSize().height)); |
| searchBar.getFindPreviousButton().setPreferredSize(new Dimension(searchBar.getFindPreviousButton().getPreferredSize().width - diffButtonsSize, searchBar.getFindPreviousButton().getPreferredSize().height)); |
| } |
| } |
| |
| private void addEnterKeystrokeReplaceTo(JTextComponent replaceTextField) { |
| replaceTextField.getInputMap().put( |
| KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true), |
| "replace-next"); // NOI18N |
| replaceTextField.getActionMap().put("replace-next", // NOI18N |
| new AbstractAction() { |
| |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| if (!popupMenuWasCanceled && !searchBar.isPopupMenuWasCanceled()) { |
| replace(); |
| } else { |
| popupMenuWasCanceled = false; |
| searchBar.setPopupMenuWasCanceled(false); |
| } |
| } |
| }); |
| } |
| |
| private void addShiftEnterReplaceAllTo(JTextComponent textField) { |
| textField.getInputMap().put(KeyStroke.getKeyStroke( |
| KeyEvent.VK_ENTER, InputEvent.SHIFT_MASK, true), |
| "replace-all"); // NOI18N |
| textField.getActionMap().put("replace-all", // NOI18N |
| new AbstractAction() { |
| |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| replaceAll(); |
| } |
| }); |
| } |
| |
| private void addEscapeKeystrokeFocusBackTo(JPanel jpanel) { |
| jpanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), |
| "loose-focus"); // NOI18N |
| jpanel.getActionMap().put("loose-focus", new AbstractAction() { // NOI18N |
| |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| if (!popupMenuWasCanceled && !searchBar.isPopupMenuWasCanceled()) { |
| looseFocus(); |
| } else { |
| popupMenuWasCanceled = false; |
| searchBar.setPopupMenuWasCanceled(false); |
| } |
| |
| } |
| }); |
| } |
| |
| void updateReplaceComboBoxHistory(String incrementalSearchText) { |
| EditorFindSupport.getInstance().addToReplaceHistory(new EditorFindSupport.RP(incrementalSearchText, preserveCaseCheckBox.isSelected())); |
| // Add the text to the top of the list |
| for (int i = replaceComboBox.getItemCount() - 1; i >= 0; i--) { |
| String item = replaceComboBox.getItemAt(i); |
| if (item.equals(incrementalSearchText)) { |
| replaceComboBox.removeItemAt(i); |
| } |
| } |
| ((MutableComboBoxModel<String>) replaceComboBox.getModel()).insertElementAt(incrementalSearchText, 0); |
| replaceComboBox.setSelectedIndex(0); |
| } |
| |
| private ActionListener closeButtonListener; |
| |
| private ActionListener getCloseButtonListener() { |
| if (closeButtonListener == null) { |
| closeButtonListener = new ActionListener() { |
| |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| looseFocus(); |
| } |
| }; |
| } |
| return closeButtonListener; |
| } |
| |
| private void unchangeSearchBarToBeOnlySearchBar() { |
| searchBar.getCloseButton().removeActionListener(getCloseButtonListener()); |
| Mnemonics.setLocalizedText(searchBar.getFindLabel(), NbBundle.getMessage(ReplaceBar.class, "CTL_Find")); // NOI18N |
| Dimension oldDimensionForFindLabel = searchBar.getFindLabel().getUI().getMinimumSize(searchBar.getFindLabel()); |
| searchBar.getFindLabel().setMinimumSize(oldDimensionForFindLabel); |
| searchBar.getFindLabel().setPreferredSize(oldDimensionForFindLabel); |
| searchBar.addEscapeKeystrokeFocusBackTo(searchBar); |
| searchBar.setFocusTraversalPolicy(null); |
| searchBar.looseFocus(); |
| searchBar.setSearchProperties(SearchPropertiesSupport.getSearchProperties()); |
| } |
| |
| private void changeSearchBarToBePartOfReplaceBar() { |
| searchBar.getCloseButton().addActionListener(getCloseButtonListener()); |
| Mnemonics.setLocalizedText(searchBar.getFindLabel(), NbBundle.getMessage(ReplaceBar.class, "CTL_Replace_Find")); // NOI18N |
| Dimension newDimensionForFindLabel = new Dimension(replaceLabel.getPreferredSize().width, searchBar.getFindLabel().getPreferredSize().height); |
| searchBar.getFindLabel().setMinimumSize(newDimensionForFindLabel); |
| searchBar.getFindLabel().setPreferredSize(newDimensionForFindLabel); |
| this.addEscapeKeystrokeFocusBackTo(searchBar); |
| searchBar.setFocusTraversalPolicy(searchBarFocusTraversalPolicy); |
| setFocusTraversalPolicy(searchBarFocusTraversalPolicy); |
| searchBar.getPreferredSize(); |
| searchBar.setSearchProperties(SearchPropertiesSupport.getReplaceProperties()); |
| } |
| |
| public void looseFocus() { |
| if (!isVisible()) { |
| return; |
| } |
| |
| unchangeSearchBarToBeOnlySearchBar(); |
| setVisible(false); |
| } |
| |
| public void gainFocus(boolean persistanceStatus) { |
| if (!isVisible()) { |
| String lastReplace = replaceTextField.getText(); |
| changeSearchBarToBePartOfReplaceBar(); |
| SearchComboBoxEditor.changeToOneLineEditorPane((JEditorPane) replaceTextField); |
| addEnterKeystrokeReplaceTo(replaceTextField); |
| MutableComboBoxModel<String> comboBoxModelIncSearch = ((MutableComboBoxModel<String>) replaceComboBox.getModel()); |
| for (int i = comboBoxModelIncSearch.getSize() - 1; i >= 0; i--) { |
| comboBoxModelIncSearch.removeElementAt(i); |
| } |
| for (EditorFindSupport.RP rp : EditorFindSupport.getInstance().getReplaceHistory()) { |
| comboBoxModelIncSearch.addElement(rp.getReplaceExpression()); |
| } |
| replaceTextField.setText(lastReplace); |
| setVisible(true); |
| } |
| searchBar.gainFocus(persistanceStatus); |
| searchBar.getIncSearchTextField().requestFocusInWindow(); |
| } |
| |
| private void replace() { |
| replace(false); |
| } |
| |
| private void replaceAll() { |
| replace(true); |
| } |
| |
| private void replace(boolean replaceAll) { |
| searchBar.updateIncSearchComboBoxHistory(searchBar.getIncSearchTextField().getText()); |
| this.updateReplaceComboBoxHistory(replaceTextField.getText()); |
| |
| EditorFindSupport findSupport = EditorFindSupport.getInstance(); |
| Map<String, Object> findProps = new HashMap<>(); |
| findProps.putAll(searchBar.getSearchProperties()); |
| findProps.put(EditorFindSupport.FIND_REPLACE_WITH, replaceTextField.getText()); |
| findProps.put(EditorFindSupport.FIND_BACKWARD_SEARCH, backwardsCheckBox.isSelected()); |
| findProps.put(EditorFindSupport.FIND_PRESERVE_CASE, preserveCaseCheckBox.isSelected() && preserveCaseCheckBox.isEnabled()); |
| findSupport.putFindProperties(findProps); |
| if (replaceAll) { |
| findSupport.replaceAll(findProps); |
| } else { |
| try { |
| findSupport.replace(findProps, false); |
| findSupport.find(findProps, false); |
| } catch (GuardedException ge) { |
| LOG.log(Level.FINE, null, ge); |
| Toolkit.getDefaultToolkit().beep(); |
| } catch (BadLocationException ble) { |
| LOG.log(Level.WARNING, null, ble); |
| } |
| } |
| } |
| |
| @Override |
| public void propertyChange(PropertyChangeEvent evt) { |
| if (isShowing()) { |
| preserveCaseCheckBox.setEnabled(!(Boolean) EditorFindSupport.getInstance().getFindProperty(EditorFindSupport.FIND_REG_EXP) && !(Boolean) EditorFindSupport.getInstance().getFindProperty(EditorFindSupport.FIND_MATCH_CASE)); |
| } |
| } |
| |
| private class ReplacePopupMenuListener implements PopupMenuListener { |
| |
| @Override |
| public void popupMenuWillBecomeVisible(PopupMenuEvent e) { |
| } |
| |
| @Override |
| public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { |
| } |
| |
| @Override |
| public void popupMenuCanceled(PopupMenuEvent e) { |
| popupMenuWasCanceled = true; |
| } |
| } |
| } |