/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui.dialog;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.util.Collection;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import com.rapidminer.gui.tools.ResourceAction;
import com.rapidminer.gui.tools.ResourceActionAdapter;
import com.rapidminer.gui.tools.ResourceLabel;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.gui.tools.dialogs.ButtonDialog;
import com.rapidminer.gui.tools.dialogs.ConfirmDialog;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.Tools;
/**
* The search dialog for searching strings in a {@link SearchableTextComponent}.
*
* @author Ingo Mierswa, Simon Fischer, Tobias Malbrecht
*/
public class SearchDialog extends ButtonDialog {
private static final long serialVersionUID = -1019890951712706875L;
private static class Result {
private final int start, end;
private Result(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public String toString() {
return "start: " + start + ", end: " + end;
}
}
private final JTextField patternField = new JTextField(20);
private final JTextField replaceField = new JTextField(20);
private final JCheckBox caseSensitive = new JCheckBox(new ResourceActionAdapter("case_sensitive"));
private final JCheckBox regExp = new JCheckBox(new ResourceActionAdapter("search_regular_expression"));
private final JRadioButton forwardRadioButton = new JRadioButton(new ResourceActionAdapter("search_forward"));
private final JRadioButton backwardRadioButton = new JRadioButton(new ResourceActionAdapter("search_backward"));
private transient final SearchableTextComponent textComponent;
public SearchDialog(Component owner, SearchableTextComponent textComponent) {
this(owner, textComponent, false);
}
public SearchDialog(Component owner, SearchableTextComponent textComponent, boolean allowReplace) {
super("search_replace", false);
this.textComponent = textComponent;
this.textComponent.requestFocus();
this.textComponent.setCaretPosition(0);
if (allowReplace) {
setTitle(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".search_replace.title"));
} else {
setTitle(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".search.title"));
}
JPanel searchPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(4, 0, 4, 0);
c.gridwidth = 1;
c.weightx = 1;
c.gridx = 0;
c.gridy = 0;
JLabel label = new ResourceLabel("search_what");
searchPanel.add(label, c);
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridx = 1;
c.gridy = 0;
searchPanel.add(patternField, c);
label.setLabelFor(patternField);
Collection<AbstractButton> buttons = new LinkedList<AbstractButton>();
final JButton search = new JButton(new ResourceAction("start_search") {
private static final long serialVersionUID = -7450018802479146549L;
public void actionPerformed(ActionEvent e) {
search();
}
});
buttons.add(search);
if (allowReplace) {
c.insets = new Insets(4, 0, 4, 0);
c.gridwidth = 1;
c.gridx = 0;
c.gridy = 1;
label = new ResourceLabel("replace_with");
searchPanel.add(label, c);
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridx = 1;
c.gridy = 1;
searchPanel.add(replaceField, c);
final JButton replaceButton = new JButton(new ResourceAction("start_replace") {
private static final long serialVersionUID = -5028551435610677265L;
public void actionPerformed(ActionEvent e) {
replace();
search();
}
});
buttons.add(replaceButton);
}
GridLayout settingsPanelLayout = new GridLayout(1, 2);
settingsPanelLayout.setVgap(0);
settingsPanelLayout.setHgap(0);
JPanel settingsPanel = new JPanel(settingsPanelLayout);
GridLayout directionPanelLayout = new GridLayout(2, 1);
directionPanelLayout.setVgap(0);
directionPanelLayout.setHgap(0);
JPanel directionPanel = new JPanel(directionPanelLayout);
directionPanel.setBorder(BorderFactory.createTitledBorder("Direction"));
ButtonGroup directionGroup = new ButtonGroup();
directionGroup.add(forwardRadioButton);
directionGroup.add(backwardRadioButton);
forwardRadioButton.setSelected(true);
directionPanel.add(forwardRadioButton);
directionPanel.add(backwardRadioButton);
settingsPanel.add(directionPanel);
JPanel optionsPanel = new JPanel(new GridLayout(2, 1));
optionsPanel.setBorder(BorderFactory.createTitledBorder("Options"));
optionsPanel.add(caseSensitive);
optionsPanel.add(regExp);
settingsPanel.add(optionsPanel);
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridx = 0;
c.gridy = 2;
searchPanel.add(settingsPanel, c);
buttons.add(makeCloseButton());
layoutDefault(searchPanel, buttons);
getRootPane().setDefaultButton(search);
}
private void search() {
String pattern = patternField.getText().trim();
if (pattern.length() == 0)
return;
int startPos = textComponent.getCaretPosition();
String text = textComponent.getText();
if (startPos > text.length())
startPos = 0;
if (forwardRadioButton.isSelected()) {
Result result = search(startPos, pattern, text, textComponent.canHandleCarriageReturn());
if (result == null) {
noMoreHits();
return;
} else {
textComponent.select(result.start, result.end);
}
} else {
Result lastResult = null;
int pos = 0;
while (true) {
Result result = search(pos, pattern, text, textComponent.canHandleCarriageReturn());
if (result != null) {
if (result.end < startPos) {
pos = result.start + 1;
lastResult = result;
} else {
break;
}
} else {
break;
}
}
if (lastResult == null) {
noMoreHits();
} else {
textComponent.select(lastResult.start, lastResult.end);
}
}
}
private void replace() {
textComponent.replaceSelection(replaceField.getText());
}
private Result search(int start, String pattern, String text, boolean canHandleCarriageReturn) {
if (!canHandleCarriageReturn) {
text = Tools.transformAllLineSeparators(text);
}
if (regExp.isSelected()) {
Matcher matcher = Pattern.compile(pattern, caseSensitive.isSelected() ? 0 : Pattern.CASE_INSENSITIVE).matcher(text.subSequence(start, text.length()));
if (matcher.find()) {
return new Result(start + matcher.start(), start + matcher.end());
} else {
return null;
}
} else {
if (!caseSensitive.isSelected()) {
text = text.toLowerCase();
pattern = pattern.toLowerCase();
}
int result = text.indexOf(pattern, start);
if (result == -1) {
return null;
} else {
return new Result(result, result + pattern.length());
}
}
}
private void noMoreHits() {
String restartAt = backwardRadioButton.isSelected() ? "end" : "beginning";
switch (SwingTools.showConfirmDialog("editor.search_replace.no_more_hits", ConfirmDialog.YES_NO_OPTION, restartAt)) {
case ConfirmDialog.YES_OPTION:
textComponent.setCaretPosition(backwardRadioButton.isSelected() ? textComponent.getText().replaceAll("\r","").length() : 0);
search();
break;
case ConfirmDialog.NO_OPTION:
default:
return;
}
}
}