/*
* Copyright 2008 Ayman Al-Sairafi ayman.alsairafi@gmail.com
*
* 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 jsyntaxpane.actions;
import java.awt.event.ActionEvent;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
import javax.swing.text.JTextComponent;
import javax.swing.text.TextAction;
import jsyntaxpane.SyntaxDocument;
import jsyntaxpane.util.Configuration;
/**
* Finder class. This class contains the general Find, Find Next,
* Find Previous, and the Find Marker Actions.
*
* Note that all Actions are subclasses of this class because all actions
* require the find text to be shared among them. This is the best approach
* to have all Action classes share this same data.
*
* @author Ayman Al-Sairafi
*/
public class FindReplaceActions implements SyntaxAction {
private Pattern pattern = null;
private boolean wrap = true;
private final FindDialogAction findDialogAction = new FindDialogAction();
private final FindNextAction findNextAction = new FindNextAction();
private ReplaceDialog dlg;
public FindReplaceActions() {
}
public TextAction getFindDialogAction() {
return findDialogAction;
}
public TextAction getFindNextAction() {
return findNextAction;
}
public void config(Configuration config, String prefix, String name) {
}
public TextAction getAction(String key) {
if(key.equals("FIND") ) {
return findDialogAction;
} else if(key.equals("REPLACE")) {
return findDialogAction;
} else if(key.equals("FIND_NEXT")) {
return findNextAction;
} else {
throw new IllegalArgumentException("Bad Action: " + key);
}
}
/**
* This class displays the Find Dialog. The dialog will use the pattern
* and will update it once it is closed.
*/
class FindDialogAction extends TextAction {
public FindDialogAction() {
super("FIND_ACTION");
}
public void actionPerformed(ActionEvent e) {
JTextComponent target = getTextComponent(e);
if (target != null) {
showDialog(target);
}
}
}
/**
* This class performs a Find Next operation by using the current pattern
*/
class FindNextAction extends TextAction {
public FindNextAction() {
super("FIND_NEXT");
}
public void actionPerformed(ActionEvent e) {
// if we did not start searching, return now
if (pattern == null) {
return;
}
JTextComponent target = getTextComponent(e);
doFindNext(target);
}
}
/**
* Display an OptionPane dialog that the search string is not found
*/
public void msgNotFound() {
JOptionPane.showMessageDialog(null,
"Search String " + pattern + " not found",
"Find", JOptionPane.INFORMATION_MESSAGE);
}
/**
* Show the dialog
* @param targetFrame
* @param sDoc
* @param target
*/
private void showDialog(JTextComponent target) {
if (dlg == null) {
dlg = new ReplaceDialog(target, FindReplaceActions.this);
}
dlg.setVisible(true);
}
/**
* Perform a FindNext operation on the given text component. Position
* the caret at the start of the next found pattern
* @param target
*/
public void doFindNext(JTextComponent target) {
if (target == null || pattern == null) {
return;
}
SyntaxDocument sDoc = ActionUtils.getSyntaxDocument(target);
if (sDoc == null) {
return;
}
int start = target.getCaretPosition() + 1;
// we must advance the position by one, otherwise we will find
// the same text again
if (start >= sDoc.getLength()) {
start = 0;
}
Matcher matcher = sDoc.getMatcher(pattern, start);
if (matcher != null && matcher.find()) {
// since we used an offset in the matcher, the matcher location
// MUST be offset by that location
target.select(matcher.start() + start, matcher.end() + start);
} else {
if (isWrap()) {
matcher = sDoc.getMatcher(pattern);
if (matcher != null && matcher.find()) {
target.select(matcher.start(), matcher.end());
} else {
msgNotFound();
}
} else {
msgNotFound();
}
}
}
/**
* Perform a replace all operation on the given component.
* Note that this create a new duplicate String big as the entire
* document and then assign it to the target text component
* @param target
* @param replacement
*/
public void replaceAll(JTextComponent target, String replacement) {
SyntaxDocument sDoc = ActionUtils.getSyntaxDocument(target);
if (pattern == null || sDoc == null) {
return;
}
Matcher matcher = sDoc.getMatcher(pattern);
String newText = matcher.replaceAll(replacement);
target.setText(newText);
}
// - Getters and setters -------------------------------------------------
public Pattern getPattern() {
return pattern;
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
public boolean isWrap() {
return wrap;
}
public void setWrap(boolean wrap) {
this.wrap = wrap;
}
}