how is possible to synchonize text typed into JTextField
(then by DocumentListener
passed String
to the RowFilter
) with TableCell
if contains same as String value in JTextField/Document,
and then highlights (meaning f.e. change text Color.Red
) for identical text in synchronized TableCell(s)
I know that by usage of some hacks is that possible by using
1/ by using getTableCellRendererComponent
2/ by using prepareRenderer
is there another and maybe correct way(s)
little bit modified code from JTable tutorial
import java.awt.*;
import java.util.regex.PatternSyntaxException;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TableFilterSorter extends JPanel {
private boolean DEBUG = false;
private static final long serialVersionUID = 1L;
public TableFilterSorter() {
super(new BorderLayout(5, 5));
final JTextField filterCpText = new JTextField();
filterCpText.setFont(new Font("Serif", Font.BOLD, 28));
filterCpText.setForeground(Color.BLUE);
filterCpText.setBackground(Color.LIGHT_GRAY);
JPanel filterCpPanel = new JPanel();
filterCpPanel.setLayout(new BorderLayout(5, 5));
filterCpPanel.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
filterCpPanel.setBackground(Color.LIGHT_GRAY);
filterCpPanel.setPreferredSize(new Dimension(300, 30));
filterCpPanel.add(filterCpText, BorderLayout.CENTER);
add(filterCpPanel, BorderLayout.NORTH);
final JTable table = new JTable(new MyTableModel());
table.setPreferredScrollableViewportSize(new Dimension(500, 160));
table.setFillsViewportHeight(true);
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane, BorderLayout.CENTER);
TableModel myTableModel = table.getModel();
final TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(myTableModel);
table.setRowSorter(sorter);
filterCpText.getDocument().addDocumentListener(new DocumentListener() {
private void searchFieldChangedUpdate(DocumentEvent evt) {
String text = filterCpText.getText();
if (text.length() == 0) {
sorter.setRowFilter(null);
table.clearSelection();
} else {
try {
sorter.setRowFilter(RowFilter.regexFilter("(?i)" + text, 4));
table.clearSelection();
} catch (PatternSyntaxException pse) {
JOptionPane.showMessageDialog(null, "Bad regex pattern",
"Bad regex pattern", JOptionPane.ERROR_MESSAGE);
}
}
}
@Override
public void insertUpdate(DocumentEvent evt) {
searchFieldChangedUpdate(evt);
}
@Override
public void removeUpdate(DocumentEvent evt) {
searchFieldChangedUpdate(evt);
}
@Override
public void changedUpdate(DocumentEvent evt) {
searchFieldChangedUpdate(evt);
}
});
}
private class MyTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private String[] columnNames = {"First Name", "Last Name", "Sport", "# of Years", "Vegetarian"};
private Object[][] data = {
{"Mary", "Campione", "Snowboarding", new Integer(5), false},
{"Alison", "Huml", "Rowing", new Integer(3), true},
{"Kathy", "Walrath", "Knitting", new Integer(2), false},
{"Sharon", "Zakhour", "Speed reading", new Integer(20), true},
{"Philip", "Milne", "Pool", new Integer(10), false},
{"Mary", "Campione", "Snowboarding", new Integer(5), false},
{"Alison", "Huml", "Rowing", new Integer(3), true},
{"Kathy", "Walrath", "Knitting", new Integer(2), false},
{"Sharon", "Zakhour", "Speed reading", new Integer(20), true},
{"Philip", "Milne", "Pool", new Integer(10), false},};
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public int getRowCount() {
return data.length;
}
@Override
public String getColumnName(int col) {
return columnNames[col];
}
@Override
public Object getValueAt(int row, int col) {
return data[row][col];
}
@Override
public Class<?> getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
@Override
public boolean isCellEditable(int row, int col) {
if (col < 2) {
return false;
} else {
return true;
}
}
@Override
public void setValueAt(Object value, int row, int col) {
if (DEBUG) {
System.out.println("Setting value at " + row + "," + col
+ " to " + value + " (an instance of " + value.getClass() + ")");
}
data[row][col] = value;
fireTableCellUpdated(row, col);
if (DEBUG) {
System.out.println("New value of data:");
printDebugData();
}
}
private void printDebugData() {
int numRows = getRowCount();
int numCols = getColumnCount();
for (int i = 0; i < numRows; i++) {
System.out.print(" row " + i + ":");
for (int j = 0; j < numCols; j++) {
System.out.print(" " + data[i][j]);
}
System.out.println();
}
System.out.println("--------------------------");
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("TableDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TableFilterSorter newContentPane = new TableFilterSorter();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.setLocation(150, 150);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
}
JXTable can do so via a Highlighter - see swinglabs-demos for an example (MatchingTextHighlighter in the search demo) - there a background highlight is applied by a Painter.
You can do something like that manually somewhere in your renderer. If using a JLabel as renderingComponent, you basically have to find parts of the text that need the background highlight and paint the background for that region (below is a code snippet for finding the areas, c&p from the demos example). Alternatively you might consider using a JTextField as rendering component: the adv is that Highlighter (from javax.swing.text) is built-in, the disadv are the usual issues with text comps as renderers ;-)
/**
* Finds the rectangles that contain rendered characters that match the
* pattern.
*
* @param object an optional configuration parameter. This may be null.
* @param width width of the area to paint.
* @param height height of the area to paint.
* @return a <code>List</code> of <code>Rectangle</code>s marking characters
* to highlight
*/
protected List<Rectangle> findHighlightAreas(JLabel object, int width,
int height) {
String text = object.getText();
insets = object.getInsets(insets);
viewR.x = 0 + insets.left;
viewR.y = 0 + insets.bottom;
viewR.width = width - insets.right;
viewR.height = height - insets.top;
// Reset the text and view rectangle x any y coordinates.
// These are not set to 0 in SwingUtilities.layoutCompoundLabel
iconR.x = iconR.y = 0;
textR.x = textR.y = 0;
FontMetrics fm = object.getFontMetrics(object.getFont());
String clippedText = SwingUtilities.layoutCompoundLabel(object, fm,
text, object.getIcon(), object.getVerticalAlignment(), object
.getHorizontalAlignment(), object
.getVerticalTextPosition(), object
.getHorizontalTextPosition(), viewR, iconR, textR,
object.getIconTextGap());
int xOffset = calculateXOffset(object, viewR, textR);
String textToSearch = clippedText;
// Check to see if the text will be clipped
if (!object.getText().equals(clippedText)) {
// TODO There has to be a better way that assuming ellipses
// are the last characters of the text
textToSearch = clippedText.substring(0, clippedText.length() - 3);
}
return createHighlightAreas(textToSearch, fm, xOffset, height);
}
/**
* Creates the rectangles that contain matched characters in the given text.
*
* @param text the text to search
* @param fm the font metrics of the rendered font
* @param xOffset the x offset at which text rendering starts
* @param height the height of painted highlights
* @return a <code>List</code> of highlight areas to paint
*/
protected List<Rectangle> createHighlightAreas(String text, FontMetrics fm,
int xOffset, int height) {
SearchPredicate predicate = (SearchPredicate) getHighlightPredicate();
Matcher matcher = predicate.getPattern().matcher(text);
List<Rectangle> highlightAreas = null;
int startFrom = 0;
while (startFrom < text.length() && matcher.find(startFrom)) {
if (highlightAreas == null) {
highlightAreas = new ArrayList<Rectangle>();
}
int start = matcher.start();
int end = matcher.end();
if (start == end) {
// empty matcher will cause infinite loop
break;
}
startFrom = end;
int highlightx;
int highlightWidth;
if (start == 0) {
// start highlight from the start of the field
highlightx = textR.x + xOffset;
} else {
// Calculate the width of the unhighlighted text to
// get the start of the highlighted region.
String strToStart = text.substring(0, start);
highlightx = textR.x + fm.stringWidth(strToStart) + xOffset;
}
// Get the width of the highlighted region
String highlightText = text.substring(start, end);
highlightWidth = fm.stringWidth(highlightText);
highlightAreas.add(new Rectangle(highlightx, 0, highlightWidth,
height));
}
if (highlightAreas == null) {
highlightAreas = Collections.emptyList();
} else {
coalesceHighlightAreas(highlightAreas);
}
return highlightAreas;
}
/**
* Joins highlight rectangles that mark adjacent horizontal areas into
* single rectangles. This is useful to renderers that vary horizontally,
* such a horizontal gradient - the gradient will not restart when there are
* two adjacent highlight areas.
*
* @param highlightAreas a <code>List</code> of <code>Rectangle</code>s.
*/
protected void coalesceHighlightAreas(List<Rectangle> highlightAreas) {
Collections.sort(highlightAreas, X_AXIS_RECTANGLE_COMPARATOR);
int i = 0;
while (i < highlightAreas.size() - 1) {
Rectangle r1 = highlightAreas.get(i);
Rectangle r2 = highlightAreas.get(i + 1);
if (r1.x + r1.width == r2.x) {
r1.width += r2.width;
highlightAreas.remove(i + 1);
} else {
i++;
}
}
}
/**
* Calculates the x offset of highlights based on component orientation and
* text direction.
*
* @param component the renderer component
* @param viewR the view rectangle of the renderer component
* @param textR the text rectangle of the renderer component
* @return the number of pixels to offset the highlight from the left edge
* of the component
*/
protected int calculateXOffset(JLabel component, Rectangle viewR,
Rectangle textR) {
int horizAlignment = component.getHorizontalAlignment();
boolean leftToRight = component.getComponentOrientation()
.isLeftToRight();
if (horizAlignment == SwingConstants.LEFT
|| (horizAlignment == SwingConstants.LEADING && leftToRight)
|| (horizAlignment == SwingConstants.TRAILING && !leftToRight)) {
return 0;
} else if (horizAlignment == SwingConstants.RIGHT
|| (horizAlignment == SwingConstants.TRAILING && !leftToRight)
|| (horizAlignment == SwingConstants.LEADING && leftToRight)) {
return viewR.width - textR.width;
} else if (horizAlignment == SwingConstants.CENTER) {
return (viewR.width - textR.width) / 2;
}
throw new AssertionError("Unknown horizonal alignment "
+ horizAlignment);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With