0001: /*BEGIN_COPYRIGHT_BLOCK
0002: *
0003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
0004: * All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions are met:
0008: * * Redistributions of source code must retain the above copyright
0009: * notice, this list of conditions and the following disclaimer.
0010: * * Redistributions in binary form must reproduce the above copyright
0011: * notice, this list of conditions and the following disclaimer in the
0012: * documentation and/or other materials provided with the distribution.
0013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
0014: * names of its contributors may be used to endorse or promote products
0015: * derived from this software without specific prior written permission.
0016: *
0017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0028: *
0029: * This software is Open Source Initiative approved Open Source Software.
0030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
0031: *
0032: * This file is part of DrJava. Download the current version of this project
0033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
0034: *
0035: * END_COPYRIGHT_BLOCK*/
0036:
0037: package edu.rice.cs.drjava.ui;
0038:
0039: import java.awt.*;
0040: import java.awt.event.*;
0041:
0042: import javax.swing.*;
0043: import javax.swing.event.*;
0044: import javax.swing.text.*;
0045: import java.awt.datatransfer.*;
0046: import java.io.File;
0047: import java.util.HashSet;
0048: import java.util.LinkedList;
0049:
0050: import edu.rice.cs.drjava.DrJava;
0051: import edu.rice.cs.drjava.config.*;
0052: import edu.rice.cs.drjava.model.SingleDisplayModel;
0053: import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
0054: import edu.rice.cs.drjava.model.FindReplaceMachine;
0055: import edu.rice.cs.drjava.model.FindResult;
0056: import edu.rice.cs.drjava.model.ClipboardHistoryModel;
0057: import edu.rice.cs.drjava.model.DocumentRegion;
0058: import edu.rice.cs.drjava.model.MovingDocumentRegion;
0059: import edu.rice.cs.drjava.model.RegionManager;
0060: import edu.rice.cs.drjava.model.FileMovedException;
0061:
0062: import edu.rice.cs.util.swing.BorderlessScrollPane;
0063: import edu.rice.cs.util.swing.Utilities;
0064: import edu.rice.cs.util.text.AbstractDocumentInterface;
0065: import edu.rice.cs.util.text.SwingDocument;
0066: import edu.rice.cs.util.UnexpectedException;
0067: import edu.rice.cs.util.Lambda;
0068: import edu.rice.cs.util.StringOps;
0069:
0070: /** The tabbed panel that handles requests for finding and replacing text.
0071: * @version $Id: FindReplacePanel.java 4255 2007-08-28 19:17:37Z mgricken $
0072: */
0073: class FindReplacePanel extends TabbedPanel implements ClipboardOwner {
0074:
0075: private JButton _findNextButton;
0076: private JButton _findPreviousButton;
0077: private JButton _findAllButton;
0078: private JButton _replaceButton;
0079: private JButton _replaceFindNextButton;
0080: private JButton _replaceFindPreviousButton;
0081: private JButton _replaceAllButton;
0082:
0083: private JTextPane _findField;
0084: private JTextPane _replaceField;
0085:
0086: private JLabel _findLabelBot; // Dynamically updated
0087:
0088: private JCheckBox _ignoreCommentsAndStrings;
0089: private JCheckBox _matchCase;
0090: private JCheckBox _searchAllDocuments;
0091: private JCheckBox _matchWholeWord;
0092:
0093: /* MainFrame _frame is inherited from TabbedPanel */
0094:
0095: private FindReplaceMachine _machine;
0096: private SingleDisplayModel _model;
0097: private DefinitionsPane _defPane = null;
0098: private boolean _caretChanged;
0099:
0100: /** Listens for changes to the cursor position in order to reset the start position */
0101: private CaretListener _caretListener = new CaretListener() {
0102: public void caretUpdate(CaretEvent e) {
0103: Utilities.invokeLater(new Runnable() {
0104: public void run() {
0105: _replaceAction.setEnabled(false);
0106: _replaceFindNextAction.setEnabled(false);
0107: _replaceFindPreviousAction.setEnabled(false);
0108: _machine.positionChanged();
0109: _caretChanged = true;
0110: }
0111: });
0112: }
0113: };
0114:
0115: /** The action performed when searching forwards */
0116: private Action _findNextAction = new AbstractAction("Find Next") {
0117: public void actionPerformed(ActionEvent e) {
0118: findNext();
0119: }
0120: };
0121:
0122: private Action _findPreviousAction = new AbstractAction(
0123: "Find Previous") {
0124: public void actionPerformed(ActionEvent e) {
0125: findPrevious();
0126: }
0127: };
0128:
0129: private Action _findAllAction = new AbstractAction("Find All") {
0130: public void actionPerformed(final ActionEvent e) {
0131: _findAll();
0132: }
0133: };
0134:
0135: private Action _doFindAction = new AbstractAction("Do Find") {
0136: public void actionPerformed(ActionEvent e) {
0137: _doFind();
0138: }
0139: };
0140:
0141: private Action _replaceAction = new AbstractAction("Replace") {
0142: public void actionPerformed(ActionEvent e) {
0143: _replace();
0144: }
0145: };
0146:
0147: private Action _replaceFindNextAction = new AbstractAction(
0148: "Replace/Find Next") {
0149: public void actionPerformed(ActionEvent e) {
0150: _replaceFindNext();
0151: }
0152: };
0153:
0154: private Action _replaceFindPreviousAction = new AbstractAction(
0155: "Replace/Find Previous") {
0156: public void actionPerformed(ActionEvent e) {
0157: _replaceFindPrevious();
0158: };
0159: };
0160:
0161: /** Replaces all occurences of the findfield text with that of the replacefield text both before and after the cursor
0162: * without prompting for wrapping around the end of the document.
0163: */
0164: private Action _replaceAllAction = new AbstractAction("Replace All") {
0165: public void actionPerformed(ActionEvent e) {
0166: _replaceAll();
0167: }
0168: };
0169:
0170: // Inserts '\n' into a text field. (The default binding for "enter" is to insert
0171: // the system-specific newline string (I think), which causes trouble when finding
0172: // in files with different newline strings.)
0173: // TODO: Standardize on \n in a post-processing step, rather than mucking around
0174: // in the workings of a text editor field. (Notice, for example, that this
0175: // doesn't correctly handle an 'enter' pressed while some text is selected.)
0176: Action _standardNewlineAction = new TextAction("NewLine Action") {
0177: public void actionPerformed(ActionEvent e) {
0178: JTextComponent c = getTextComponent(e);
0179: String text = c.getText();
0180: int caretPos = c.getCaretPosition();
0181: String textBeforeCaret = text.substring(0, caretPos);
0182: String textAfterCaret = text.substring(caretPos);
0183: c.setText(textBeforeCaret.concat("\n").concat(
0184: textAfterCaret));
0185: c.setCaretPosition(caretPos + 1);
0186: }
0187: };
0188:
0189: /*private Action _closeAction = new AbstractAction("X") {
0190: public void actionPerformed(ActionEvent e) {
0191: // removeTab automatically calls show()
0192: _close();
0193: }
0194: };*/
0195:
0196: /** Standard Constructor.
0197: * @param frame the overall enclosing window
0198: * @param model the model containing the documents to search
0199: */
0200: public FindReplacePanel(MainFrame frame, SingleDisplayModel model) {
0201: super (frame, "Find/Replace");
0202: _model = model;
0203: _machine = new FindReplaceMachine(_model, _model
0204: .getDocumentIterator());
0205: _updateMachine();
0206:
0207: /********* Button Initialization ********/
0208: _findNextButton = new JButton(_findNextAction);
0209: _findPreviousButton = new JButton(_findPreviousAction);
0210: _findAllButton = new JButton(_findAllAction);
0211: _replaceButton = new JButton(_replaceAction);
0212: _replaceFindNextButton = new JButton(_replaceFindNextAction);
0213: _replaceFindPreviousButton = new JButton(
0214: _replaceFindPreviousAction);
0215: _replaceAllButton = new JButton(_replaceAllAction);
0216:
0217: _replaceAction.setEnabled(false);
0218: _replaceFindNextAction.setEnabled(false);
0219: _replaceFindPreviousAction.setEnabled(false);
0220:
0221: /********* Find/Replace Field Initialization **********/
0222: _findField = new JTextPane(new DefaultStyledDocument());
0223: _replaceField = new JTextPane(new SwingDocument());
0224:
0225: // Ignore special treatment of 'tab' in text panes
0226: int tabForward = KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS;
0227: int tabBackward = KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS;
0228: _findField.setFocusTraversalKeys(tabForward, null);
0229: _replaceField.setFocusTraversalKeys(tabForward, null);
0230: _findField.setFocusTraversalKeys(tabBackward, null);
0231: _replaceField.setFocusTraversalKeys(tabBackward, null);
0232:
0233: // Define custom key bindings for 'enter' and 'tab'
0234: KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
0235: KeyStroke ctrlEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
0236: Event.CTRL_MASK);
0237: KeyStroke ctrlTab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
0238: Event.CTRL_MASK);
0239: InputMap findIM = _findField.getInputMap();
0240: InputMap replaceIM = _replaceField.getInputMap();
0241: findIM.put(enter, "Do Find");
0242: findIM.put(ctrlEnter, "Insert Newline");
0243: findIM.put(ctrlTab, "Insert Tab");
0244: findIM.put(DrJava.getConfig().getSetting(
0245: OptionConstants.KEY_CUT), "Cut");
0246: findIM.put(DrJava.getConfig().getSetting(
0247: OptionConstants.KEY_COPY), "Copy");
0248: replaceIM.put(enter, "Insert Newline");
0249: replaceIM.put(ctrlEnter, "Insert Newline");
0250: replaceIM.put(ctrlTab, "Insert Tab");
0251: replaceIM.put(DrJava.getConfig().getSetting(
0252: OptionConstants.KEY_CUT), "Cut");
0253: replaceIM.put(DrJava.getConfig().getSetting(
0254: OptionConstants.KEY_COPY), "Copy");
0255:
0256: Action insertTabAction = new DefaultEditorKit.InsertTabAction();
0257: ActionMap findAM = _findField.getActionMap();
0258: ActionMap replaceAM = _replaceField.getActionMap();
0259: findAM.put("Do Find", _doFindAction);
0260: findAM.put("Insert Newline", _standardNewlineAction);
0261: findAM.put("Insert Tab", insertTabAction);
0262: findAM.put("Cut", cutAction);
0263: findAM.put("Copy", copyAction);
0264: replaceAM.put("Insert Newline", _standardNewlineAction);
0265: replaceAM.put("Insert Tab", insertTabAction);
0266: replaceAM.put("Cut", cutAction);
0267: replaceAM.put("Copy", copyAction);
0268:
0269: // Setup color listeners.
0270: new ForegroundColorListener(_findField);
0271: new BackgroundColorListener(_findField);
0272: new ForegroundColorListener(_replaceField);
0273: new BackgroundColorListener(_replaceField);
0274: Font font = DrJava.getConfig().getSetting(
0275: OptionConstants.FONT_MAIN);
0276: setFieldFont(font);
0277:
0278: /******** Label Initializations ********/
0279: // Create the Structure for the replace label
0280: JLabel _replaceLabelTop = new JLabel("Replace",
0281: SwingConstants.RIGHT);
0282: JLabel _replaceLabelBot = new JLabel("With",
0283: SwingConstants.RIGHT);
0284:
0285: JPanel replaceLabelPanelTop = new JPanel(new BorderLayout(5, 5));
0286: JPanel replaceLabelPanelBot = new JPanel(new BorderLayout(5, 5));
0287: JPanel replaceLabelPanel = new JPanel(new GridLayout(2, 1));
0288:
0289: replaceLabelPanelTop.add(_replaceLabelTop, BorderLayout.SOUTH);
0290: replaceLabelPanelBot.add(_replaceLabelBot, BorderLayout.NORTH);
0291:
0292: replaceLabelPanel.add(replaceLabelPanelTop);
0293: replaceLabelPanel.add(replaceLabelPanelBot);
0294:
0295: // Create the stucture for the find label
0296: JLabel _findLabelTop = new JLabel("Find", SwingConstants.RIGHT);
0297: _findLabelBot = new JLabel("Next", SwingConstants.RIGHT);
0298:
0299: JPanel findLabelPanelTop = new JPanel(new BorderLayout(5, 5));
0300: JPanel findLabelPanelBot = new JPanel(new BorderLayout(5, 5));
0301: JPanel findLabelPanel = new JPanel(new GridLayout(2, 1));
0302:
0303: findLabelPanelTop.add(_findLabelTop, BorderLayout.SOUTH);
0304: findLabelPanelBot.add(_findLabelBot, BorderLayout.NORTH);
0305:
0306: findLabelPanel.add(findLabelPanelTop);
0307: findLabelPanel.add(findLabelPanelBot);
0308:
0309: /******** Button Panel ********/
0310: JPanel buttons = new JPanel();
0311: buttons.setLayout(new GridLayout(1, 0, 5, 0));
0312: buttons.add(_findNextButton);
0313: buttons.add(_findPreviousButton);
0314: buttons.add(_findAllButton);
0315: buttons.add(_replaceFindNextButton);
0316: buttons.add(_replaceFindPreviousButton);
0317: buttons.add(_replaceButton);
0318: buttons.add(_replaceAllButton);
0319:
0320: /******** Listeners for the right-hand check boxes ********/
0321: boolean matchCaseSelected = DrJava.getConfig().getSetting(
0322: OptionConstants.FIND_MATCH_CASE);
0323: _matchCase = new JCheckBox("Match Case", matchCaseSelected);
0324: _machine.setMatchCase(matchCaseSelected);
0325: _matchCase.addItemListener(new ItemListener() {
0326: public void itemStateChanged(ItemEvent e) {
0327: boolean isSelected = (e.getStateChange() == ItemEvent.SELECTED);
0328: _machine.setMatchCase(isSelected);
0329: DrJava.getConfig().setSetting(
0330: OptionConstants.FIND_MATCH_CASE, isSelected);
0331: _findField.requestFocusInWindow();
0332: }
0333: });
0334:
0335: boolean searchAllSelected = DrJava.getConfig().getSetting(
0336: OptionConstants.FIND_ALL_DOCUMENTS);
0337: _searchAllDocuments = new JCheckBox("Search All Documents",
0338: searchAllSelected);
0339: _machine.setSearchAllDocuments(searchAllSelected);
0340: _searchAllDocuments.addItemListener(new ItemListener() {
0341: public void itemStateChanged(ItemEvent e) {
0342: boolean isSelected = (e.getStateChange() == ItemEvent.SELECTED);
0343: _machine.setSearchAllDocuments(isSelected);
0344: DrJava.getConfig().setSetting(
0345: OptionConstants.FIND_ALL_DOCUMENTS, isSelected);
0346: _findField.requestFocusInWindow();
0347: }
0348: });
0349:
0350: boolean matchWordSelected = DrJava.getConfig().getSetting(
0351: OptionConstants.FIND_WHOLE_WORD);
0352: _matchWholeWord = new JCheckBox("Whole Word", matchWordSelected);
0353: if (matchWordSelected) {
0354: _machine.setMatchWholeWord();
0355: } else {
0356: _machine.setFindAnyOccurrence();
0357: }
0358: _matchWholeWord.addItemListener(new ItemListener() {
0359: public void itemStateChanged(ItemEvent e) {
0360: boolean isSelected = (e.getStateChange() == ItemEvent.SELECTED);
0361: if (isSelected) {
0362: _machine.setMatchWholeWord();
0363: } else {
0364: _machine.setFindAnyOccurrence();
0365: }
0366: DrJava.getConfig().setSetting(
0367: OptionConstants.FIND_WHOLE_WORD, isSelected);
0368: _findField.requestFocusInWindow();
0369: }
0370: });
0371:
0372: boolean ignoreCommentsSelected = DrJava.getConfig().getSetting(
0373: OptionConstants.FIND_NO_COMMENTS_STRINGS);
0374: _ignoreCommentsAndStrings = new JCheckBox(
0375: "No Comments/Strings", ignoreCommentsSelected);
0376: _machine.setIgnoreCommentsAndStrings(ignoreCommentsSelected);
0377: _ignoreCommentsAndStrings.addItemListener(new ItemListener() {
0378: public void itemStateChanged(ItemEvent e) {
0379: boolean isSelected = (e.getStateChange() == ItemEvent.SELECTED);
0380: _machine.setIgnoreCommentsAndStrings(isSelected);
0381: DrJava.getConfig().setSetting(
0382: OptionConstants.FIND_NO_COMMENTS_STRINGS,
0383: isSelected);
0384: _findField.requestFocusInWindow();
0385: }
0386: });
0387:
0388: // We choose not to preserve backwards searching between sessions
0389: //_machine.setSearchBackwards(DrJava.getConfig().getSetting(OptionConstants.FIND_SEARCH_BACKWARDS));
0390:
0391: /******** Initialize the panels containing the checkboxes ********/
0392: this .removeAll(); // actually, override the behavior of TabbedPanel
0393:
0394: // remake closePanel
0395: _closePanel = new JPanel(new BorderLayout());
0396: _closePanel.add(_closeButton, BorderLayout.NORTH);
0397:
0398: JPanel _lowerCheckPanel = new JPanel(new FlowLayout(
0399: FlowLayout.LEFT, 0, 0));
0400: _lowerCheckPanel.add(_matchWholeWord);
0401: _lowerCheckPanel.add(_ignoreCommentsAndStrings);
0402: _lowerCheckPanel.setMaximumSize(new Dimension(200, 40));
0403:
0404: JPanel _matchCaseAndAllDocsPanel = new JPanel(new FlowLayout(
0405: FlowLayout.LEFT, 0, 0));
0406: _matchCase.setPreferredSize(_matchWholeWord.getPreferredSize());
0407: _matchCaseAndAllDocsPanel.add(_matchCase);
0408: _matchCaseAndAllDocsPanel.add(_searchAllDocuments);
0409: _matchCaseAndAllDocsPanel
0410: .setMaximumSize(new Dimension(200, 40));
0411:
0412: BorderlessScrollPane _findPane = new BorderlessScrollPane(
0413: _findField);
0414: BorderlessScrollPane _replacePane = new BorderlessScrollPane(
0415: _replaceField);
0416: _findPane
0417: .setHorizontalScrollBarPolicy(BorderlessScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
0418: _replacePane
0419: .setHorizontalScrollBarPolicy(BorderlessScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
0420:
0421: JPanel findPanel = new JPanel(new BorderLayout(5, 5));
0422: findPanel.add(findLabelPanel, BorderLayout.WEST);
0423: findPanel.add(_findPane, BorderLayout.CENTER);
0424:
0425: JPanel replacePanel = new JPanel(new BorderLayout(5, 5));
0426: replacePanel.add(replaceLabelPanel, BorderLayout.WEST);
0427: replacePanel.add(_replacePane, BorderLayout.CENTER);
0428:
0429: /******** Set up the Panel containing the Text Fields ********/
0430: JPanel leftPanel = new JPanel(new GridLayout(1, 2, 5, 5));
0431: leftPanel.add(findPanel);
0432: leftPanel.add(replacePanel);
0433:
0434: /******** Set up the Panel containing both rows of checkboxes ********/
0435: GridBagLayout gbLayout = new GridBagLayout();
0436: GridBagConstraints c = new GridBagConstraints();
0437: JPanel emptyPanel = new JPanel();
0438: JPanel optionsPanel = new JPanel(gbLayout);
0439: optionsPanel.setLayout(gbLayout);
0440: optionsPanel.add(_matchCaseAndAllDocsPanel);
0441: optionsPanel.add(_lowerCheckPanel);
0442: optionsPanel.add(emptyPanel);
0443:
0444: c.fill = GridBagConstraints.HORIZONTAL;
0445: c.anchor = GridBagConstraints.NORTH;
0446: c.gridwidth = GridBagConstraints.REMAINDER;
0447: c.weightx = 1.0;
0448: gbLayout.setConstraints(_matchCaseAndAllDocsPanel, c);
0449: gbLayout.setConstraints(_lowerCheckPanel, c);
0450:
0451: c.fill = GridBagConstraints.BOTH;
0452: c.anchor = GridBagConstraints.SOUTH;
0453: c.gridheight = GridBagConstraints.REMAINDER;
0454: c.weighty = 1.0;
0455:
0456: gbLayout.setConstraints(emptyPanel, c);
0457:
0458: /******** Set up the Panel containing the two above main panels ********/
0459: JPanel midPanel = new JPanel(new BorderLayout(5, 5));
0460: midPanel.add(leftPanel, BorderLayout.CENTER);
0461: midPanel.add(optionsPanel, BorderLayout.EAST);
0462:
0463: /******** Set up the Panel containing the midPanel and the closePanel ********/
0464: JPanel _rightPanel = new JPanel(new BorderLayout(5, 5));
0465: _rightPanel.add(midPanel, BorderLayout.CENTER);
0466: _rightPanel.add(_closePanel, BorderLayout.EAST);
0467:
0468: JPanel newPanel = new JPanel();
0469: newPanel.setLayout(new BoxLayout(newPanel, BoxLayout.Y_AXIS));
0470: newPanel.add(_rightPanel);
0471: newPanel.add(Box.createVerticalStrut(5));
0472: newPanel.add(buttons);
0473: newPanel.add(Box.createVerticalStrut(5));
0474:
0475: this .add(newPanel);
0476:
0477: /******** Document, Focus and Key Listeners ********/
0478:
0479: // DocumentListener that keeps track of changes in the find field.
0480: _findField.getDocument().addDocumentListener(
0481: new DocumentListener() {
0482:
0483: /**If attributes in the find field have changed, gray out "Replace" & "Replace and Find Next" buttons.
0484: * @param e the event caught by this listener
0485: */
0486: public void changedUpdate(DocumentEvent e) {
0487: _updateHelper();
0488: }
0489:
0490: /** If text has been changed in the find field, gray out "Replace" & "Replace and Find Next" buttons.
0491: * @param e the event caught by this listener
0492: */
0493: public void insertUpdate(DocumentEvent e) {
0494: _updateHelper();
0495: }
0496:
0497: /** If text has been changed in the find field, gray out "Replace" & "Replace and Find Next" buttons.
0498: * @param e the event caught by this listener
0499: */
0500: public void removeUpdate(DocumentEvent e) {
0501: _updateHelper();
0502: }
0503:
0504: private void _updateHelper() {
0505: Utilities.invokeLater(new Runnable() {
0506: public void run() {
0507: // _machine.makeCurrentOffsetStart();
0508: updateFirstDocInSearch();
0509: _replaceAction.setEnabled(false);
0510: _replaceFindNextAction
0511: .setEnabled(false);
0512: _replaceFindPreviousAction
0513: .setEnabled(false);
0514: _machine.positionChanged();
0515: if (_findField.getText().equals(""))
0516: _replaceAllAction.setEnabled(false);
0517: else
0518: _replaceAllAction.setEnabled(true);
0519: updateUI();
0520: }
0521: });
0522: }
0523: });
0524:
0525: }
0526:
0527: /** Focuses the find/replace dialog in the window, placing the focus on the _findField, and selecting all the text.*/
0528: public boolean requestFocusInWindow() {
0529: super .requestFocusInWindow();
0530: _findField.selectAll();
0531: return _findField.requestFocusInWindow();
0532: }
0533:
0534: /** Getter method for the _findField component */
0535: JTextPane getFindField() {
0536: return _findField;
0537: }
0538:
0539: /** Performs "find all" command. */
0540: private void _findAll() {
0541: String searchStr = _findField.getText();
0542: int searchLen = searchStr.length();
0543: if (searchLen == 0)
0544: return;
0545:
0546: _frame.updateStatusField("Finding All");
0547: String title = searchStr;
0548: if (title.length() > 10) {
0549: title = title.substring(0, 10) + "...";
0550: }
0551: title = "Find: " + title;
0552:
0553: final RegionManager<MovingDocumentRegion> rm = _model
0554: .createFindResultsManager();
0555: final FindResultsPanel panel = _frame.createFindResultsPanel(
0556: rm, title);
0557:
0558: _updateMachine();
0559: _machine.setFindWord(searchStr);
0560: String replaceStr = _replaceField.getText();
0561: _machine.setReplaceWord(replaceStr);
0562: _frame.clearStatusMessage();
0563: final OpenDefinitionsDocument startDoc = _defPane
0564: .getOpenDefDocument();
0565: final LinkedList<FindResult> results = new LinkedList<FindResult>();
0566:
0567: _frame.hourglassOn();
0568: try {
0569: /* Accumulate all occurrences of searchStr in results. */
0570: final int count = _machine
0571: .processAll(new Lambda<Void, FindResult>() {
0572: public Void apply(final FindResult fr) {
0573: results.add(fr);
0574: return null;
0575: }
0576: });
0577:
0578: // Set of documents that have been reverted in the process of "find all"
0579: HashSet<OpenDefinitionsDocument> reverted = new HashSet<OpenDefinitionsDocument>();
0580:
0581: for (FindResult fr : results) {
0582: if (reverted.contains(fr.getDocument())) {
0583: // skipping document because we have previously noticed that it has been modified,
0584: // i.e. the document is in the reverted list
0585: continue;
0586: }
0587:
0588: // // get the original time stamp
0589: // long origts = fr.getDocument().getTimestamp();
0590:
0591: if (!_model.getActiveDocument()
0592: .equals(fr.getDocument())) {
0593: _model.setActiveDocument(fr.getDocument());
0594: } else
0595: _model.refreshActiveDocument();
0596:
0597: final OpenDefinitionsDocument doc = _defPane
0598: .getOpenDefDocument();
0599:
0600: // // get the time stamp after making the document the active one
0601: // long newts = doc.getTimestamp();
0602: // if (newts != origts) {
0603: // // timestamps changed, document has been modified, so all our FindResults
0604: // // may not apply anymore. we are going to discard all FindResults for this
0605: // // document.
0606: // // add thi document to the list of reverted documents
0607: // reverted.add(doc);
0608: // continue;
0609: // }
0610:
0611: final StringBuilder sb = new StringBuilder();
0612: try {
0613: int endSel = fr.getFoundOffset();
0614: int startSel = endSel - searchLen;
0615: final Position startPos = doc
0616: .createPosition(startSel);
0617: final Position endPos = doc.createPosition(endSel);
0618:
0619: // create excerpt string
0620: int excerptEndSel = doc.getLineEndPos(endSel);
0621: int excerptStartSel = doc.getLineStartPos(startSel);
0622: int length = Math.min(120, excerptEndSel
0623: - excerptStartSel);
0624:
0625: // this highlights the actual region in red
0626: int startRed = startSel - excerptStartSel;
0627: int endRed = endSel - excerptStartSel;
0628: String s = doc.getText(excerptStartSel, length);
0629:
0630: // change control characters and ones that may not be displayed to spaces
0631: for (int i = 0; i < s.length(); ++i) {
0632: if (s.charAt(i) < ' ' || s.charAt(i) > 127) {
0633: sb.append(' ');
0634: } else {
0635: sb.append(s.charAt(i));
0636: }
0637: }
0638: s = sb.toString();
0639:
0640: // trim the front
0641: for (int i = 0; i < s.length(); ++i) {
0642: if (!Character.isWhitespace(s.charAt(i)))
0643: break;
0644: --startRed;
0645: --endRed;
0646: }
0647:
0648: // trim the end
0649: s = s.trim();
0650:
0651: // bound startRed and endRed
0652: if (startRed < 0) {
0653: startRed = 0;
0654: }
0655: if (startRed > s.length()) {
0656: startRed = s.length();
0657: }
0658: if (endRed < startRed) {
0659: endRed = startRed;
0660: }
0661: if (endRed > s.length()) {
0662: endRed = s.length();
0663: }
0664:
0665: // create the excerpt string
0666: sb.setLength(0);
0667: sb.append(StringOps.encodeHTML(s.substring(0,
0668: startRed)));
0669: sb.append("<font color=#ff0000>");
0670: sb.append(StringOps.encodeHTML(s.substring(
0671: startRed, endRed)));
0672: sb.append("</font>");
0673: sb
0674: .append(StringOps.encodeHTML(s
0675: .substring(endRed)));
0676: rm
0677: .addRegion(new MovingDocumentRegion(doc,
0678: doc.getFile(), startPos, endPos, sb
0679: .toString()));
0680: // rm.addRegion(new MovingDocumentRegion(doc, doc.getFile(), startPos, endPos, s));
0681: } catch (FileMovedException fme) {
0682: throw new UnexpectedException(fme);
0683: } catch (BadLocationException ble) {
0684: throw new UnexpectedException(ble);
0685: }
0686: }
0687:
0688: SwingUtilities.invokeLater(new Runnable() {
0689: public void run() {
0690: _model.setActiveDocument(startDoc);
0691: Toolkit.getDefaultToolkit().beep();
0692: _frame.setStatusMessage("Found " + count
0693: + " occurrence" + ((count == 1) ? "" : "s")
0694: + ".");
0695: if (count > 0) {
0696: _frame.showFindResultsPanel(panel);
0697: } else {
0698: panel.freeResources();
0699: }
0700: }
0701: });
0702: } finally {
0703: _frame.hourglassOff();
0704: }
0705: }
0706:
0707: /** Performs the "replace all" command. */
0708: private void _replaceAll() {
0709: _frame.updateStatusField("Replacing All");
0710: _updateMachine();
0711: _machine.setFindWord(_findField.getText());
0712: _machine.setReplaceWord(_replaceField.getText());
0713: _frame.clearStatusMessage();
0714: int count = _machine.replaceAll();
0715: Toolkit.getDefaultToolkit().beep();
0716: _frame.setStatusMessage("Replaced " + count + " occurrence"
0717: + ((count == 1) ? "" : "s") + ".");
0718: _replaceAction.setEnabled(false);
0719: _replaceFindNextAction.setEnabled(false);
0720: _replaceFindPreviousAction.setEnabled(false);
0721: }
0722:
0723: private void _replaceFindNext() {
0724: _frame.updateStatusField("Replacing and Finding Next");
0725: if (getSearchBackwards() == true) {
0726: _machine.positionChanged();
0727: findNext();
0728: }
0729: _updateMachine();
0730: _machine.setFindWord(_findField.getText());
0731: String replaceWord = _replaceField.getText();
0732: _machine.setReplaceWord(replaceWord);
0733: _frame.clearStatusMessage(); // _message.setText(""); // JL
0734:
0735: // replaces the occurrence at the current position
0736: boolean replaced = _machine.replaceCurrent();
0737: // and finds the next word
0738: if (replaced) {
0739: _selectReplacedItem(replaceWord.length());
0740: findNext();
0741: _replaceFindNextButton.requestFocusInWindow();
0742: } else {
0743: _replaceAction.setEnabled(false);
0744: _replaceFindNextAction.setEnabled(false);
0745: _replaceFindPreviousAction.setEnabled(false);
0746: Toolkit.getDefaultToolkit().beep();
0747: _frame.setStatusMessage("Replace failed.");
0748: }
0749: }
0750:
0751: private void _replaceFindPrevious() {
0752: _frame.updateStatusField("Replacing and Finding Previous");
0753: if (getSearchBackwards() == false) {
0754: _machine.positionChanged();
0755: findPrevious();
0756: }
0757: _updateMachine();
0758: _machine.setFindWord(_findField.getText());
0759: String replaceWord = _replaceField.getText();
0760: _machine.setReplaceWord(replaceWord);
0761: _frame.clearStatusMessage();
0762:
0763: // replaces the occurrence at the current position
0764: boolean replaced = _machine.replaceCurrent();
0765: // and finds the previous word
0766: if (replaced) {
0767: _selectReplacedItem(replaceWord.length());
0768: findPrevious();
0769: _replaceFindPreviousButton.requestFocusInWindow();
0770: } else {
0771: _replaceAction.setEnabled(false);
0772: _replaceFindNextAction.setEnabled(false);
0773: _replaceFindPreviousAction.setEnabled(false);
0774: Toolkit.getDefaultToolkit().beep();
0775: _frame.setStatusMessage("Replace failed.");
0776: }
0777: }
0778:
0779: /** Perfroms the "find next" command. Package visibility to accommodate calls from MainFrame. */
0780: void findNext() {
0781: _frame.updateStatusField("Finding Next");
0782: _machine.setSearchBackwards(false);
0783: _findLabelBot.setText("Next");
0784: _doFind();
0785: }
0786:
0787: /** Called when user the activates "find previous" command. Package visibility to accommodate calls from MainFrame. */
0788: void findPrevious() {
0789: _frame.updateStatusField("Finding Previous");
0790: _machine.setSearchBackwards(true);
0791: _findLabelBot.setText("Prev");
0792: _doFind();
0793: }
0794:
0795: private void _replace() {
0796: _frame.updateStatusField("Replacing");
0797: _updateMachine();
0798: _machine.setFindWord(_findField.getText());
0799: String replaceWord = _replaceField.getText();
0800: _machine.setReplaceWord(replaceWord);
0801: _frame.clearStatusMessage();
0802:
0803: // replaces the occurrence at the current position
0804: boolean replaced = _machine.replaceCurrent();
0805: if (replaced)
0806: _selectReplacedItem(replaceWord.length());
0807: _replaceAction.setEnabled(false);
0808: _replaceFindNextAction.setEnabled(false);
0809: _replaceFindPreviousAction.setEnabled(false);
0810: _replaceButton.requestFocusInWindow();
0811: }
0812:
0813: /** Called from MainFrame in response to opening this or changes in the active document. */
0814: void beginListeningTo(DefinitionsPane defPane) {
0815: if (_defPane == null) {
0816: // removed so it doesn't give the pane focus when switching documents
0817: // requestFocusInWindow();
0818: _displayed = true;
0819: _defPane = defPane;
0820: _defPane.addCaretListener(_caretListener);
0821: _caretChanged = true;
0822:
0823: _updateMachine();
0824: _machine.setFindWord(_findField.getText());
0825: _machine.setReplaceWord(_replaceField.getText());
0826: _frame.clearStatusMessage(); // _message.setText(""); // JL
0827: if (!_machine.onMatch() || _findField.getText().equals("")) {
0828: _replaceAction.setEnabled(false);
0829: _replaceFindNextAction.setEnabled(false);
0830: _replaceFindPreviousAction.setEnabled(false);
0831: } else {
0832: _replaceAction.setEnabled(true);
0833: _replaceFindNextAction.setEnabled(true);
0834: _replaceFindPreviousAction.setEnabled(true);
0835: _machine.setLastFindWord();
0836: }
0837:
0838: if (_findField.getText().equals(""))
0839: _replaceAllAction.setEnabled(false);
0840: else
0841: _replaceAllAction.setEnabled(true);
0842:
0843: _frame.clearStatusMessage();
0844: } else
0845: throw new UnexpectedException(
0846: new RuntimeException(
0847: "FindReplacePanel should not be listening to anything"));
0848: }
0849:
0850: /** Called from MainFrame upon closing this Dialog or changes in the active document. */
0851: public void stopListening() {
0852: if (_defPane != null) {
0853: _defPane.removeCaretListener(_caretListener);
0854: _defPane = null;
0855: _displayed = false;
0856: _frame.clearStatusMessage();
0857: }
0858: }
0859:
0860: /** Abstracted out since this is called from find and replace/find. */
0861: private void _doFind() {
0862:
0863: if (_findField.getText().length() > 0) {
0864:
0865: _model.addToBrowserHistory();
0866:
0867: _updateMachine();
0868: _machine.setFindWord(_findField.getText());
0869: _machine.setReplaceWord(_replaceField.getText());
0870: _frame.clearStatusMessage(); // _message.setText(""); // JL
0871: final boolean searchAll = _machine.getSearchAllDocuments();
0872:
0873: // FindResult contains the document that the result was found in, offset to the next occurrence of
0874: // the string, and a flag indicating whether the end of the document was wrapped around while searching
0875: // for the string.
0876: FindResult fr = _machine.findNext();
0877: OpenDefinitionsDocument doc = fr.getDocument();
0878: OpenDefinitionsDocument matchDoc = _model
0879: .getODDForDocument(doc);
0880: OpenDefinitionsDocument openDoc = _defPane
0881: .getOpenDefDocument();
0882: final boolean docChanged = !matchDoc.equals(openDoc);
0883:
0884: final int pos = fr.getFoundOffset();
0885:
0886: // If there actually *is* a match, then switch active documents. otherwise don't
0887: if (searchAll) {
0888: if (docChanged)
0889: _model.setActiveDocument(matchDoc); // set active doc if matchDoc != openDoc
0890: else
0891: _model.refreshActiveDocument(); // the unmodified active document may have been kicked out of the cache!
0892: }
0893:
0894: if (fr.getWrapped() && !searchAll) {
0895: Toolkit.getDefaultToolkit().beep();
0896: if (!_machine.getSearchBackwards())
0897: _frame
0898: .setStatusMessage("Search wrapped to beginning.");
0899: else
0900: _frame.setStatusMessage("Search wrapped to end.");
0901: }
0902:
0903: if (fr.getAllDocsWrapped() && searchAll) {
0904: Toolkit.getDefaultToolkit().beep();
0905: _frame
0906: .setStatusMessage("Search wrapped around all documents.");
0907: }
0908:
0909: if (pos >= 0) { // found a match
0910: Caret c = _defPane.getCaret();
0911: c.setDot(c.getDot());
0912: _defPane.setCaretPosition(pos);
0913: _caretChanged = true;
0914: _updateMachine();
0915:
0916: final Runnable command = new Runnable() {
0917: public void run() {
0918: _selectFoundItem();
0919: _replaceAction.setEnabled(true);
0920: _replaceFindNextAction.setEnabled(true);
0921: _replaceFindPreviousAction.setEnabled(true);
0922: _machine.setLastFindWord();
0923: // _model.addToBrowserHistory();
0924: }
0925: };
0926:
0927: if (docChanged)
0928: // defer executing this code until after active document switch is complete
0929: SwingUtilities.invokeLater(command);
0930: else
0931: command.run();
0932: }
0933: // else the entire document was searched and no instance of the string
0934: // was found. display at most 50 characters of the non-found string
0935: else {
0936: Toolkit.getDefaultToolkit().beep();
0937: final StringBuilder statusMessage = new StringBuilder(
0938: "Search text \"");
0939: if (_machine.getFindWord().length() <= 50)
0940: statusMessage.append(_machine.getFindWord());
0941: else
0942: statusMessage.append(_machine.getFindWord()
0943: .substring(0, 49)
0944: + "...");
0945: statusMessage.append("\" not found.");
0946: _frame.setStatusMessage(statusMessage.toString());
0947: }
0948: }
0949:
0950: if (!DrJava.getConfig().getSetting(
0951: OptionConstants.FIND_REPLACE_FOCUS_IN_DEFPANE)
0952: .booleanValue()) {
0953: _findField.requestFocusInWindow();
0954: }
0955: }
0956:
0957: protected void _close() {
0958: _defPane.requestFocusInWindow();
0959: if (_displayed)
0960: stopListening();
0961: super ._close();
0962: //_frame.uninstallFindReplaceDialog(this);
0963: }
0964:
0965: public void setSearchBackwards(boolean b) {
0966: _machine.setSearchBackwards(b);
0967: }
0968:
0969: public boolean getSearchBackwards() {
0970: return _machine.getSearchBackwards();
0971: }
0972:
0973: /** Sets the font of the find and replace fields to f. */
0974: public void setFieldFont(Font f) {
0975: _findField.setFont(f);
0976: _replaceField.setFont(f);
0977: }
0978:
0979: /** Updates the first document where the current all-document search began (called in two places: either when the
0980: * _findField is updated, or when the user changes documents.
0981: */
0982: public void updateFirstDocInSearch() {
0983: _machine.setFirstDoc(_model.getActiveDocument());
0984: }
0985:
0986: // private static Container wrap(JComponent comp) {
0987: // Container stretcher = Box.createHorizontalBox();
0988: // stretcher.add(comp);
0989: // stretcher.add(Box.createHorizontalGlue());
0990: // return stretcher;
0991: // }
0992: //
0993: // /** Consider a parent container. Change its layout to GridBagLayout
0994: // * with 2 columns, 2 rows. Consider them quadrants in a coordinate plain.
0995: // * put the arguments in their corresponding quadrants, ignoring q3.
0996: // */
0997: // private static void hookComponents(Container parent, JComponent q1,
0998: // JComponent q2, JComponent q4) {
0999: // GridBagLayout gbl = new GridBagLayout();
1000: // GridBagConstraints c = new GridBagConstraints();
1001: // parent.setLayout(gbl);
1002: // c.fill = c.BOTH;
1003: // addComp(parent, q2, c, gbl, 0, 0, 0f, 0f, 1, 0);
1004: // addComp(parent, q1, c, gbl, 0, 1, 1f, 0f, 1, 0);
1005: // addComp(parent, new JPanel(), c, gbl, 1, 0, 1f, 1f, 2, 0);
1006: // addComp(parent, new JPanel(), c, gbl, 2, 0, 0f, 0f, 1, 0);
1007: // addComp(parent, q4, c, gbl, 2, 1, 1f, 0f, 1, 0);
1008: // }
1009:
1010: // private static void addComp(Container p, JComponent child,
1011: // GridBagConstraints c, GridBagLayout gbl,
1012: // int row, int col,
1013: // float weightx, float weighty, int gridw,
1014: // int ipady) {
1015: // c.gridx = col; c.gridy = row;
1016: // c.weightx = weightx; c.weighty = weighty;
1017: // c.gridwidth = gridw;
1018: // c.ipady = ipady;
1019: // gbl.setConstraints(child,c);
1020: // p.add(child);
1021: // }
1022:
1023: /** Sets appropriate variables in the FindReplaceMachine if the caret has been changed. */
1024: private void _updateMachine() {
1025: if (_caretChanged) {
1026: OpenDefinitionsDocument doc = _model.getActiveDocument();
1027: _machine.setDocument(doc);
1028: if (_machine.getFirstDoc() == null)
1029: _machine.setFirstDoc(doc);
1030: // _machine.setStart(_defPane.getCaretPosition());
1031: _machine.setPosition(_defPane.getCaretPosition());
1032: _caretChanged = false;
1033: }
1034: }
1035:
1036: // /** Shows the dialog and sets the focus appropriately. */
1037: // public void show() {
1038: // //super.show();
1039: // System.err.println("*** Called show ***");
1040: //// if (!isVisible())
1041: // _frame.installFindReplaceDialog(this);
1042: // _updateMachine();
1043: // _findField.requestFocusInWindow();
1044: // _findField.selectAll();
1045: // }
1046:
1047: /** This method is used to select the item that has been inserted in a replacement. */
1048: private void _selectReplacedItem(int length) {
1049: int from, to;
1050: to = _machine.getCurrentOffset();
1051: if (_machine.getSearchBackwards())
1052: from = to + length;
1053: else
1054: from = to - length;
1055: _selectFoundItem(from, to);
1056: }
1057:
1058: /** Calls _selectFoundItem(from, to) with reasonable defaults. */
1059: private void _selectFoundItem() {
1060: int position = _machine.getCurrentOffset();
1061: int to, from;
1062: to = position;
1063: if (!_machine.getSearchBackwards())
1064: from = position - _machine.getFindWord().length();
1065: else
1066: from = position + _machine.getFindWord().length();
1067: _selectFoundItem(from, to);
1068: }
1069:
1070: /** Will select the searched-for text. Originally highlighted the text, but we ran into problems
1071: * with the document remove method changing the view to where the cursor was located, resulting in
1072: * replace constantly jumping from the replaced text back to the cursor. There was a
1073: * removePreviousHighlight method which was removed since selections are removed automatically upon
1074: * a caret change.
1075: */
1076: private void _selectFoundItem(int from, int to) {
1077: _defPane.centerViewOnOffset(from);
1078: _defPane.select(from, to);
1079:
1080: // Found this little statement that will show the selected text in _defPane without giving _defPane
1081: // focus, allowing the user to hit enter repeatedly and change the document while finding next.
1082: SwingUtilities.invokeLater(new Runnable() {
1083: public void run() {
1084: _defPane.getCaret().setSelectionVisible(true);
1085: }
1086: });
1087: // _defPane.centerViewOnOffset(from);
1088: }
1089:
1090: // private void _close() { hide(); }
1091:
1092: // public void hide() {
1093: // System.err.println("*** Called hide ***");
1094: // if (_open)
1095: // _frame.uninstallFindReplaceDialog(this);
1096: // //super.hide();
1097: // }
1098:
1099: // private ContinueCommand CONFIRM_CONTINUE = new ContinueCommand() {
1100: // public boolean shouldContinue() {
1101: // String text = "The search has reached the end of the document.\n" +
1102: // "Continue searching from the start?";
1103: // int rc = JOptionPane.showConfirmDialog(FindReplacePanel.this,
1104: // text,
1105: // "Continue search?",
1106: // JOptionPane.YES_NO_OPTION);
1107: //
1108: // switch (rc) {
1109: // case JOptionPane.YES_OPTION:
1110: // return true;
1111: // case JOptionPane.NO_OPTION:
1112: // return false;
1113: // default:
1114: // throw new RuntimeException("Invalid rc: " + rc);
1115: // }
1116: //
1117: // }
1118: // };
1119:
1120: /** We lost ownership of what we put in the clipboard. */
1121: public void lostOwnership(Clipboard clipboard, Transferable contents) {
1122: // ignore
1123: }
1124:
1125: /** Default cut action. */
1126: Action cutAction = new DefaultEditorKit.CutAction() {
1127: public void actionPerformed(ActionEvent e) {
1128: if (e.getSource() instanceof JTextComponent) {
1129: JTextComponent tc = (JTextComponent) e.getSource();
1130: if (tc.getSelectedText() != null) {
1131: super .actionPerformed(e);
1132: String s = edu.rice.cs.util.swing.Utilities
1133: .getClipboardSelection(FindReplacePanel.this );
1134: if (s != null && s.length() != 0) {
1135: ClipboardHistoryModel.singleton().put(s);
1136: }
1137: }
1138: }
1139: }
1140: };
1141:
1142: /** Default copy action. */
1143: Action copyAction = new DefaultEditorKit.CopyAction() {
1144: public void actionPerformed(ActionEvent e) {
1145: if (e.getSource() instanceof JTextComponent) {
1146: JTextComponent tc = (JTextComponent) e.getSource();
1147: if (tc.getSelectedText() != null) {
1148: super .actionPerformed(e);
1149: String s = edu.rice.cs.util.swing.Utilities
1150: .getClipboardSelection(FindReplacePanel.this );
1151: if (s != null && s.length() != 0) {
1152: ClipboardHistoryModel.singleton().put(s);
1153: }
1154: }
1155: }
1156: }
1157: };
1158:
1159: /***************** METHODS FOR TESTING PURPOSES ONLY ***********************/
1160: public DefinitionsPane getDefPane() {
1161: return _defPane;
1162: }
1163:
1164: public JButton getFindNextButton() {
1165: return _findNextButton;
1166: }
1167:
1168: }
|