// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.search;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import org.infinity.gui.Center;
import org.infinity.gui.ChildFrame;
import org.infinity.gui.WindowBlocker;
import org.infinity.icon.Icons;
public final class SearchMaster extends JPanel implements Runnable, ActionListener
{
private final JButton bnext = new JButton("Find Next", Icons.getIcon(Icons.ICON_FIND_AGAIN_16));
private final JButton bclear = new JButton("New Search", Icons.getIcon(Icons.ICON_NEW_16));
private final JCheckBox cbwhole = new JCheckBox("Match whole word only");
private final JCheckBox cbcase = new JCheckBox("Match case");
private final JCheckBox cbregex = new JCheckBox("Use regular expressions");
private final JFrame container;
private final JRadioButton rbup = new JRadioButton("Up");
private final JRadioButton rbdown = new JRadioButton("Down");
private final JTextField tfinput = new JTextField(15);
private final SearchClient slave;
private final WindowBlocker blocker;
private Thread thread;
private int index;
public static void createAsFrame(SearchClient slave, String title, Component parent)
{
ChildFrame frame = new ChildFrame("Find: " + title, true);
frame.setIconImage(Icons.getIcon(Icons.ICON_FIND_16).getImage());
JPanel pane = (JPanel)frame.getContentPane();
pane.setLayout(new BorderLayout());
pane.add(new SearchMaster(slave, frame), BorderLayout.CENTER);
frame.pack();
Center.center(frame, parent.getBounds());
frame.setVisible(true);
}
public static JPanel createAsPanel(SearchClient slave, JFrame container)
{
return new SearchMaster(slave, container);
}
private static void syncNotify(Object o)
{
synchronized (o) {
o.notifyAll();
}
}
private SearchMaster(SearchClient slave, JFrame container)
{
this.slave = slave;
this.container = container;
blocker = new WindowBlocker(container);
container.getRootPane().setDefaultButton(bnext);
bclear.setMnemonic('n');
bnext.addActionListener(this);
tfinput.addActionListener(this);
bclear.addActionListener(this);
ButtonGroup gb = new ButtonGroup();
gb.add(rbup);
gb.add(rbdown);
rbup.setMnemonic('u');
rbdown.setMnemonic('d');
rbdown.setSelected(true);
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
setLayout(gbl);
JLabel label = new JLabel("Find what:");
label.setLabelFor(tfinput);
label.setDisplayedMnemonic('f');
tfinput.setMinimumSize(new Dimension(tfinput.getMinimumSize().width, bnext.getMinimumSize().height));
tfinput.setPreferredSize(
new Dimension(tfinput.getPreferredSize().width, bnext.getPreferredSize().height));
JPanel dirpanel = new JPanel();
dirpanel.add(new JPanel());
dirpanel.add(rbup);
dirpanel.add(rbdown);
dirpanel.setBorder(BorderFactory.createTitledBorder("Direction"));
JPanel matchpanel = new JPanel();
matchpanel.setLayout(new GridLayout(3, 1));
matchpanel.add(cbwhole);
matchpanel.add(cbcase);
matchpanel.add(cbregex);
cbwhole.setMnemonic('w');
cbcase.setMnemonic('m');
cbregex.setMnemonic('r');
JPanel bpanel = new JPanel();
bpanel.setLayout(new GridLayout(2, 1, 6, 6));
bpanel.add(bclear);
gbc.insets = new Insets(6, 6, 3, 3);
gbc.weightx = 0.0;
gbc.weighty = 0.0;
gbc.fill = GridBagConstraints.NONE;
gbl.setConstraints(label, gbc);
add(label);
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0;
gbc.gridwidth = 2;
gbc.insets.left = 3;
gbl.setConstraints(tfinput, gbc);
add(tfinput);
gbc.weightx = 0.0;
gbc.insets.right = 6;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbl.setConstraints(bnext, gbc);
add(bnext);
gbc.gridwidth = 2;
gbc.insets = new Insets(3, 6, 6, 3);
gbl.setConstraints(matchpanel, gbc);
add(matchpanel);
gbc.gridwidth = 1;
gbc.insets.left = 3;
gbl.setConstraints(dirpanel, gbc);
add(dirpanel);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.NORTH;
gbc.insets.right = 6;
gbl.setConstraints(bpanel, gbc);
add(bpanel);
}
// --------------------- Begin Interface ActionListener ---------------------
@Override
public void actionPerformed(ActionEvent event)
{
if (event.getSource() == bnext || event.getSource() == tfinput) {
if (thread == null || !thread.isAlive()) {
thread = new Thread(this);
thread.start();
}
else
syncNotify(slave);
}
else if (event.getSource() == bclear) {
rbdown.setSelected(true);
tfinput.setText("");
syncNotify(slave);
}
}
// --------------------- End Interface ActionListener ---------------------
// --------------------- Begin Interface Runnable ---------------------
@Override
public void run()
{
index = 0;
String term = tfinput.getText();
if (!cbregex.isSelected()) {
term = term.replaceAll("(\\W)", "\\\\$1");
}
if (cbwhole.isSelected()) {
term = ".*\\b" + term + "\\b.*";
} else {
term = ".*" + term + ".*";
}
Pattern regPattern;
try {
if (cbcase.isSelected()) {
regPattern = Pattern.compile(term, Pattern.DOTALL);
} else {
regPattern = Pattern.compile(term, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
}
} catch (PatternSyntaxException e) {
JOptionPane.showMessageDialog(this, "Syntax error in search string.", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
blocker.setBlocked(true);
bnext.setEnabled(false);
cbwhole.setEnabled(false);
cbcase.setEnabled(false);
tfinput.setEnabled(false);
while (true) {
String s = slave.getText(index);
if (s == null)
break;
if (regPattern.matcher(s).matches()) {
slave.hitFound(index);
blocker.setBlocked(false);
container.requestFocus();
bnext.setEnabled(true);
synchronized (slave) {
try {
slave.wait();
} catch (InterruptedException e) {
}
}
blocker.setBlocked(true);
bnext.setEnabled(false);
if (tfinput.getText().equals("")) {
term = null;
break;
}
}
if (rbdown.isSelected())
index++;
else
index--;
}
blocker.setBlocked(false);
bnext.setEnabled(true);
cbwhole.setEnabled(true);
cbcase.setEnabled(true);
tfinput.setEnabled(true);
if (term != null)
JOptionPane.showMessageDialog(this, "No more matches found", "Search complete",
JOptionPane.INFORMATION_MESSAGE);
}
// --------------------- End Interface Runnable ---------------------
}