package com.intuit.tank.tools.debugger;
/*
* #%L
* Intuit Tank Agent Debugger
* %%
* Copyright (C) 2011 - 2015 Intuit Inc.
* %%
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* #L%
*/
import java.awt.Dimension;
import java.awt.FontMetrics;
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.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import org.apache.commons.lang3.StringUtils;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
public class FindReplaceDialog extends JDialog implements ActionListener {
private static final long serialVersionUID = 1L;
public enum DialogType {
SEARCH, // Defines a search only dialog
REPLACE // Defines a search and replace dialog
}
// Private declarations
private AgentDebuggerFrame parent;
private JComboBox cbSearch;
private JComboBox cbReplace;
private JTextField tfSearchEditor;
private JTextField tfReplaceEditor;
private JButton btnFind;
private JButton btnReplace;
private JButton btnReplaceAll;
private JButton btnCancel;
private JCheckBox checkboxMatchCase;
private JCheckBox checkboxRegexp;
private JCheckBox checkboxWrap;
private int currentLine;
private int initialLine;
private int initialRunningStep;
/**
* Constructs a new find dialog according to the specified type of dialog requested. The dialog can be either a FIND
* dialog, either a REPLACE dialog. In both cases, components displayed remain the sames, but the ones specific to
* replace feature are grayed out.
*
* @param parent
* The window holder
* @param type
* The type of the dialog: FindReplace.FIND or FindReplace.REPLACE
* @param modal
* Displays dialog as a modal window if true
*/
public FindReplaceDialog(AgentDebuggerFrame parent, DialogType type) {
super(parent, type == DialogType.REPLACE ? "Replace" : "Find", true);
this.parent = parent;
cbSearch = new JComboBox();
cbSearch.setEditable(true);
cbReplace = new JComboBox();
cbReplace.setEditable(true);
KeyHandler handler = new KeyHandler();
tfSearchEditor = (JTextField) cbSearch.getEditor().getEditorComponent();
tfSearchEditor.addKeyListener(handler);
tfReplaceEditor = (JTextField) cbReplace.getEditor().getEditorComponent();
tfReplaceEditor.addKeyListener(handler);
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
getContentPane().setLayout(gridbag);
((JPanel) getContentPane()).setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
int gridX = 0;
int gridY = 0;
JLabel findLabel = new JLabel("Find");
buildConstraints(constraints, gridX, gridY, 1, 1, 0, 0);
constraints.anchor = GridBagConstraints.WEST;
gridbag.setConstraints(findLabel, constraints);
getContentPane().add(findLabel);
gridX++;
buildConstraints(constraints, gridX, gridY, 1, 1, 100, 0);
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.CENTER;
gridbag.setConstraints(cbSearch, constraints);
getContentPane().add(cbSearch);
gridX++;
btnFind = new JButton("Find");
btnFind.setToolTipText("Find text in scripts");
btnFind.setMnemonic('F');
btnFind.addActionListener(this);
buildConstraints(constraints, gridX, gridY, 1, 1, 0, 0);
constraints.anchor = GridBagConstraints.CENTER;
gridbag.setConstraints(btnFind, constraints);
getContentPane().add(btnFind);
getRootPane().setDefaultButton(btnFind);
gridX++;
btnCancel = new JButton("Cancel");
btnCancel.setMnemonic('C');
btnCancel.addActionListener(this);
buildConstraints(constraints, gridX, gridY, 1, 1, 0, 0);
constraints.anchor = GridBagConstraints.CENTER;
gridbag.setConstraints(btnCancel, constraints);
getContentPane().add(btnCancel);
gridY++;
gridX = 0;
if (type == DialogType.REPLACE) {
JLabel replaceLabel = new JLabel("Replace");
buildConstraints(constraints, gridX, gridY, 1, 1, 0, 0);
constraints.anchor = GridBagConstraints.WEST;
gridbag.setConstraints(replaceLabel, constraints);
getContentPane().add(replaceLabel);
gridX++;
buildConstraints(constraints, gridX, gridY, 1, 1, 100, 0);
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.CENTER;
gridbag.setConstraints(cbReplace, constraints);
getContentPane().add(cbReplace);
gridX++;
btnReplace = new JButton("Replace");
btnReplace.setToolTipText("REplace in script");
btnReplace.setMnemonic('R');
btnReplace.addActionListener(this);
buildConstraints(constraints, gridX, gridY, 1, 1, 0, 0);
constraints.anchor = GridBagConstraints.CENTER;
gridbag.setConstraints(btnReplace, constraints);
getContentPane().add(btnReplace);
gridX++;
btnReplaceAll = new JButton("Replace All");
btnReplaceAll.addActionListener(this);
buildConstraints(constraints, gridX, gridY, 1, 1, 0, 0);
constraints.anchor = GridBagConstraints.CENTER;
gridbag.setConstraints(btnReplaceAll, constraints);
getContentPane().add(btnReplaceAll);
btnReplace.addKeyListener(handler);
btnReplaceAll.addKeyListener(handler);
gridY++;
gridX = 0;
}
TitledBorder border = new TitledBorder("Options");
JPanel panel = new JPanel(new GridLayout(1, 4));
panel.setBorder(border);
checkboxWrap = new JCheckBox("Wrap Search");
panel.add(checkboxWrap);
checkboxMatchCase = new JCheckBox("Case sensitive");
panel.add(checkboxMatchCase);
checkboxRegexp = new JCheckBox("Regular expressions");
panel.add(checkboxRegexp);
buildConstraints(constraints, gridX, gridY, 4, 1, 100, 100);
constraints.anchor = GridBagConstraints.WEST;
gridbag.setConstraints(panel, constraints);
getContentPane().add(panel);
FontMetrics fm = getFontMetrics(getFont());
cbSearch.setPreferredSize(new Dimension(18 * fm.charWidth('m'),
(int) cbSearch.getPreferredSize().height));
cbReplace.setPreferredSize(new Dimension(18 * fm.charWidth('m'),
(int) cbReplace.getPreferredSize().height));
pack();
// setResizable(false);
WindowUtil.centerOnParent(this);
// patch by MJB 8/1/2002
btnFind.addKeyListener(handler);
btnCancel.addKeyListener(handler);
checkboxMatchCase.addKeyListener(handler);
checkboxRegexp.addKeyListener(handler);
}
@Override
public void setVisible(boolean b) {
tfSearchEditor.setText("");
if (tfReplaceEditor != null) {
tfReplaceEditor.setText("");
}
RSyntaxTextArea ta = parent.getScriptEditorTA();
if (b) {
currentLine = ta.getCaretLineNumber();
initialRunningStep = parent.getCurrentRunningStep();
initialLine = ta.getCaretLineNumber();
tfSearchEditor.requestFocusInWindow();
} else {
// if (initialRunningStep >= 0 && initialRunningStep < ta.getLineCount() -1) {
// ta.setActiveLineRange(initialRunningStep + 1, initialRunningStep + 1);
// } else {
// ta.setActiveLineRange(-1, -1);
// }
// parent.fireStepChanged(initialRunningStep);
// ta.setCurrentLine(initialLine);
// parent.repaint();
}
super.setVisible(b);
}
// Catch the action performed and then look for its source
// According to the source object we call appropriate methods
public void actionPerformed(ActionEvent evt) {
Object source = evt.getSource();
if (source == btnCancel) {
setVisible(false);
} else if (source == btnFind) {
doFind();
} else if (source == btnReplace) {
doReplace();
} else if (source == btnReplaceAll) {
doReplaceAll();
}
}
// replace all the occurences of search pattern by
// the replace one. If 'All Files' is checked, this is
// done in all the opened file in the component 'parent'
private void doReplaceAll() {
addReplaceHistory();
addSearchHistory();
// try {
// JextTextArea textArea = parent.getTextArea();
// setSettings();
// if (Search.replaceAll(textArea, 0, textArea.getLength()) == 0) {
// Utilities.beep();
// }
//
// } catch (Exception e) {
// // nothing
// } finally {
// Utilities.setCursorOnWait(this, false);
// }
}
// replaces specified search pattern by the replace one.
// this is done only if a match is found.
private void doReplace() {
addReplaceHistory();
addSearchHistory();
//
// try {
//
// JextTextArea textArea = parent.getTextArea();
// setSettings();
//
// if (!Search.replace(textArea))
// {
// Utilities.beep();
// } else
// find(textArea);
//
// } catch (Exception e) {
// // nothing
// } finally {
// Utilities.setCursorOnWait(this, false);
// }
}
// finds the next occurence of current search pattern
// the search is done in current text area
private void doFind() {
addSearchHistory();
find(parent.getScriptEditorTA());
}
// finds the next occurence of the search pattern in a
// a given text area. if match is not found, and if user
// don't ask to start over from beginning, then the method
// calls itself by specifying next opened text area.
private void find(RSyntaxTextArea textArea) {
try {
int offset = currentLine < textArea.getLineCount() ? textArea.getLineStartOffset(currentLine) : 0;
String searchTerm = tfSearchEditor.getText();
String text = textArea.getText();
int foundIndex = -1;
int flags = (checkboxRegexp.isSelected() ? 0 : Pattern.LITERAL)
| (checkboxMatchCase.isSelected() ? 0 : Pattern.CASE_INSENSITIVE);
Pattern p = Pattern.compile(searchTerm, flags);
Matcher matcher = p.matcher(text);
matcher.region(offset, text.length());
if (matcher.find()) {
foundIndex = matcher.start();
} else if (checkboxWrap.isSelected() && offset > 0) {
matcher.region(0, offset);
if (matcher.find()) {
foundIndex = matcher.start();
}
}
if (foundIndex != -1) {
int lineOfOffset = textArea.getLineOfOffset(foundIndex);
// textArea.setActiveLineRange(lineOfOffset, lineOfOffset);
textArea.setCurrentLine(lineOfOffset);
// textArea.setCaretPosition(foundIndex + searchTerm.length());
parent.repaint();
parent.fireStepChanged(lineOfOffset);
currentLine = lineOfOffset + 1;
} else {
JOptionPane.showMessageDialog(parent, "Search String not found.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* adds current search pattern in the search history list
*/
private void addSearchHistory() {
addSearchHistory(tfSearchEditor.getText());
}
/**
* adds a pattern in the search history list
*
* @param c
* the pattern to be added
*/
private void addSearchHistory(String c) {
if (StringUtils.isBlank(c)) {
return;
}
for (int i = 0; i < cbSearch.getItemCount(); i++) {
if (((String) cbSearch.getItemAt(i)).equals(c)) {
return;
}
}
cbSearch.insertItemAt(c, 0);
if (cbSearch.getItemCount() > 25) {
for (int i = 25; i < cbSearch.getItemCount();)
cbSearch.removeItemAt(i);
}
tfSearchEditor.setText((String) cbSearch.getItemAt(0));
}
/**
* adds current replace pattern in the replace history list
*/
private void addReplaceHistory() {
addReplaceHistory(tfReplaceEditor.getText());
}
/**
* adds a pattern in the replace history list
*
* @param c
* the pattern to be added
*/
private void addReplaceHistory(String c) {
if (StringUtils.isBlank(c)) {
return;
}
for (int i = 0; i < cbReplace.getItemCount(); i++) {
if (((String) cbReplace.getItemAt(i)).equals(c)) {
return;
}
}
cbReplace.insertItemAt(c, 0);
if (cbReplace.getItemCount() > 25) {
for (int i = 25; i < cbReplace.getItemCount();)
cbReplace.removeItemAt(i);
}
tfReplaceEditor.setText((String) cbReplace.getItemAt(0));
}
private void buildConstraints(GridBagConstraints agbc, int agx, int agy, int agw, int agh,
int awx, int awy) {
agbc.gridx = agx;
agbc.gridy = agy;
agbc.gridwidth = agw;
agbc.gridheight = agh;
agbc.weightx = awx;
agbc.weighty = awy;
agbc.insets = new Insets(2, 2, 2, 2);
}
class KeyHandler extends KeyAdapter {
public void keyPressed(KeyEvent evt) {
switch (evt.getKeyCode()) {
case KeyEvent.VK_ENTER:
if (evt.getSource() == tfSearchEditor) {
doFind();
} else if (evt.getSource() == tfReplaceEditor) {
doReplace();
}
break;
case KeyEvent.VK_ESCAPE:
setVisible(false);
break;
}
}
}
}