org.jdesktop.swingx.search.RecentSearches Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swingx-all Show documentation
Show all versions of swingx-all Show documentation
A Maven project to aggregate all modules into a single artifact.
The newest version!
package org.jdesktop.swingx.search;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jdesktop.swingx.JXSearchField;
import org.jdesktop.swingx.plaf.UIManagerExt;
/**
* Maintains a list of recent searches and persists this list automatically
* using {@link Preferences}. A recent searches popup menu can be installed on
* a {@link JXSearchField} using {@link #install(JXSearchField)}.
*
* @author Peter Weishapl
*
*/
public class RecentSearches implements ActionListener {
private Preferences prefsNode;
private int maxRecents = 5;
private List recentSearches = new ArrayList();
private List listeners = new ArrayList();
/**
* Creates a list of recent searches and uses saveName
to
* persist this list under the {@link Preferences} user root node. Existing
* entries will be loaded automatically.
*
* @param saveName
* a unique name for saving this list of recent searches
*/
public RecentSearches(String saveName) {
this(null, saveName);
}
/**
* Creates a list of recent searches and uses saveName
to
* persist this list under the prefs
node. Existing entries
* will be loaded automatically.
*
* @param prefsNode
* the preferences node under which this list will be persisted.
* If prefsNode is null
the preferences node will
* be set to the user root node
* @param saveName
* a unique name for saving this list of recent searches. If
* saveName is null
, the list will not be
* persisted
*/
public RecentSearches(Preferences prefs, String saveName) {
if (prefs == null) {
try {
prefs = Preferences.userRoot();
} catch (AccessControlException ace) {
// disable persistency, if we aren't allowed to access
// preferences.
Logger.getLogger(getClass().getName()).warning("cannot acces preferences. persistency disabled.");
}
}
if (prefs != null && saveName != null) {
this.prefsNode = prefs.node(saveName);
load();
}
}
private void load() {
// load persisted entries
try {
String[] recent = new String[prefsNode.keys().length];
for (String key : prefsNode.keys()) {
recent[prefsNode.getInt(key, -1)] = key;
}
recentSearches.addAll(Arrays.asList(recent));
} catch (Exception ex) {
// ignore
}
}
private void save() {
if (prefsNode == null) {
return;
}
try {
prefsNode.clear();
} catch (BackingStoreException e) {
// ignore
}
int i = 0;
for (String search : recentSearches) {
prefsNode.putInt(search, i++);
}
}
/**
* Add a search string as the first element. If the search string is
* null
or empty nothing will be added. If the search string
* already exists, the old element will be removed. The modified list will
* automatically be persisted.
*
* If the number of elements exceeds the maximum number of entries, the last
* entry will be removed.
*
* @see #getMaxRecents()
* @param searchString
* the search string to add
*/
public void put(String searchString) {
if (searchString == null || searchString.trim().length() == 0) {
return;
}
int lastIndex = recentSearches.indexOf(searchString);
if (lastIndex != -1) {
recentSearches.remove(lastIndex);
}
recentSearches.add(0, searchString);
if (getLength() > getMaxRecents()) {
recentSearches.remove(recentSearches.size() - 1);
}
save();
fireChangeEvent();
}
/**
* Returns all recent searches in this list.
*
* @return the recent searches
*/
public String[] getRecentSearches() {
return recentSearches.toArray(new String[] {});
}
/**
* The number of recent searches.
*
* @return number of recent searches
*/
public int getLength() {
return recentSearches.size();
}
/**
* Remove all recent searches.
*/
public void removeAll() {
recentSearches.clear();
save();
fireChangeEvent();
}
/**
* Returns the maximum number of recent searches.
*
* @see #put(String)
* @return the maximum number of recent searches
*/
public int getMaxRecents() {
return maxRecents;
}
/**
* Set the maximum number of recent searches.
*
* @see #put(String)
* @param maxRecents
* maximum number of recent searches
*/
public void setMaxRecents(int maxRecents) {
this.maxRecents = maxRecents;
}
/**
* Add a change listener. A {@link ChangeEvent} will be fired whenever a
* search is added or removed.
*
* @param l
* the {@link ChangeListener}
*/
public void addChangeListener(ChangeListener l) {
listeners.add(l);
}
/**
* Remove a change listener.
*
* @param l
* a registered {@link ChangeListener}
*/
public void removeChangeListener(ChangeListener l) {
listeners.remove(l);
}
/**
* Returns all registered {@link ChangeListener}s.
*
* @return all registered {@link ChangeListener}s
*/
public ChangeListener[] getChangeListeners() {
return listeners.toArray(new ChangeListener[] {});
}
private void fireChangeEvent() {
ChangeEvent e = new ChangeEvent(this);
for (ChangeListener l : listeners) {
l.stateChanged(e);
}
}
/**
* Creates the recent searches popup menu which will be used by
* {@link #install(JXSearchField)} to set a search popup menu on
* searchField
.
*
* Override to return a custom popup menu.
*
* @param searchField
* the search field the returned popup menu will be installed on
* @return the recent searches popup menu
*/
protected JPopupMenu createPopupMenu(JTextField searchField) {
return new RecentSearchesPopup(this, searchField);
}
/**
* Install a recent the searches popup menu returned by
* {@link #createPopupMenu(JXSearchField)} on searchField
.
* Also registers an {@link ActionListener} on searchField
* and adds the search string to the list of recent searches whenever a
* {@link ActionEvent} is received.
*
* Uses {@link NativeSearchFieldSupport} to achieve compatibility with the native
* search field support provided by the Mac Look And Feel since Mac OS 10.5.
*
* @param searchField
* the search field to install a recent searches popup menu on
*/
public void install(JTextField searchField) {
searchField.addActionListener(this);
NativeSearchFieldSupport.setFindPopupMenu(searchField, createPopupMenu(searchField));
}
/**
* Remove the recent searches popup from searchField
when
* installed and stop listening for {@link ActionEvent}s fired by the
* search field.
*
* @param searchField
* uninstall recent searches popup menu
*/
public void uninstall(JXSearchField searchField) {
searchField.removeActionListener(this);
if (searchField.getFindPopupMenu() instanceof RecentSearchesPopup) {
removeChangeListener((ChangeListener) searchField.getFindPopupMenu());
searchField.setFindPopupMenu(null);
}
}
/**
* Calls {@link #put(String)} with the {@link ActionEvent}s action command
* as the search string.
*/
@Override
public void actionPerformed(ActionEvent e) {
put(e.getActionCommand());
}
/**
* The popup menu returned by
* {@link RecentSearches#createPopupMenu(JXSearchField)}.
*/
public static class RecentSearchesPopup extends JPopupMenu implements ActionListener, ChangeListener {
private RecentSearches recentSearches;
private JTextField searchField;
private JMenuItem clear;
/**
* Creates a new popup menu based on the given {@link RecentSearches}
* and {@link JXSearchField}.
*
* @param recentSearches
* @param searchField
*/
public RecentSearchesPopup(RecentSearches recentSearches, JTextField searchField) {
this.searchField = searchField;
this.recentSearches = recentSearches;
recentSearches.addChangeListener(this);
buildMenu();
}
/**
* Rebuilds the menu according to the recent searches.
*/
private void buildMenu() {
setVisible(false);
removeAll();
if (recentSearches.getLength() == 0) {
JMenuItem noRecent = new JMenuItem(UIManagerExt.getString("SearchField.noRecentsText"));
noRecent.setEnabled(false);
add(noRecent);
} else {
JMenuItem recent = new JMenuItem(UIManagerExt.getString("SearchField.recentsMenuTitle"));
recent.setEnabled(false);
add(recent);
for (String searchString : recentSearches.getRecentSearches()) {
JMenuItem mi = new JMenuItem(searchString);
mi.addActionListener(this);
add(mi);
}
addSeparator();
clear = new JMenuItem(UIManagerExt.getString("SearchField.clearRecentsText"));
clear.addActionListener(this);
add(clear);
}
}
/**
* Sets {@link #searchField}s text to the {@link ActionEvent}s action
* command and call {@link JXSearchField#postActionEvent()} to fire an
* {@link ActionEvent}, if e
s source is not the clear
* menu item. If the source is the clear menu item, all recent searches
* will be removed.
*/
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == clear) {
recentSearches.removeAll();
} else {
searchField.setText(e.getActionCommand());
searchField.postActionEvent();
}
}
/**
* Every time the recent searches fires a {@link ChangeEvent} call
* {@link #buildMenu()} to rebuild the whole menu.
*/
@Override
public void stateChanged(ChangeEvent e) {
buildMenu();
}
}
}