001: // Copyright (c) 2000, 2005 BlueJ Group, Deakin University
002: //
003: // This software is made available under the terms of the "MIT License"
004: // A copy of this license is included with this source distribution
005: // in "license.txt" and is also available at:
006: // http://www.opensource.org/licenses/mit-license.html
007: // Any queries should be directed to Michael Kolling mik@bluej.org
008:
009: package bluej.editor.moe;
010:
011: import java.awt.*;
012: import java.awt.event.*;
013:
014: import javax.swing.*;
015: import javax.swing.event.*;
016:
017: import bluej.Config;
018: import bluej.utility.EscapeDialog;
019:
020: /**
021: * The Finder class implements the find and replace functionality of the Moe editor.
022: * It provides both the user interface dialogue and the high level implementation
023: * of the find and replace functionality.
024: *
025: * @author Michael Kolling
026: * @author Bruce Quig
027: * @version $Id: Finder.java 3357 2005-05-02 03:23:33Z davmac $
028: */
029:
030: public class Finder extends EscapeDialog implements ActionListener,
031: DocumentListener {
032: static final String title = Config.getString("editor.find.title");
033: static final String findLabel = Config
034: .getString("editor.find.find.label");
035: static final String replaceLabel = Config
036: .getString("editor.find.replace.label");
037:
038: // -------- CONSTANTS --------
039:
040: // search direction for the finder
041: static final String UP = "up";
042: static final String DOWN = "down";
043:
044: // -------- INSTANCE VARIABLES --------
045:
046: private boolean searchFound; // true if last find was successfull
047: private boolean replacing;
048:
049: private JButton findButton;
050: private JButton replaceButton;
051: private JButton replaceAllButton;
052: private JButton cancelButton;
053: private JTextField searchField;
054: private JTextField replaceField;
055: private JCheckBox wholeWord;
056: private JCheckBox ignoreCase;
057: private ButtonGroup directionButtons;
058:
059: private MoeEditor editor;
060:
061: // ------------- METHODS --------------
062:
063: public Finder() {
064: super ((Frame) null, title, true);
065: searchFound = true;
066: makeDialog();
067: }
068:
069: /**
070: * Ask the user for input of search details via a dialogue.
071: *
072: */
073: public void show(MoeEditor currentEditor, String selection,
074: boolean replace) {
075: editor = currentEditor;
076: replacing = replace;
077: getRootPane().setDefaultButton(findButton);
078:
079: if (selection != null && selection.length() > 0) {
080: setSearchString(selection);
081: replaceButton.setEnabled(true);
082: } else
083: replaceButton.setEnabled(false);
084:
085: if (!replacing)
086: replaceField.setText("");
087:
088: searchField.selectAll();
089: searchField.requestFocus();
090:
091: setVisible(true);
092: }
093:
094: /**
095: * search for the next instance of a text string
096: */
097: private void find() {
098: searchFound = editor.findString(getSearchString(),
099: getSearchBack(), getIgnoreCase(), getWholeWord(),
100: !searchFound);
101: replaceButton.setEnabled(searchFound);
102: if (searchFound && replacing)
103: getRootPane().setDefaultButton(replaceButton);
104: }
105:
106: /**
107: * replaces selected text with the contents of the replaceField and return
108: * next instance of the searchString.
109: */
110: private void replace() {
111: String replaceText = smartFormat(editor.getSelectedText(),
112: replaceField.getText());
113: editor.insertText(replaceText, getSearchBack());
114: find();
115: }
116:
117: /**
118: * Replace all instances of the search String with a replacement.
119: * -check for valid search criteria
120: * - TODO: get initial cursor pos
121: * -start at beginning
122: * -do initial find
123: * -replace until not found, no wrapping!
124: * -print out number of replacements (?)
125: * -TODO: return cursor/caret to original place
126: */
127: private void replaceAll() {
128: String searchString = getSearchString();
129: String replaceString = replaceField.getText();
130:
131: int count = 0;
132: if (getSearchBack()) {
133: while (editor.doFindBackward(searchString, getIgnoreCase(),
134: getWholeWord(), false)) {
135: editor.insertText(smartFormat(editor.getSelectedText(),
136: replaceString), true);
137: count++;
138: }
139: } else {
140: while (editor.doFind(searchString, getIgnoreCase(),
141: getWholeWord(), false)) {
142: editor.insertText(smartFormat(editor.getSelectedText(),
143: replaceString), false);
144: count++;
145: }
146: }
147: if (count > 0)
148: //editor.writeMessage("Replaced " + count + " instances of " + searchString);
149: editor.writeMessage(Config
150: .getString("editor.replaceAll.replaced")
151: + count
152: + Config.getString("editor.replaceAll.intancesOf")
153: + searchString);
154: else
155: //editor.writeMessage("String " + searchString + " not found. Nothing replaced.");
156: editor
157: .writeMessage(Config
158: .getString("editor.replaceAll.string")
159: + searchString
160: + Config
161: .getString("editor.replaceAll.notFoundNothingReplaced"));
162: }
163:
164: /**
165: * Replace the text currently selected in the editor with
166: */
167: private String smartFormat(String original, String replacement) {
168: if (original == null || replacement == null)
169: return replacement;
170:
171: // only do smart stuff if search and replace strings were entered in lowercase.
172: // check here. if not lowercase, just return.
173:
174: String search = getSearchString();
175: if (!isLowerCase(replacement) || !isLowerCase(search))
176: return replacement;
177:
178: if (isUpperCase(original))
179: return replacement.toUpperCase();
180: if (isTitleCase(original))
181: return Character.toTitleCase(replacement.charAt(0))
182: + replacement.substring(1);
183: else
184: return replacement;
185: }
186:
187: /**
188: * True if the string is in lower case.
189: */
190: public boolean isLowerCase(String s) {
191: for (int i = 0; i < s.length(); i++) {
192: if (!Character.isLowerCase(s.charAt(i)))
193: return false;
194: }
195: return true;
196: }
197:
198: /**
199: * True if the string is in Upper case.
200: */
201: public boolean isUpperCase(String s) {
202: for (int i = 0; i < s.length(); i++) {
203: if (!Character.isUpperCase(s.charAt(i)))
204: return false;
205: }
206: return true;
207: }
208:
209: /**
210: * True if the string is in title case.
211: */
212: public boolean isTitleCase(String s) {
213: if (s.length() < 2)
214: return false;
215: return Character.isUpperCase(s.charAt(0))
216: && Character.isLowerCase(s.charAt(1));
217: }
218:
219: /**
220: * set the search string
221: */
222: public void setSearchString(String s) {
223: searchField.setText(s);
224: }
225:
226: /**
227: * return the last search string
228: */
229: public String getSearchString() {
230: return searchField.getText();
231: }
232:
233: /**
234: * return true if the current search direction is backward
235: */
236: private boolean getSearchBack() {
237: return directionButtons.getSelection().getActionCommand() == UP;
238: }
239:
240: /**
241: * return true if "Ignore case" search is selected
242: */
243: public boolean getIgnoreCase() {
244: return ignoreCase.isSelected();
245: }
246:
247: /**
248: * return true if "whole word" search is selected
249: */
250: public boolean getWholeWord() {
251: return wholeWord.isSelected();
252: }
253:
254: /**
255: * set last search found
256: */
257: public void setSearchFound(boolean found) {
258: searchFound = found;
259: }
260:
261: /**
262: * return info whether the last search was successful
263: */
264: public boolean getSearchFound() {
265: return searchFound;
266: }
267:
268: // === Actionlistener interface ===
269: /**
270: * A button was pressed. Find out which one and do the appropriate
271: * thing.
272: */
273: public void actionPerformed(ActionEvent evt) {
274: Object src = evt.getSource();
275: if (src == findButton)
276: find();
277: else if (src == replaceButton)
278: replace();
279: else if (src == replaceAllButton)
280: replaceAll();
281: else if (src == cancelButton)
282: setVisible(false);
283: }
284:
285: // === Documentlistener interface ===
286: /**
287: * The search text was changed.
288: */
289: public void changedUpdate(DocumentEvent evt) {
290: }
291:
292: public void insertUpdate(DocumentEvent evt) {
293: findButton.setEnabled(true);
294: replaceAllButton.setEnabled(true);
295: }
296:
297: public void removeUpdate(DocumentEvent evt) {
298: if (getSearchString().length() == 0) {
299: findButton.setEnabled(false);
300: replaceAllButton.setEnabled(false);
301: }
302: }
303:
304: private void makeDialog() {
305: addWindowListener(new WindowAdapter() {
306: public void windowClosing(WindowEvent E) {
307: setVisible(false);
308: }
309: });
310:
311: // add search and replace text fields with labels
312:
313: JPanel textPanel = new JPanel();
314: textPanel.setLayout(new BoxLayout(textPanel, BoxLayout.Y_AXIS));
315: textPanel.setBorder(BorderFactory.createEmptyBorder(10, 20, 20,
316: 20));
317: {
318: JPanel findPanel = new JPanel(new BorderLayout());
319: findPanel.add(new JLabel(findLabel), BorderLayout.WEST);
320: searchField = new JTextField(16);
321: searchField.getDocument().addDocumentListener(this );
322: findPanel.add(searchField, BorderLayout.CENTER);
323: textPanel.add(findPanel);
324:
325: textPanel.add(Box.createVerticalStrut(6));
326:
327: JPanel replacePanel = new JPanel(new BorderLayout());
328: replacePanel.add(new JLabel(replaceLabel),
329: BorderLayout.WEST);
330: replaceField = new JTextField(16);
331: replacePanel.add(replaceField, BorderLayout.CENTER);
332: textPanel.add(replacePanel);
333:
334: textPanel.add(Box.createVerticalStrut(6));
335:
336: Box togglesBox = new Box(BoxLayout.X_AXIS);
337: {
338: Box optionBox = new Box(BoxLayout.Y_AXIS);
339: {
340: ignoreCase = new JCheckBox(Config
341: .getString("editor.find.ignoreCase"), true);
342: optionBox.add(ignoreCase);
343: optionBox.add(Box.createVerticalStrut(6));
344: wholeWord = new JCheckBox(Config
345: .getString("editor.find.wholeWord"));
346: optionBox.add(wholeWord);
347: }
348: togglesBox.add(optionBox);
349:
350: Box directionBox = new Box(BoxLayout.Y_AXIS);
351: {
352: directionButtons = new ButtonGroup();
353: JToggleButton dirUp = new JRadioButton(Config
354: .getString("editor.find.up"));
355: dirUp.setActionCommand(UP);
356: directionButtons.add(dirUp);
357: directionBox.add(dirUp);
358: directionBox.add(Box.createVerticalStrut(6));
359: JToggleButton dirDown = new JRadioButton(Config
360: .getString("editor.find.down"), true);
361: dirDown.setActionCommand(DOWN);
362: directionButtons.add(dirDown);
363: directionBox.add(dirDown);
364: }
365: togglesBox.add(directionBox);
366: }
367: textPanel.add(togglesBox);
368: }
369: getContentPane().add(textPanel, BorderLayout.CENTER);
370:
371: // add buttons
372:
373: JPanel buttonPanel = new JPanel(new GridLayout(0, 1, 0, 5));
374: buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 10,
375: 10, 10));
376:
377: findButton = new JButton(Config
378: .getString("editor.find.findNext"));
379: findButton.setEnabled(false);
380: buttonPanel.add(findButton);
381: findButton.addActionListener(this );
382:
383: replaceButton = new JButton(Config
384: .getString("editor.find.replace"));
385: buttonPanel.add(replaceButton);
386: replaceButton.setEnabled(false);
387: replaceButton.addActionListener(this );
388:
389: replaceAllButton = new JButton(Config
390: .getString("editor.find.replaceAll"));
391: replaceAllButton.setEnabled(false);
392: buttonPanel.add(replaceAllButton);
393: replaceAllButton.addActionListener(this );
394:
395: cancelButton = new JButton(Config.getString("close"));
396: buttonPanel.add(cancelButton);
397: cancelButton.addActionListener(this );
398: getContentPane().add("East", buttonPanel);
399:
400: pack();
401: }
402:
403: }
|