/*
* Copyright 1998-2012 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation. Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ucar.util.prefs.ui;
import ucar.nc2.ui.widget.IndependentDialog;
import ucar.util.prefs.PersistenceManager;
import ucar.util.prefs.PreferencesExt;
import java.util.*;
import java.util.List;
import java.io.IOException;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
/**
* A simple extension to JComboBox, which persists the n latest values.
* The JComboBox is editable; user can add a new String, then if acceptable,
* the calling routine should transform it to correct object type and call addItem().
* When item is added, it is placed on the top of the list, so it is more likely to be saved.
* <p> The items in the list can be any Object type with these caveats:
* <ul>
* <li> item.toString() used as display name
* <li> item.equals() used for object equality
* <li> prefs.putBeanObject() used for storage, so XMLEncoder used, so object must have no-arg Constructor.
* </ul>
*
* When listening for change events, generally key on type comboBoxChanged, and you
* must explicitly decide to save it in the list:
*
* <pre>
* cb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("comboBoxChanged")) {
Object select = cb.getSelectedItem());
if (isOK( select))
cb.addItem( select);
}
}
});
</pre>
* @author John Caron
*/
public class ComboBox extends JComboBox {
private static final String LIST = "ComboBoxList";
private boolean deleting = false;
private PersistenceManager prefs;
private int nkeep = 20;
public ComboBox() {
this( null, 20);
}
/**
* Constructor.
* @param prefs get/put list here; may be null.
*/
public ComboBox(PersistenceManager prefs) {
this( prefs, 20);
}
/**
* Constructor.
* @param prefs get/put list here; may be null.
* @param nkeep keep this many when you save.
*/
public ComboBox(PersistenceManager prefs, int nkeep) {
super();
this.prefs = prefs;
this.nkeep = nkeep;
setEditable(true);
setPreferences(prefs);
addContextMenu();
}
public void setPreferences(PersistenceManager prefs) {
this.prefs = prefs;
if (prefs != null) {
ArrayList list = (ArrayList) prefs.getList(LIST, null);
setItemList(list);
}
}
public JComponent getDeepEditComponent() {
return (JComponent) getEditor().getEditorComponent();
}
private JPopupMenu popupMenu;
public void addContextMenu() {
Component editComp = getEditor().getEditorComponent();
popupMenu = new JPopupMenu();
editComp.addMouseListener( new PopupTriggerListener() {
public void showPopup(java.awt.event.MouseEvent e) {
popupMenu.show(ComboBox.this, e.getX(), e.getY());
}
});
AbstractAction deleteAction = new AbstractAction() {
public void actionPerformed( java.awt.event.ActionEvent e) {
final JList delComp= new JList();
delComp.setModel( getModel());
delComp.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
int index = delComp.getSelectedIndex();
deleting = true;
if (index >= 0)
removeItemAt( index);
deleting = false;
}
});
IndependentDialog iw = new IndependentDialog(null, true, "delete items", delComp);
iw.show();
}
};
deleteAction.putValue( Action.NAME, "Delete");
popupMenu.add( deleteAction);
AbstractAction deleteAllAction = new AbstractAction() {
public void actionPerformed( java.awt.event.ActionEvent e) {
setItemList( new ArrayList());
}
};
deleteAllAction.putValue( Action.NAME, "Delete All");
popupMenu.add( deleteAllAction);
}
private static abstract class PopupTriggerListener extends MouseAdapter {
public void mouseReleased (MouseEvent e) { if(e.isPopupTrigger()) showPopup(e); }
public abstract void showPopup(MouseEvent e);
}
protected void fireActionEvent() {
if (deleting) return; // no events while deleting
super.fireActionEvent();
}
/**
* Add the item to the top of the list. If it already exists, move it to the top.
* @param item to be added.
*/
public void addItem( Object item) {
if (item == null) return;
for (int i=0; i<getItemCount(); i++) {
if (item.equals( getItemAt(i))) {
if (i == 0) {
setSelectedIndex(0);
return; // already there
}
removeItemAt(i);
}
}
// add as first in the list
insertItemAt( item, 0);
setSelectedIndex(0);
}
/** Save the last n items to PreferencesExt. */
public void save() {
if (prefs != null)
prefs.putList(LIST, getItemList());
}
/**
* Use this to obtain the list of items.
* @return ArrayList of items, may be any Object type.
*/
public List<Object> getItemList() {
ArrayList<Object> list = new ArrayList<Object>();
for (int i=0; i< getItemCount() && i < nkeep; i++)
list.add( getItemAt(i));
return list;
}
/**
* Use this to set the list of items.
* @param list of items, may be any Object type.
*/
public void setItemList(Collection<Object> list) {
if (list == null) return;
setModel( new DefaultComboBoxModel( list.toArray()));
if (list.size() > 0)
setSelectedIndex(0);
}
/** Set the number of items to keep */
public void setNkeep( int nkeep) { this.nkeep = nkeep; }
/** Get the number of items to keep */
public int getNkeep() { return nkeep; }
/** Get value from Store, will be an ArrayList or null */
protected Object getStoreValue(Object defValue) {
if (prefs == null)
return defValue;
return ((PreferencesExt)prefs).getBean(LIST, defValue);
}
/** Put new value into Store, must be a List of Strings */
protected void setStoreValue(List newValue) {
if (prefs != null)
prefs.putList(LIST, newValue);
}
// debug
private static long lastEvent;
public static void main(String args[]) throws IOException {
JFrame frame = new JFrame("Test");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
final ComboBox cb = new ComboBox( null);
cb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("**** cb event="+e);
if (e.getActionCommand().equals("comboBoxChanged")) {
//System.out.println("cb.getSelectedItem="+cb.getSelectedItem());
cb.addItem( cb.getSelectedItem());
}
}
});
cb.getEditor().getEditorComponent().setForeground(Color.red);
/* JButton butt = new JButton("accept");
butt.addActionListener( new AbstractAction() {
public void actionPerformed(ActionEvent e) {
System.out.println("butt accept");
cb.accept();
}
}); */
JPanel main = new JPanel();
main.add(cb);
// main.add(butt);
frame.getContentPane().add(main);
// cb.setPreferredSize(new java.awt.Dimension(500, 200));
frame.pack();
frame.setLocation(300, 300);
frame.setVisible(true);
}
}