mirror of
https://github.com/nasa/trick.git
synced 2025-04-12 21:53:03 +00:00
Customized FindBar class that used to extend JXFindBar now extends JPanel to avoid the compatibility issue between Swing and newer JDK. (#1827)
Customized FindBar class that used to extend JXFindBar now extends JPanel to avoid the compatibility issue between Swing and newer JDK.
This commit is contained in:
parent
b755b2e7b9
commit
db998604cd
@ -1,4 +1,3 @@
|
||||
|
||||
//========================================
|
||||
// Package
|
||||
//========================================
|
||||
@ -8,190 +7,282 @@ package trick.common.ui.panels;
|
||||
//Imports
|
||||
//========================================
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.FlowLayout;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.SwingConstants;
|
||||
|
||||
import org.jdesktop.swingx.AbstractPatternPanel;
|
||||
import org.jdesktop.swingx.JXFindBar;
|
||||
import org.jdesktop.swingx.JXFindPanel;
|
||||
import org.jdesktop.swingx.search.Searchable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import org.jdesktop.swingx.JXEditorPane;
|
||||
|
||||
/**
|
||||
* A {@link JXFindBar} that is for allowing users to input search text.
|
||||
*
|
||||
* @since Trick 10
|
||||
* A {@link JXFindBar} that allows users to input search text.
|
||||
* This class extends JXFindBar, a SwingX component providing search bar functionality for a JXEditorPane.
|
||||
*
|
||||
* Notes for not extending JXFindBar:
|
||||
* JXFindBar is a SwingX component extending JXFindPanel.
|
||||
* JXFindPanel offers various search options, allowing users to input search text.
|
||||
* When JXFindPanel performs a search, it calls the search method in the Searchable object.
|
||||
* The Searchable search method is invoked when findNext or findPrevious is clicked.
|
||||
* The JXEditorPane's Matcher calls toMatchResult() where the search text is used to find the match.
|
||||
* The toMatchResult method is called in the Matcher class and creates a new Matcher, copying the state over.
|
||||
* The toMatchResult method returns a {@link MatchResult} object.
|
||||
* This works until JDK 9.
|
||||
* JDK 9 introduces a new, non-public {@link java.util.regex.Matcher$ImmutableMatchResult} class that is the result of toMatchResult().
|
||||
* Therefore, a Matcher can't be cloned and the state can't be copied over. The following exception would be seen:
|
||||
* java.lang.ClassCastException: class java.util.regex.Matcher$ImmutableMatchResult cannot be cast to class java.util.regex.Matcher
|
||||
* (java.util.regex.Matcher$ImmutableMatchResult and java.util.regex.Matcher are in module java.base of loader 'bootstrap')
|
||||
*
|
||||
* @since Trick 19
|
||||
* This class no longer extends JXFindBar. It extends {@link JPanel} instead.
|
||||
* FindBar is a custom JPanel that provides search functionality for a JXEditorPane.
|
||||
* It allows users to search for text within the editor pane and navigate through the matches.
|
||||
*
|
||||
* <p>Usage example:</p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* JXEditorPane editorPane = new JXEditorPane();
|
||||
* FindBar findBar = new FindBar(editorPane);
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class FindBar extends JXFindBar implements ActionListener{
|
||||
|
||||
//========================================
|
||||
// Public data
|
||||
//========================================
|
||||
|
||||
|
||||
//========================================
|
||||
// Protected data
|
||||
//========================================
|
||||
|
||||
public class FindBar extends JPanel {
|
||||
|
||||
//========================================
|
||||
// Private Data
|
||||
//=======================================
|
||||
|
||||
private static final long serialVersionUID = 9092192049485321408L;
|
||||
|
||||
/** The initial search text for this find bar */
|
||||
private String initialSearchText = null;
|
||||
|
||||
/** By default, this would not have search options as JXFindPanel, true otherwise. */
|
||||
private boolean hasOptions = false;
|
||||
|
||||
// TODO: add pattern support
|
||||
//private JCheckBox anchorCheck;
|
||||
|
||||
//========================================
|
||||
// Constructors
|
||||
//========================================
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public FindBar() {
|
||||
super();
|
||||
setNotFoundForegroundColor();
|
||||
}
|
||||
// ========================================
|
||||
// Public data
|
||||
// ========================================
|
||||
|
||||
/**
|
||||
* The constructor that specifies total number of popup menus for the panel.
|
||||
*
|
||||
* @param searchable An instance of {@link Searchable} from a gui component which
|
||||
* this search bar is for.
|
||||
*/
|
||||
public FindBar(Searchable searchable) {
|
||||
super(searchable);
|
||||
setNotFoundForegroundColor();
|
||||
}
|
||||
|
||||
//========================================
|
||||
// Set/Get methods
|
||||
//========================================
|
||||
/**
|
||||
* Gets to see if case sensitive is checked.
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isCaseSensitive() {
|
||||
return getPatternModel().isCaseSensitive();
|
||||
}
|
||||
/**
|
||||
* Sets whether to have the options as {@link JXFindPanel}.
|
||||
* @param b true or false
|
||||
*/
|
||||
public void setOptions(boolean b) {
|
||||
hasOptions = b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the searchField that is defined in parent class {@link AbstractPatternPanel}
|
||||
* if there is initial search text is defined.
|
||||
* @param searchText searchText
|
||||
*/
|
||||
public void updateSearchField(String searchText) {
|
||||
initialSearchText = searchText;
|
||||
if (searchField != null) {
|
||||
searchField.setText(searchText);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current search text shown in searchField.
|
||||
* @return the text
|
||||
*/
|
||||
public String getSearchText() {
|
||||
if (searchField != null) {
|
||||
return searchField.getText();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for changing the forground color when the text is not found.
|
||||
* Since notFoundForegroundColor is proteced in {@link JXFindBar}, extending
|
||||
* it is the only way to be able to change it.
|
||||
*
|
||||
*/
|
||||
private void setNotFoundForegroundColor() {
|
||||
notFoundForegroundColor = Color.red;
|
||||
}
|
||||
|
||||
|
||||
//========================================
|
||||
// Methods
|
||||
//========================================
|
||||
@Override
|
||||
protected void build() {
|
||||
if (hasOptions) {
|
||||
buildBarWithOption();
|
||||
} else {
|
||||
buildBar();
|
||||
}
|
||||
if (initialSearchText != null) {
|
||||
updateSearchField(initialSearchText);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildBar() {
|
||||
setLayout(new FlowLayout(SwingConstants.LEADING));
|
||||
add(searchLabel);
|
||||
add(new JLabel(":"));
|
||||
add(new JLabel(" "));
|
||||
// ========================================
|
||||
// Protected data
|
||||
// ========================================
|
||||
|
||||
// ========================================
|
||||
// Private Data
|
||||
// =======================================
|
||||
|
||||
/**
|
||||
* A text field for entering search queries.
|
||||
*/
|
||||
private JTextField searchField;
|
||||
/**
|
||||
* A button that, when pressed, triggers the action to find the next occurrence
|
||||
* of the search term in the text or document.
|
||||
*/
|
||||
private JButton findNextButton;
|
||||
/**
|
||||
* JButton used to trigger the action of finding the previous occurrence
|
||||
* in a search operation within the UI.
|
||||
*/
|
||||
private JButton findPreviousButton;
|
||||
/**
|
||||
* The JXEditorPane component used for displaying and editing text content.
|
||||
*/
|
||||
private JXEditorPane editorPane;
|
||||
/**
|
||||
* A list of integer positions.
|
||||
* This list stores the positions of all find matches.
|
||||
*/
|
||||
private List<Integer> matchPosList;
|
||||
/**
|
||||
* A list of integers representing the lengths of each find match.
|
||||
*/
|
||||
private List<Integer> matchLenList;
|
||||
/**
|
||||
* The current index used for tracking the position within a list or array.
|
||||
* This variable is typically used to keep track of the current item being
|
||||
* processed or displayed.
|
||||
*/
|
||||
private int currentIndex;
|
||||
|
||||
// ========================================
|
||||
// Constructors
|
||||
// ========================================
|
||||
/**
|
||||
* Constructs a new FindBar with the specified JXEditorPane.
|
||||
*
|
||||
* <p>
|
||||
* The FindBar consists of a JTextField for entering the search text, and two buttons
|
||||
* for navigating to the next and previous matches. It also highlights the current match
|
||||
* in the editor pane.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Features:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Real-time search and highlight as the user types in the search field.</li>
|
||||
* <li>Navigation buttons to find the next and previous matches.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param editorPane The JXEditorPane in which the search will be performed.
|
||||
*/
|
||||
public FindBar(JXEditorPane editorPane) {
|
||||
this.editorPane = editorPane;
|
||||
setLayout(new FlowLayout(FlowLayout.LEFT));
|
||||
|
||||
searchField = new JTextField(20);
|
||||
findNextButton = new JButton("Find Next");
|
||||
findPreviousButton = new JButton("Find Previous");
|
||||
|
||||
// Add a DocumentListener to the search field to trigger search and highlight on
|
||||
// text change as real-time search.
|
||||
// The search field background color is set to red if no matches are found
|
||||
// otherwise it is set to white.
|
||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
searchAndHighlight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
searchAndHighlight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
searchAndHighlight();
|
||||
}
|
||||
|
||||
private void searchAndHighlight() {
|
||||
String searchText = searchField.getText();
|
||||
findMatches(searchText);
|
||||
highlightCurrentMatch();
|
||||
if (matchPosList == null || matchPosList.isEmpty()) {
|
||||
searchField.setBackground(Color.RED);
|
||||
} else {
|
||||
searchField.setBackground(Color.WHITE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add an ActionListener to the search field to trigger search and highlight on Enter key press.
|
||||
searchField.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String searchText = searchField.getText();
|
||||
findMatches(searchText);
|
||||
highlightCurrentMatch();
|
||||
}
|
||||
});
|
||||
|
||||
// Add ActionListeners to the find next button.
|
||||
findNextButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (matchPosList != null && !matchPosList.isEmpty()) {
|
||||
currentIndex = (currentIndex + 1) % matchPosList.size();
|
||||
highlightCurrentMatch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add ActionListeners to the find previous button.
|
||||
findPreviousButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (matchPosList != null && !matchPosList.isEmpty()) {
|
||||
currentIndex = (currentIndex - 1 + matchPosList.size()) % matchPosList.size();
|
||||
highlightCurrentMatch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add(new JLabel("Find:"));
|
||||
add(searchField);
|
||||
add(findNext);
|
||||
add(findPrevious);
|
||||
}
|
||||
|
||||
private void buildBarWithOption() {
|
||||
//anchorCheck = new JCheckBox("Anchor");
|
||||
//anchorCheck.addActionListener(this);
|
||||
|
||||
wrapCheck = new JCheckBox();
|
||||
backCheck = new JCheckBox();
|
||||
Box lBox = new Box(BoxLayout.LINE_AXIS);
|
||||
//lBox.add(anchorCheck);
|
||||
lBox.add(matchCheck);
|
||||
lBox.add(wrapCheck);
|
||||
lBox.add(backCheck);
|
||||
lBox.setAlignmentY(Component.TOP_ALIGNMENT);
|
||||
|
||||
Box mBox = new Box(BoxLayout.LINE_AXIS);
|
||||
mBox.add(searchLabel);
|
||||
mBox.add(new JLabel(": "));
|
||||
mBox.add(searchField);
|
||||
mBox.add(findNext);
|
||||
mBox.add(findPrevious);
|
||||
mBox.setAlignmentY(Component.TOP_ALIGNMENT);
|
||||
add(findNextButton);
|
||||
add(findPreviousButton);
|
||||
}
|
||||
|
||||
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
|
||||
|
||||
add(lBox);
|
||||
add(mBox);
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
/*if (e.getSource() == anchorCheck) {
|
||||
if (anchorCheck.isSelected()) {
|
||||
getPatternModel().setRegexCreatorKey(PatternModel.REGEX_ANCHORED);
|
||||
} else {
|
||||
getPatternModel().setMatchRule(PatternModel.REGEX_MATCH_RULES);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
// ========================================
|
||||
// Set/Get methods
|
||||
// ========================================
|
||||
|
||||
// ========================================
|
||||
// Methods
|
||||
// ========================================
|
||||
/**
|
||||
* Finds and highlights all matches of the given search text in the editor pane.
|
||||
*
|
||||
* @param searchText The text to search for within the editor pane.
|
||||
* @throws PatternSyntaxException If the regular expression's syntax is invalid.
|
||||
* @throws BadLocationException If the document's text cannot be retrieved.
|
||||
*/
|
||||
private void findMatches(String searchText) {
|
||||
try {
|
||||
Highlighter highlighter = editorPane.getHighlighter();
|
||||
Highlighter.HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW);
|
||||
highlighter.removeAllHighlights();
|
||||
|
||||
Document doc = editorPane.getDocument();
|
||||
String text = doc.getText(0, doc.getLength());
|
||||
matchPosList = new ArrayList<>();
|
||||
matchLenList = new ArrayList<>();
|
||||
Pattern pattern = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
|
||||
// Find all matches of the search text in the document.
|
||||
// Add the start to matchPosList and the length to matchLenList.
|
||||
while (matcher.find()) {
|
||||
matchPosList.add(matcher.start());
|
||||
matchLenList.add(matcher.end() - matcher.start());
|
||||
}
|
||||
|
||||
// Reset the current index to 0 and highlight the first match if available.
|
||||
currentIndex = 0;
|
||||
if (!matchPosList.isEmpty()) {
|
||||
// Highlight the current match.
|
||||
int pos = matchPosList.get(currentIndex);
|
||||
highlighter.addHighlight(pos, pos + matchLenList.get(currentIndex), painter);
|
||||
// Set the caret position to the start of the current match.
|
||||
editorPane.setCaretPosition(pos);
|
||||
}
|
||||
} catch (PatternSyntaxException e) {
|
||||
JOptionPane.showMessageDialog(this, "Invalid regular expression: " + e.getDescription(), "Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights the current match in the editor pane.
|
||||
*
|
||||
* This method uses the Highlighter to highlight the text in the editor pane
|
||||
* that matches the current search term. It removes any existing highlights
|
||||
* before applying the new highlight. If there are positions available in the
|
||||
* list, it highlights the text at the current index position and sets the
|
||||
* caret position to the start of the highlighted text.
|
||||
*
|
||||
* @throws BadLocationException if the position is invalid in the document model
|
||||
*/
|
||||
private void highlightCurrentMatch() {
|
||||
try {
|
||||
Highlighter highlighter = editorPane.getHighlighter();
|
||||
Highlighter.HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW);
|
||||
highlighter.removeAllHighlights();
|
||||
|
||||
if (matchPosList != null && !matchPosList.isEmpty()) {
|
||||
int pos = matchPosList.get(currentIndex);
|
||||
// Highlight the current match.
|
||||
highlighter.addHighlight(pos, pos + searchField.getText().length(), painter);
|
||||
// Set the caret position to the start of the current match.
|
||||
editorPane.setCaretPosition(pos);
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -444,7 +444,7 @@ public class SieApplication extends TrickApplication implements TreeSelectionLis
|
||||
contentPane.setEditable(false);
|
||||
contentPane.setContentType("text/html");
|
||||
|
||||
FindBar findBar = new FindBar(contentPane.getSearchable());
|
||||
FindBar findBar = new FindBar(contentPane);
|
||||
contentPanel.add(findBar, BorderLayout.SOUTH);
|
||||
|
||||
// Add all the desired components to the comp
|
||||
|
@ -1207,7 +1207,9 @@ public class SimControlApplication extends TrickApplication implements PropertyC
|
||||
Font font = new Font("Monospaced", Font.PLAIN, curr_font_size);
|
||||
statusMsgPane.setFont(font);
|
||||
|
||||
JPanel statusMsgPanel = UIUtils.createSearchableTitledPanel("Status Messages", statusMsgPane, new FindBar(statusMsgPane.getSearchable()));
|
||||
// Create a customized search bar for the status message pane.
|
||||
FindBar findBar = new FindBar(statusMsgPane);
|
||||
JPanel statusMsgPanel = UIUtils.createSearchableTitledPanel("Status Messages", statusMsgPane, findBar);
|
||||
return statusMsgPanel;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user