package com.limegroup.gnutella.gui; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.io.File; import java.util.Vector; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; /** * This reusable component is a list with buttons for adding and removing * elements. The add button brings up a dialog window to retrieve * the element to add from the user. The remove button is only enabled * when there is an item selected in the list. */ //2345678|012345678|012345678|012345678|012345678|012345678|012345678|012345678| public final class StandardListEditor { /** * Constant handle to the main panel in the editor. */ private final JPanel MAIN_PANEL = new JPanel(new GridBagLayout()); /** * Constant handle to the underlying <tt>JList</tt> instance. */ private final JList LIST = new JList(); /** * Handle to the remove button to allow us to set its state. */ private final JButton REMOVE_BUTTON; /** * Handle to the add button. */ private final JButton ADD_BUTTON; /** * Default add action which delegates to the <code>addListener</code> if it is set. */ private AddAction _addAction; /** * Handle to the remove action used for disabling it when no item in the list is selected. */ private RemoveAction _removeAction; /** * Handle to the set add listener. */ private ActionListener _addListener = null; /** * Member variable for whether or not the list data has changed * since the last call to reset this value. */ private boolean _listChanged = false; /** * Creates a <tt>StandardListEditor</tt> with the list of elements on * the left and buttons for adding and removing elements on the * right. The key allows for a custom label for the text field * in the dialog window that the "Add..." button pops up. For example, * using <p> * * OPTIONS_AUTO_CONNECT_INPUT_FIELD_LABEL=Enter Host Address:<p> * * in the messages bundle will make "Enter Host Address: " appear * as the label for the text field in dialog window popped up by * the "add" button. * * @param INPUT_FIELD_KEY the key for the locale-specific label for the dialog * window popped up by the "Add..." button */ public StandardListEditor(final String INPUT_FIELD_KEY) { this("LIST_EDITOR_ADD_BUTTON", "LIST_EDITOR_REMOVE_BUTTON",INPUT_FIELD_KEY); } /** * Creates a <tt>StandardListEditor</tt> with the default settings * for the names of the add and remove buttons and that uses * the specified <tt>ActionListener</tt> instance as the listener * for the add button. */ public StandardListEditor(ActionListener listener) { this("LIST_EDITOR_ADD_BUTTON", "LIST_EDITOR_REMOVE_BUTTON",""); setAddActionListener(listener); } /** * More flexible constructor that allows the text for the add and * remove buttons to be set and that allows a custom <tt>ActionListener</tt> * for the add button. * * @param ADD_BUTTON_KEY the key for the locale-specific string to use for * the add button * @param REMOVE_BUTTON_KEY the key for the locale-specific string to use * for the remove button * @param INPUT_FIELD_KEY the key for the locale-specific string to use * for the label in the generic text input component used by default */ public StandardListEditor(final String ADD_BUTTON_KEY, final String REMOVE_BUTTON_KEY, final String INPUT_FIELD_KEY) { _addAction = new AddAction(ADD_BUTTON_KEY, INPUT_FIELD_KEY); _removeAction = new RemoveAction(REMOVE_BUTTON_KEY); GUIUtils.bindKeyToAction(LIST, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), _removeAction); LIST.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); LIST.addListSelectionListener(new ListEditorSelectionListener()); JScrollPane scrollPane = new JScrollPane(LIST); GridBagConstraints gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.NORTHWEST; gbc.gridheight = GridBagConstraints.REMAINDER; gbc.weightx = 1; gbc.weighty = 1; gbc.fill = GridBagConstraints.BOTH; MAIN_PANEL.add(scrollPane, gbc); gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.NORTHWEST; gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(0, 3, ButtonRow.BUTTON_SEP, 0); ADD_BUTTON = new JButton(_addAction); MAIN_PANEL.add(ADD_BUTTON, gbc); REMOVE_BUTTON = new JButton(_removeAction); REMOVE_BUTTON.setEnabled(false); MAIN_PANEL.add(REMOVE_BUTTON, gbc); } /** * Provides access to the wrapped <tt>Component</tt> of the * <tt>StandardListEditor</tt>. This is the <tt>Component</tt> * that other gui elements should add. * * @return the underlying <tt>Component</tt> that contains all * of the elements fo the <tt>StandardListEditor</tt> */ public Component getComponent() { return MAIN_PANEL; } /** * Adds the specified object to the end of the list. * * @param element the <tt>Object</tt> to add */ private void addElement(Object element) { DefaultListModel model = (DefaultListModel)LIST.getModel(); model.addElement(element); } /** * Adds the specified <tt>File</tt> instance to the end of the list. * * @param file the <tt>File</tt> to add */ public void addFile(File file) { addElement(file); } /** * Adds the specified <tt>String</tt> instance to the end of the list. * * @param string the <tt>String</tt> to add */ public void addString(String string) { addElement(string); } /** * Sets the data in the underlying <tt>ListModel</tt>. * * @param data the <tt>Vector</tt> containing data for the model */ public void setListData(Vector data) { DefaultListModel model = new DefaultListModel(); for (int i=0; i<data.size(); i++) model.addElement(data.get(i)); LIST.setModel(model); } /** * Sets the data in the underlying <tt>ListModel</tt>. * * @param data array of strings containing the data for the model */ private void setListDataObjects(Object[] data) { DefaultListModel model = new DefaultListModel(); for (int i=0; i<data.length; i++) model.addElement(data[i]); LIST.setModel(model); } /** * Sets the data in the underlying <tt>ListModel</tt>. * * @param data array of <tt>File</tt> instances containing the data for the * model */ public void setListData(File[] data) { setListDataObjects(data); } /** * Sets the data in the underlying <tt>ListModel</tt>. * * @param data array of <tt>String</tt> instances containing the data for the * model */ public void setListData(String[] data) { setListDataObjects(data); } /** * Clears the selections in the list. */ public void clearSelection() { LIST.clearSelection(); } /** * Returns an array of the underlying data represented as strings * by calling toString() on each element. * * @return the list data as an array of strings. */ public String[] getDataAsStringArray() { DefaultListModel model = (DefaultListModel)LIST.getModel(); Object[] dataObjects = model.toArray(); String[] dataStrings = new String[dataObjects.length]; for(int i=0; i<dataObjects.length; i++) { dataStrings[i] = dataObjects[i].toString(); } return dataStrings; } /** * Returns an array of the underlying data represented as <tt>File</tt> * instances. * * @return the list data as an array of <tt>File</tt> instances. */ public File[] getDataAsFileArray() { DefaultListModel model = (DefaultListModel)LIST.getModel(); Object[] dataObjects = model.toArray(); File[] dataFiles = new File[dataObjects.length]; for(int i=0; i<dataObjects.length; i++) { dataFiles[i] = (File)dataObjects[i]; } return dataFiles; } /** * Returns an array of the underlying data represented as objects. * * @return the list data as an array of objects. */ public Object[] getDataAsObjectArray() { DefaultListModel model = (DefaultListModel)LIST.getModel(); return model.toArray(); } /** * Sets the <tt>ActionListener</tt> to use for the add button. * * @param addAction the <tt>ActionListener</tt> to use for the add button */ private void setAddActionListener(ActionListener addListener) { this._addListener = addListener; } /** * Returns whether or not the list has changed since the last * call to reset the list. Note that this will not be * accurate if you use your own <tt>ActionListener</tt> for the * add button. In this case, this will return whether or not * elements have been removed from the list. * * @return <tt>true</tt> if the list has changed since the last * call to reset the list, <tt>false</tt> otherwise */ public boolean getListChanged() { return _listChanged; } /** * Resets the value for whether or not the list has changed to * <tt>false</tt>. */ public void resetList() { _listChanged = false; } /** * This class responds to a click of the add button and pops * up a window for the user to enter a new element to add * to the list. */ private class AddAction extends AbstractAction { private final String INPUT_FIELD_KEY; public AddAction(final String name, final String key) { putValue(Action.NAME, GUIMediator.getStringResource(name)); putValue(Action.SHORT_DESCRIPTION, GUIMediator.getStringResource("LIST_EDITOR_ADD_BUTTON_TIP")); INPUT_FIELD_KEY = key; } public void actionPerformed(ActionEvent e) { // delegate event if there is a special addListener if (_addListener != null) { _addListener.actionPerformed(e); } else { InputFieldDialog dialog = new InputFieldDialog(INPUT_FIELD_KEY); int returnCode = dialog.showDialog(); if(returnCode == InputFieldDialog.TEXT_ENTERED) { _listChanged = true; addElement(dialog.getText()); } } } } /** * This class responds to a click of the remove button and removes * the selected element from the list. */ private class RemoveAction extends AbstractAction { public RemoveAction(final String name) { putValue(Action.NAME, GUIMediator.getStringResource(name)); putValue(Action.SHORT_DESCRIPTION, GUIMediator.getStringResource("LIST_EDITOR_REMOVE_BUTTON_TIP")); } public void actionPerformed(ActionEvent e) { _listChanged = true; // return if nothing is selected if(LIST.isSelectionEmpty()) return; DefaultListModel model = (DefaultListModel)LIST.getModel(); model.remove(LIST.getSelectedIndex()); if(LIST.isSelectionEmpty()) { REMOVE_BUTTON.setEnabled(false); } } } /** * This private class handles selection of items in the list. It * controls the state of the remove button as well, disabling it * if nothing is selected. */ private class ListEditorSelectionListener implements ListSelectionListener { /** * Implements the <tt>ListSelectionListener</tt> interface. * Responds to selections in the list. */ public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) return; if (LIST.isSelectionEmpty()) { REMOVE_BUTTON.setEnabled(false); } else { REMOVE_BUTTON.setEnabled(true); } } } }