All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.odell.glazedlists.swing.TextComponentMatcherEditor Maven / Gradle / Ivy

/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.swing;

import ca.odell.glazedlists.TextFilterator;
import ca.odell.glazedlists.matchers.TextMatcherEditor;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;

/**
 * A MatcherEditor that matches Objects that contain the filter text located
 * within a {@link Document}. This {@link TextMatcherEditor} is directly
 * coupled with a Document and fires MatcherEditor changes in response to
 * Document changes. This matcher is fully concrete and is expected to be used
 * by Swing applications.
 *
 * 

The {@link TextComponentMatcherEditor} constructors require that either a * {@link Document} or a {@link JTextComponent} (from which a {@link Document} * is extracted) be specified. * *

The MatcherEditor registers itself as a {@link DocumentListener} on the * given Document, or {@link ActionListener} on the {@link JTextComponent} for * non-live filtering. If a {@link JTextComponent} is given on construction, it * is also watched for changes of its Document and the Document used by this * MatcherEditor is updated to reflect the latest Document behind the text * component. * * If this MatcherEditor must be garbage collected before the underlying * Document, or JTextComponent, the listeners can be unregistered by calling * {@link #dispose()}. * * @author James Lemieux */ public class TextComponentMatcherEditor extends TextMatcherEditor { /** the Document that provides the filter values */ private Document document; /** the JTextComponent being observed for actions */ private final JTextComponent textComponent; /** whether we're listening to each keystroke */ private boolean live; /** The listener attached to the given {@link #document}. */ private final FilterHandler filterHandler = new FilterHandler(); /** * Creates a TextMatcherEditor bound to the {@link Document} backing the * given textComponent with the given * textFilterator. * * @param textComponent the text component backed by the {@link Document} * that is the source of text filter values * @param textFilterator an object capable of producing Strings from the * objects being filtered. If textFilterator is * null then all filtered objects are expected to * implement {@link ca.odell.glazedlists.TextFilterable}. */ public TextComponentMatcherEditor(JTextComponent textComponent, TextFilterator textFilterator) { this(textComponent, textFilterator, true); } /** * Creates a TextMatcherEditor bound to the {@link Document} backing the * given textComponent with the given * textFilterator. * * @param textComponent the text component backed by the {@link Document} * that is the source of text filter values * @param textFilterator an object capable of producing Strings from the * objects being filtered. If textFilterator is * null then all filtered objects are expected to * implement {@link ca.odell.glazedlists.TextFilterable}. * @param live true to filter by the keystroke or false * to filter only when {@link java.awt.event.KeyEvent#VK_ENTER Enter} is pressed * within the {@link JTextComponent}. Note that non-live filtering is only * supported if textComponent is a {@link JTextField}. * @throws IllegalArgumentException if the textComponent * is not a {@link JTextField} and non-live filtering is specified. */ public TextComponentMatcherEditor(JTextComponent textComponent, TextFilterator textFilterator, boolean live) { this(textComponent, textComponent.getDocument(), textFilterator, live); } /** * Creates a TextMatcherEditor bound to the given document * with the given textFilterator. * * @param document the {@link Document} that is the source of text filter * values * @param textFilterator an object capable of producing Strings from the * objects being filtered. If textFilterator is * null then all filtered objects are expected to * implement {@link ca.odell.glazedlists.TextFilterable}. */ public TextComponentMatcherEditor(Document document, TextFilterator textFilterator) { this(null, document, textFilterator, true); } /** * This private constructor implements the actual construction work and thus * ensures that all public constructors agree on the construction logic. */ private TextComponentMatcherEditor(JTextComponent textComponent, Document document, TextFilterator textFilterator, boolean live) { super(textFilterator); this.textComponent = textComponent; this.document = document; this.live = live; registerListeners(live); // if the document is non-empty to begin with! refilter(); } /** * Whether filtering occurs by the keystroke or not. */ public boolean isLive() { return live; } /** * Toggle between filtering by the keystroke and not. * * @param live true to filter by the keystroke or false * to filter only when {@link java.awt.event.KeyEvent#VK_ENTER Enter} is pressed * within the {@link JTextComponent}. Note that non-live filtering is only * supported if textComponent is a {@link JTextField}. */ public void setLive(boolean live) { if (live == this.live) return; deregisterListeners(this.live); this.live = live; registerListeners(this.live); } /** * Listen live or on action performed. */ private void registerListeners(boolean live) { if(live) { document.addDocumentListener(filterHandler); } else { if(textComponent == null) throw new IllegalArgumentException("Non-live filtering supported only for JTextField (document provided)"); if(!(textComponent instanceof JTextField)) throw new IllegalArgumentException("Non-live filtering supported only for JTextField (argument class " + textComponent.getClass().getName() + ")"); JTextField textField = (JTextField) textComponent; textField.addActionListener(filterHandler); } if (textComponent != null) textComponent.addPropertyChangeListener(filterHandler); } /** * Stop listening. */ private void deregisterListeners(boolean live) { if (live) { document.removeDocumentListener(filterHandler); } else { JTextField textField = (JTextField) textComponent; textField.removeActionListener(filterHandler); } if (textComponent != null) textComponent.removePropertyChangeListener(filterHandler); } /** * A cleanup method which stops this MatcherEditor from listening to * changes on the underlying {@link Document}, thus freeing the * MatcherEditor or Document to be garbage collected. */ public void dispose() { deregisterListeners(live); } /** * Update the filter text from the contents of the Document. */ private void refilter() { try { final int mode = getMode(); final String text = document.getText(0, document.getLength()); final String[] filters; // in CONTAINS mode we treat the string as whitespace delimited if (mode == CONTAINS) filters = text.split("[ \t]"); // in STARTS_WITH, REGULAR_EXPRESSION, or EXACT modes we use the string in its entirety else if (mode == STARTS_WITH || mode == REGULAR_EXPRESSION || mode == EXACT) filters = new String[] {text}; else throw new IllegalStateException("Unknown mode: " + mode); setFilterText(filters); } catch (BadLocationException ble) { // this shouldn't ever, ever happen throw new RuntimeException(ble); } } /** * This class responds to any change in the Document by setting the filter * text of this TextMatcherEditor to the contents of the Document. */ private class FilterHandler implements DocumentListener, ActionListener, PropertyChangeListener { public void insertUpdate(DocumentEvent e) { refilter(); } public void removeUpdate(DocumentEvent e) { refilter(); } public void changedUpdate(DocumentEvent e) { refilter(); } public void actionPerformed(ActionEvent e) { refilter(); } public void propertyChange(PropertyChangeEvent evt) { if ("document" == evt.getPropertyName()) { // stop listening to the old Document deregisterListeners(live); // start listening to the new Document document = textComponent.getDocument(); registerListeners(live); // refilter based on the new Document refilter(); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy