Add a Filter History #14
Chapter 2, Lists and Combos
|
63
HACK
H A C K
#14
Add a Filter History
Hack #14
Remember previous searches and research with one click.
Chances are good that if you’ve searched for something once, it’s important
enough that you might well search for it again. In Apple’s Safari browser, a
search widget at the upper right has a little magnifying glass that remembers
your last 10 searches. Click the magnifying glass and a pop up appears with
the previous searches. Select one and it populates the field and does the
search immediately.
Here’s an implementation of the same idea, grafted onto the previous hack.
In other words, this remembers previous filters. It doesn’t remember every
keystroke—why bother remembering the searches “J” and “Jo” when you’re
really just interested in “Joe”—and only adds a search term to the filter
when the user presses return.
In the previous hack, you just needed to have a text field and a
JList. Now a
JButton needs to be attached to the text field, so the two are bundled
together in the inner class
FilterField. This class is responsible for:
Telling the model to refilter on each keystroke in the
JTextField, as
before.
Remembering the
JTextField’s contents as a saved search anytime the
Return or Enter key is pressed.
Catching clicks on the
JButton and popping up a menu with previous
searches.
Populating the
JTextField with a previous search when one is selected
from the list. It doesn’t need to explicitly tell the model to refilter
because changing the text area will fire a
DocumentEvent that is already
accounted for by the
JTextField’s DocumentListener.
Example 2-5 shows the new
FilterField class.
Example 2-5. List filtering component with text field and history button
class FilterField extends JComponent
implements DocumentListener, ActionListener {
LinkedList prevSearches;
JTextField textField;
JButton prevSearchButton;
JPopupMenu prevSearchMenu;
public FilterField (int width) {
super( );
setLayout(new BorderLayout( ));
textField = new JTextField (width);
textField.getDocument( ).addDocumentListener (this);
textField.addActionListener (this);
64
|
Chapter 2, Lists and Combos
#14 Add a Filter History
HACK
Notice how this version of the class uses a MouseListener on the JButton
instead of an ActionListener. Either will work, but the MouseEvent provides
the location of the mouse click as a
Point in the JButton’s coordinate space,
which is useful for showing the pop-up menu at the exact point of the
mouse click. There is an
ActionListener implementation, but it’s for the
JTextField so that when the user presses the Return key, the filter text is
saved to the
JPopupMenu (and, if there are more than 10 items, the oldest
saved search is removed).
prevSearchButton =
new JButton (new ImageIcon ("mag-glass.png"));
prevSearchButton.setBorder(null);
prevSearchButton.addMouseListener (new MouseAdapter( ) {
public void mousePressed (MouseEvent me) {
popMenu (me.getX(), me.getY( ));
}
});
add (prevSearchButton, BorderLayout.WEST);
add (textField, BorderLayout.CENTER);
prevSearches = new LinkedList ( );
}
public void popMenu (int x, int y) {
prevSearchMenu = new JPopupMenu( );
Iterator it = prevSearches.iterator( );
while (it.hasNext( ))
prevSearchMenu.add (
new PrevSearchAction(it.next().toString( )));
prevSearchMenu.show (prevSearchButton, x, y);
}
public void actionPerformed (ActionEvent e) {
// called on return/enter, adds term to prevSearches
if (e.getSource( ) == textField) {
prevSearches.addFirst (textField.getText( ));
if (prevSearches.size( ) > 10)
prevSearches.removeLast( );
}
}
public void changedUpdate (DocumentEvent e) {
((FilterModel)getModel()).refilter( );
}
public void insertUpdate (DocumentEvent e) {
((FilterModel)getModel()).refilter( );
}
public void removeUpdate (DocumentEvent e) {
((FilterModel)getModel()).refilter( );
}
}
Example 2-5. List filtering component with text field and history button (continued)
Add a Filter History #14
Chapter 2, Lists and Combos
|
65
HACK
The items in the JPopupMenu are instances of PrevSearchAction, which sub-
classes Swing’s
Action. This is convenient because they provide a String rep-
resentation to be shown in the pop-up menu, yet get an
actionPerformed( )
when their menu item is selected, which gives them a chance to reset the fil-
ter text. Here’s what the
PrevSearchAction
inner class looks like:
class PrevSearchAction extends AbstractAction {
String term;
public PrevSearchAction (String s) {
term = s;
putValue (Action.NAME, term);
}
public String toString( ) { return term; }
public void actionPerformed (ActionEvent e) {
getFilterField( ).textField.setText (term);
}
}
When a previous search is recalled, it looks like Figure 2-4.
And when the mouse clicks on one of the menu items, the field is populated
and the list is filtered. Figure 2-5 shows the result.
Figure 2-4. Pop-up menu with previous searches
Figure 2-5. List filtered by pop-up selection

Get Swing Hacks now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.