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

org.jdesktop.swingx.search.RecentSearches Maven / Gradle / Ivy

There is a newer version: 1.7.2
Show newest version
package org.jdesktop.swingx.search;

import org.jdesktop.swingx.JXSearchField;
import org.jdesktop.swingx.plaf.UIManagerExt;

import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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;

/**
 * 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}.
 *
 * @author Peter Weishapl 
 */
public class RecentSearches implements ActionListener {

    private static final Logger LOG = Logger.getLogger(RecentSearches.class.getName());

    private Preferences prefsNode;

    private int maxRecents = 5;

    private final List recentSearches = new ArrayList<>();

    private final 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 prefs    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 (RuntimeException ace) {
                // disable persistency, if we aren't allowed to access
                // preferences.
                LOG.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. * * @param searchString the search string to add * @see #getMaxRecents() */ 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[0]); } /** * 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. * * @return the maximum number of recent searches * @see #put(String) */ public int getMaxRecents() { return maxRecents; } /** * Set the maximum number of recent searches. * * @param maxRecents maximum number of recent searches * @see #put(String) */ 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[0]); } 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} 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} 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}. */ public static class RecentSearchesPopup extends JPopupMenu implements ActionListener, ChangeListener { private final RecentSearches recentSearches; private final 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 es 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(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy