/*
* ===================================================
* This program contains code from the book "Swing"
* 2nd Edition by Matthew Robinson and Pavel Vorobiev
* http://www.spindoczine.com/sbe
* ===================================================
*
* This class (Mostly) comes from the manning "Swing" book
* by Matthew Robinson and Pavel Vorobiev. I'd come across it
* and only after i started writing my own finder/replacer..
* So, rather than waste time reinventing the wheel,
* I modified it to fit my needs.
* -BT
*
*/
package net.atlanticbb.tantlinger.ui.text.dialogs;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Vector;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import net.atlanticbb.tantlinger.i18n.I18n;
import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
/**
* A find and replace dialog for JTextComponents
*
*/
public class TextFinderDialog extends JDialog
{
/**
*
*/
private static final long serialVersionUID = 1L;
private static final I18n i18n = I18n.getInstance("net.atlanticbb.tantlinger.ui.text.dialogs");
public static final char[] WORD_SEPARATORS = {' ', '\t', '\n',
'\r', '\f', '.', ',', ':', '-', '(', ')', '[', ']', '{',
'}', '<', '>', '/', '|', '\\', '\'', '\"'};
public static final int FIND = 0;
public static final int REPLACE = 1;
protected Frame owner;
protected JTextComponent monitor;
protected JTabbedPane tb;
protected JTextField txtFind1;
protected JTextField txtFind2;
protected Document docFind;
protected Document docReplace;
protected ButtonModel modelWord;
protected ButtonModel modelCase;
protected ButtonModel modelUp;
protected ButtonModel modelDown;
//private TextEditPopupManager popupManager = new TextEditPopupManager();
private TextEditPopupManager popupManager = TextEditPopupManager.getInstance();
//protected int searchIndex = -1;
protected boolean searchUp = false;
protected String searchData;
private static final String TITLE = i18n.str("find_and_replace"); //$NON-NLS-1$
//private JTextComponent textComp;
public TextFinderDialog(Frame owner, JTextComponent tc, int index)
{
super(owner, TITLE, false);
init(tc, index);
}
public TextFinderDialog(Dialog owner, JTextComponent tc, int index)
{
super(owner, TITLE, false);
init(tc, index);
}
private void init(JTextComponent tc, int index)
{
setJTextComponent(tc);
tb = new JTabbedPane();
// "Find" panel
JPanel p1 = new JPanel(new BorderLayout());
JPanel pc1 = new JPanel(new BorderLayout());
JPanel pf = new JPanel();
pf.setLayout(new DialogLayout(20, 5));
pf.setBorder(new EmptyBorder(8, 5, 8, 0));
pf.add(new JLabel(i18n.str("find_what")));
txtFind1 = new JTextField();
docFind = txtFind1.getDocument();
pf.add(txtFind1);
pc1.add(pf, BorderLayout.CENTER);
popupManager.registerJTextComponent(txtFind1);
JPanel po = new JPanel(new GridLayout(2, 2, 8, 2));
po.setBorder(new TitledBorder(new EtchedBorder(), i18n.str("options")));
JCheckBox chkWord = new JCheckBox(i18n.str("whole_words_only"));
chkWord.setMnemonic('w');
modelWord = chkWord.getModel();
po.add(chkWord);
ButtonGroup bg = new ButtonGroup();
JRadioButton rdUp = new JRadioButton(i18n.str("search_up"));
rdUp.setMnemonic('u');
modelUp = rdUp.getModel();
bg.add(rdUp);
po.add(rdUp);
JCheckBox chkCase = new JCheckBox(i18n.str("match_case"));
chkCase.setMnemonic('c');
modelCase = chkCase.getModel();
po.add(chkCase);
JRadioButton rdDown = new JRadioButton(i18n.str("search_down"), true);
rdDown.setMnemonic('d');
modelDown = rdDown.getModel();
bg.add(rdDown);
po.add(rdDown);
pc1.add(po, BorderLayout.SOUTH);
p1.add(pc1, BorderLayout.CENTER);
JPanel p01 = new JPanel(new FlowLayout());
JPanel p = new JPanel(new GridLayout(2, 1, 2, 8));
ActionListener findAction = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
findNext(false, true);
}
};
JButton btFind = new JButton(i18n.str("find_next"));
btFind.addActionListener(findAction);
btFind.setMnemonic('f');
p.add(btFind);
ActionListener closeAction = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
setVisible(false);
//txtFind1 = null;
//txtFind2 = null;
}
};
JButton btClose = new JButton(i18n.str("close"));
btClose.addActionListener(closeAction);
btClose.setDefaultCapable(true);
p.add(btClose);
p01.add(p);
p1.add(p01, BorderLayout.EAST);
tb.addTab(i18n.str("find"), p1);
// "Replace" panel
JPanel p2 = new JPanel(new BorderLayout());
JPanel pc2 = new JPanel(new BorderLayout());
JPanel pc = new JPanel();
pc.setLayout(new DialogLayout(20, 5));
pc.setBorder(new EmptyBorder(8, 5, 8, 0));
pc.add(new JLabel(i18n.str("find_what")));
txtFind2 = new JTextField();
txtFind2.setDocument(docFind);
pc.add(txtFind2);
popupManager.registerJTextComponent(txtFind2);
pc.add(new JLabel(i18n.str("replace")));
JTextField txtReplace = new JTextField();
docReplace = txtReplace.getDocument();
pc.add(txtReplace);
pc2.add(pc, BorderLayout.CENTER);
popupManager.registerJTextComponent(txtReplace);
po = new JPanel(new GridLayout(2, 2, 8, 2));
po.setBorder(new TitledBorder(new EtchedBorder(), i18n.str("options")));
chkWord = new JCheckBox(i18n.str("whole_words_only"));
chkWord.setMnemonic('w');
chkWord.setModel(modelWord);
po.add(chkWord);
bg = new ButtonGroup();
rdUp = new JRadioButton(i18n.str("search_up"));
rdUp.setMnemonic('u');
rdUp.setModel(modelUp);
bg.add(rdUp);
po.add(rdUp);
chkCase = new JCheckBox(i18n.str("match_case"));
chkCase.setMnemonic('c');
chkCase.setModel(modelCase);
po.add(chkCase);
rdDown = new JRadioButton(i18n.str("search_down"), true);
rdDown.setMnemonic('d');
rdDown.setModel(modelDown);
bg.add(rdDown);
po.add(rdDown);
pc2.add(po, BorderLayout.SOUTH);
p2.add(pc2, BorderLayout.CENTER);
JPanel p02 = new JPanel(new FlowLayout());
p = new JPanel(new GridLayout(3, 1, 2, 8));
ActionListener replaceAction = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
findNext(true, true);
}
};
JButton btReplace = new JButton(i18n.str("replace"));
btReplace.addActionListener(replaceAction);
btReplace.setMnemonic('r');
p.add(btReplace);
ActionListener replaceAllAction = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
int counter = 0;
while (true)
{
int result = findNext(true, false);
if (result < 0) // error
return;
else if (result == 0) // no more
break;
counter++;
}
JOptionPane.showMessageDialog(owner,
counter+ " " + i18n.str("replacements_prompt"), "Info",
JOptionPane.INFORMATION_MESSAGE);
}
};
JButton btReplaceAll = new JButton(i18n.str("replace_all"));
btReplaceAll.addActionListener(replaceAllAction);
btReplaceAll.setMnemonic('a');
p.add(btReplaceAll);
btClose = new JButton(i18n.str("close"));
btClose.addActionListener(closeAction);
btClose.setDefaultCapable(true);
p.add(btClose);
p02.add(p);
p2.add(p02, BorderLayout.EAST);
// Make button columns the same size
p01.setPreferredSize(p02.getPreferredSize());
tb.addTab(i18n.str("replace"), p2);
tb.setSelectedIndex(index);
getContentPane().add(tb, BorderLayout.CENTER);
WindowListener flst = new WindowAdapter()
{
public void windowActivated(WindowEvent e)
{
//searchIndex = -1;
if (tb.getSelectedIndex()==0)
if(!txtFind1.hasFocus())
txtFind1.requestFocusInWindow();
else
if(!txtFind2.hasFocus())
txtFind2.requestFocusInWindow();
}
public void windowDeactivated(WindowEvent e)
{
searchData = null;
}
};
addWindowListener(flst);
pack();
setResizable(false);
}
public void setJTextComponent(JTextComponent tc)
{
monitor = tc;
if(!monitor.hasFocus())
monitor.requestFocusInWindow();
}
public JTextComponent getJTextComponent()
{
return monitor;
}
public void setSelectedIndex(int index)
{
tb.setSelectedIndex(index);
//setVisible(true);
//searchIndex = -1;
}
public int getSelectedIndex()
{
return tb.getSelectedIndex();
}
public void show(int index)
{
setSelectedIndex(index);
setLocationRelativeTo(owner);
setVisible(true);
if(!monitor.hasFocus())
monitor.requestFocusInWindow();
}
public int findNext(boolean doReplace, boolean showWarnings)
{
int pos = monitor.getCaretPosition();
String key = "";
try { key = docFind.getText(0, docFind.getLength()); }
catch(BadLocationException ex){}
if(key.length()==0)
{
warning(i18n.str("no_target_prompt"));
return -1;
}
if(modelWord.isSelected())
{
for(int k=0; k<WORD_SEPARATORS.length; k++)
{
if(key.indexOf(WORD_SEPARATORS[k]) >= 0)
{
warning(i18n.str("illegal_character_prompt") +
" \'"+WORD_SEPARATORS[k]+"\'");
return -1;
}
}
}
String replacement = "";
if(doReplace)
{
try
{
replacement = docReplace.getText(0, docReplace.getLength());
}
catch (BadLocationException ex) {}
}
if(modelUp.isSelected() != searchUp)
searchUp = modelUp.isSelected();
String searchData = "";
try{
searchData = monitor.getDocument().getText(
0, monitor.getDocument().getLength());
}
catch(Exception ex)
{
ex.printStackTrace();
return -1;
}
if(!modelCase.isSelected())
{
searchData = searchData.toLowerCase();
key = key.toLowerCase();
}
int index;
while(true)
{
if(!searchUp)
index = searchData.indexOf(key, pos);
else
index = searchData.lastIndexOf(key, pos - 1);
if(index < 0 || index >= searchData.length())
{
if(showWarnings)
warning(i18n.str("text_not_found"));
return 0;
}
if(modelWord.isSelected())
{
boolean s1 = index > 0;
boolean b1 = s1 && !isSeparator(searchData.charAt(index - 1));
boolean s2 = (index + key.length()) < searchData.length();
boolean b2 = s2 && !isSeparator(searchData.charAt(index + key.length()));
if(b1 || b2)
{
if(!searchUp && s2)
{
pos = index + key.length();
continue;
}
if(searchUp && s1)
{
pos = index;
continue;
}
if(showWarnings)
warning(i18n.str("text_not_found"));
return 0;
}
}
break;
}
if(doReplace)
{
setSelection(index, index + key.length(), searchUp);
monitor.replaceSelection(replacement);
setSelection(index, index+replacement.length(), searchUp);
}
else
setSelection(index, index + key.length(), searchUp);
return 1;
}
public void setSelection(int xStart, int xFinish, boolean moveUp)
{
if(moveUp)
{
monitor.setCaretPosition(xFinish);
monitor.moveCaretPosition(xStart);
}
else
{
monitor.setCaretPosition(xStart);
monitor.moveCaretPosition(xFinish);
}
}
protected boolean isSeparator(char ch)
{
for(int k = 0; k < WORD_SEPARATORS.length; k++)
if(ch == WORD_SEPARATORS[k])
return true;
return false;
}
protected void warning(String message)
{
JOptionPane.showMessageDialog(owner,
message, TITLE, JOptionPane.INFORMATION_MESSAGE);
}
private class DialogLayout implements LayoutManager
{
protected static final int COMP_TWO_COL = 0;
protected static final int COMP_BIG = 1;
protected static final int COMP_BUTTON = 2;
protected int m_divider = -1;
protected int m_hGap = 10;
protected int m_vGap = 5;
protected Vector m_v = new Vector();
public DialogLayout() {}
public DialogLayout(int hGap, int vGap)
{
m_hGap = hGap;
m_vGap = vGap;
}
public void addLayoutComponent(String name, Component comp) {}
public void removeLayoutComponent(Component comp) {}
public Dimension preferredLayoutSize(Container parent)
{
m_v.removeAllElements();
int w = 0;
int h = 0;
int type = -1;
for (int k=0 ; k<parent.getComponentCount(); k++)
{
Component comp = parent.getComponent(k);
int newType = getLayoutType(comp);
if (k == 0)
type = newType;
if (type != newType)
{
Dimension d = preferredLayoutSize(m_v, type);
w = Math.max(w, d.width);
h += d.height + m_vGap;
m_v.removeAllElements();
type = newType;
}
m_v.addElement(comp);
}
Dimension d = preferredLayoutSize(m_v, type);
w = Math.max(w, d.width);
h += d.height + m_vGap;
h -= m_vGap;
Insets insets = parent.getInsets();
return new Dimension(w+insets.left+insets.right,
h+insets.top+insets.bottom);
}
protected Dimension preferredLayoutSize(Vector v, int type)
{
int w = 0;
int h = 0;
switch (type)
{
case COMP_TWO_COL:
int divider = getDivider(v);
for (int k=1 ; k<v.size(); k+=2)
{
Component comp = (Component)v.elementAt(k);
Dimension d = comp.getPreferredSize();
w = Math.max(w, d.width);
h += d.height + m_vGap;
}
h -= m_vGap;
return new Dimension(divider+w, h);
case COMP_BIG:
for (int k=0 ; k<v.size(); k++)
{
Component comp = (Component)v.elementAt(k);
Dimension d = comp.getPreferredSize();
w = Math.max(w, d.width);
h += d.height + m_vGap;
}
h -= m_vGap;
return new Dimension(w, h);
case COMP_BUTTON:
Dimension d = getMaxDimension(v);
w = d.width + m_hGap;
h = d.height;
return new Dimension(w*v.size()-m_hGap, h);
}
throw new IllegalArgumentException("Illegal type "+type); //$NON-NLS-1$
}
public Dimension minimumLayoutSize(Container parent)
{
return preferredLayoutSize(parent);
}
public void layoutContainer(Container parent)
{
m_v.removeAllElements();
int type = -1;
Insets insets = parent.getInsets();
int w = parent.getWidth() - insets.left - insets.right;
int x = insets.left;
int y = insets.top;
for (int k=0 ; k<parent.getComponentCount(); k++)
{
Component comp = parent.getComponent(k);
int newType = getLayoutType(comp);
if (k == 0)
type = newType;
if (type != newType)
{
y = layoutComponents(m_v, type, x, y, w);
m_v.removeAllElements();
type = newType;
}
m_v.addElement(comp);
}
y = layoutComponents(m_v, type, x, y, w);
m_v.removeAllElements();
}
protected int layoutComponents(Vector v, int type, int x, int y, int w)
{
switch (type)
{
case COMP_TWO_COL:
int divider = getDivider(v);
for (int k=1 ; k<v.size(); k+=2)
{
Component comp1 = (Component)v.elementAt(k-1);
Component comp2 = (Component)v.elementAt(k);
Dimension d = comp2.getPreferredSize();
comp1.setBounds(x, y, divider, d.height);
comp2.setBounds(x+divider, y, w-divider, d.height);
y += d.height + m_vGap;
}
//y -= m_vGap;
return y;
case COMP_BIG:
for (int k=0 ; k<v.size(); k++)
{
Component comp = (Component)v.elementAt(k);
Dimension d = comp.getPreferredSize();
comp.setBounds(x, y, w, d.height);
y += d.height + m_vGap;
}
//y -= m_vGap;
return y;
case COMP_BUTTON:
Dimension d = getMaxDimension(v);
int ww = d.width*v.size() + m_hGap*(v.size()-1);
int xx = x + Math.max(0, (w - ww)/2);
for (int k=0 ; k<v.size(); k++)
{
Component comp = (Component)v.elementAt(k);
comp.setBounds(xx, y, d.width, d.height);
xx += d.width + m_hGap;
}
return y + d.height;
}
throw new IllegalArgumentException("Illegal type "+type); //$NON-NLS-1$
}
public int getHGap()
{
return m_hGap;
}
public int getVGap()
{
return m_vGap;
}
public void setDivider(int divider)
{
if (divider > 0)
m_divider = divider;
}
public int getDivider()
{
return m_divider;
}
protected int getDivider(Vector v)
{
if (m_divider > 0)
return m_divider;
int divider = 0;
for (int k=0 ; k<v.size(); k+=2)
{
Component comp = (Component)v.elementAt(k);
Dimension d = comp.getPreferredSize();
divider = Math.max(divider, d.width);
}
divider += m_hGap;
return divider;
}
protected Dimension getMaxDimension(Vector v)
{
int w = 0;
int h = 0;
for (int k=0 ; k<v.size(); k++)
{
Component comp = (Component)v.elementAt(k);
Dimension d = comp.getPreferredSize();
w = Math.max(w, d.width);
h = Math.max(h, d.height);
}
return new Dimension(w, h);
}
protected int getLayoutType(Component comp)
{
if (comp instanceof AbstractButton)
return COMP_BUTTON;
else if (comp instanceof JPanel ||
comp instanceof JScrollPane ||
comp instanceof JTabbedPane)
return COMP_BIG;
else
return COMP_TWO_COL;
}
}
}