/* * $Id$ * * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.javatest.tool; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.File; import java.lang.reflect.Array; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.swing.BorderFactory; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JToolBar; import javax.swing.ListSelectionModel; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; /** * A component that displays an editable list of items. */ public class EditableList extends JComponent implements Accessible { /** * Create an empty component, using a standard UIFactory for this class, * and using resources beginning with "list.". */ public EditableList() { this(new UIFactory(EditableList.class, null), "list"); } private static UIFactory getDefaultUIF() { // use EditableList.class instead of "this" to get correct i18n if (defaultUIF == null) defaultUIF = new UIFactory(EditableList.class, null); // no help required return defaultUIF; } /** * Create an empty component, using a specified UIFactory and resource prefix. * @param uif The UIFactory used to construct the component * @param uiKey The prefix for any UI resources that may be required */ public EditableList(UIFactory uif, String uiKey) { this.uif = uif; setLayout(new BorderLayout()); listModel = new DefaultListModel<>(); // need to force the type of model listModel.addListDataListener(listener); list = uif.createList(uiKey, listModel); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setCellRenderer(renderer); list.addListSelectionListener(listener); add(new JScrollPane(list), BorderLayout.CENTER); JToolBar bar = new JToolBar(JToolBar.VERTICAL); bar.setFloatable(false); bar.add(addBtn = createButton(uiKey + ".add")); bar.add(removeBtn = createButton(uiKey + ".remove")); bar.add(upBtn = createButton(uiKey + ".up")); bar.add(downBtn = createButton(uiKey + ".down")); add(bar, BorderLayout.EAST); updateButtons(); setBorder(BorderFactory.createEtchedBorder()); } /** * Get the accessible context for this pane. * @return the accessible context for this pane */ public AccessibleContext getAccessibleContext() { if (accessibleContext == null) accessibleContext = new AccessibleJComponent() { }; return accessibleContext; } /** * Set whether or not the list can be edited by the user. * @param b if true, the component can be edited by the user; * if false, it cannot */ public void setEnabled(boolean b) { super.setEnabled(b); list.setEnabled(b); updateButtons(); } /** * Set the items in the list. Any previous items are removed first. * @param items the array of items to be put in the list. * @see #getItems */ public void setItems(Object[] items) { listModel.clear(); if (items != null) { for (int i = 0; i < items.length; i++) listModel.addElement(items[i]); } } /** * Remove all entries from the list. */ public void clear() { listModel.clear(); } /** * Get the items currently in the list. * @return an array containing the items currently in the list * @see #setItems */ public Object[] getItems() { return listModel.toArray(); } /** * Get the items currently in the list, in an array of a specific type. * @param c the component type of the array to be returned * @return an array containing the items currently in the list * @see #setItems */ public Object[] getItems(Class c) { Object[] items = (Object[]) (Array.newInstance(c, listModel.size())); listModel.copyInto(items); return items; } /** * Get the tool tip text that appears on the list. * (Separate tool tip text will appear on the buttons to manipulate the list.) * @return the tool tip text that appears on the list * @see #setToolTipText */ public String getToolTipText() { return list.getToolTipText(); } /** * Set the tool tip text that appears on the list. * (Separate tool tip text will appear on the buttons to manipulate the list.) * @param tip the tool tip text to appear on the list * @see #getToolTipText */ public void setToolTipText(String tip) { list.setToolTipText(tip); } /** * Add a listener to be notified of events when the list data changes. * @param l the listener to be notified * @see #removeListDataListener */ public void addListDataListener(ListDataListener l) { listenerList.add(ListDataListener.class, l); } /** * Remove a listener that was previously added to be notified of * events when the list data changes. * @param l the listener to be notified * @see #addListDataListener */ public void removeListDataListener(ListDataListener l) { listenerList.remove(ListDataListener.class, l); } /** * Specify whether or not duplicates should be allowed in the list. * @param b true if duplicates should be allowed, and false otherwise * @see #isDuplicatesAllowed */ public void setDuplicatesAllowed(boolean b) { duplicatesAllowed = b; } /** * Check whether or not duplicates should be allowed in the list. * @return true if duplicates should be allowed, and false otherwise * @see #setDuplicatesAllowed */ public boolean isDuplicatesAllowed() { return duplicatesAllowed; } /** * Get the display value for an item in the list. By default, the display * value is the item itself. * @param item the object for which to get the display value * @return the display value for the specified item */ protected Object getDisplayValue(Object item) { return item; } /** * Invoked to get a new item to put in the list, when the user clicks * the "Add" button". The default is to show an input dialog to allow * the user to type in a new string. Subtypes may override this method * to provide other ways of specifying items to be added, such as a * file chooser. * @return an object to be added to the list, or null if no object * to be added. */ protected Object getNewItem() { return JOptionPane.showInputDialog(this, uif.getI18NString("list.add.txt")); } /** * Invoked to get a new item to replace an existing item in the list. * The default is to show an input dialog to allow * the user to type in a new string. Subtypes may override this method * to provide other ways of specifying items to be added, such as a * file chooser. * @param oldItem the item to be replaced in the list * @return an object to replace the old item the list, or null if no * replacement should occur. */ protected Object getNewItem(Object oldItem) { return JOptionPane.showInputDialog(this, uif.getI18NString("list.change.txt"), oldItem); } private JButton createButton(String uiKey) { JButton b = uif.createButton(uiKey); // set max size so button can grow within toolbar b.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); b.addActionListener(listener); // should be in uif.createButton? b.setMnemonic(uif.getI18NString(uiKey+ ".mne").charAt(0)); return b; } private void insertItem() { Object newItem = getNewItem(); if (!duplicatesAllowed && listModel.contains(newItem)) { showDuplicateError(newItem); return; } if (newItem != null) { if (list.isSelectionEmpty()) listModel.addElement(newItem); else listModel.add(1 + list.getSelectedIndex(), newItem); list.setSelectedValue(newItem, true); } } private void removeSelectedItem() { if (!list.isSelectionEmpty()) listModel.remove(list.getSelectedIndex()); } private void moveSelectedItemUp() { if (!list.isSelectionEmpty()) { int i = list.getSelectedIndex(); if (i > 0) { swap(i, i - 1); list.setSelectedIndex(i - 1); } } } private void moveSelectedItemDown() { if (!list.isSelectionEmpty()) { int i = list.getSelectedIndex(); if (i + 1 < listModel.size()) { swap(i, i + 1); list.setSelectedIndex(i + 1); } } } private void editItem(int index) { Object newItem = getNewItem(listModel.getElementAt(index)); if (!duplicatesAllowed && listModel.contains(newItem)) { showDuplicateError(newItem); return; } if (newItem != null) listModel.set(index, newItem); } private void showDuplicateError(Object item) { String text = uif.getI18NString("list.duplicate.text", new Object[] { getDisplayValue(item) }); String title = uif.getI18NString("list.duplicate.title"); JOptionPane.showMessageDialog(this, text, title, JOptionPane.INFORMATION_MESSAGE); } private class Renderer extends DefaultListCellRenderer { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { return super.getListCellRendererComponent(list, getDisplayValue(value), index, isSelected, cellHasFocus); } } private class Listener implements ActionListener, ListDataListener, ListSelectionListener, MouseListener { // ActionListener events, for buttons public void actionPerformed(ActionEvent e) { Object src = e.getSource(); if (src == addBtn) insertItem(); else if (src == removeBtn) removeSelectedItem(); else if (src == upBtn) moveSelectedItemUp(); else if (src == downBtn) moveSelectedItemDown(); updateButtons(); } // ListSelect events, to update buttons depending on list selection public void valueChanged(ListSelectionEvent e) { updateButtons(); } // MouseListener, to react to double click in list public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { int index = list.locationToIndex(e.getPoint()); if(index != -1) editItem(index); } } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } // ListData events, to redispatch to client, with EditableList.this as the source public void contentsChanged(ListDataEvent e) { ListDataEvent e2 = null; Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ListDataListener.class) { if (e2 == null) e2 = new ListDataEvent(EditableList.this, e.getType(), e.getIndex0(), e.getIndex1()); ((ListDataListener)listeners[i+1]).contentsChanged(e2); } } } public void intervalAdded(ListDataEvent e) { ListDataEvent e2 = null; Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ListDataListener.class) { if (e2 == null) e2 = new ListDataEvent(EditableList.this, e.getType(), e.getIndex0(), e.getIndex1()); ((ListDataListener)listeners[i+1]).intervalAdded(e2); } } } public void intervalRemoved(ListDataEvent e) { ListDataEvent e2 = null; Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ListDataListener.class) { if (e2 == null) e2 = new ListDataEvent(EditableList.this, e.getType(), e.getIndex0(), e.getIndex1()); ((ListDataListener)listeners[i+1]).intervalRemoved(e2); } } } } private void updateButtons() { boolean enabled = isEnabled(); addBtn.setEnabled(enabled); if (list.isSelectionEmpty() || !enabled) { removeBtn.setEnabled(false); upBtn.setEnabled(false); downBtn.setEnabled(false); } else { removeBtn.setEnabled(true); int i = list.getSelectedIndex(); upBtn.setEnabled(i > 0); downBtn.setEnabled((i + 1 < listModel.size())); } } private void swap(int i1, int i2) { Object o1 = listModel.elementAt(i1); Object o2 = listModel.elementAt(i2); listModel.set(i1, o2); listModel.set(i2, o1); } /** * The factory used to create the GUI elements of the component. */ protected final UIFactory uif; // only initialize this if required private static UIFactory defaultUIF; /** * The list model that contains the elements of the list. */ protected DefaultListModel listModel; private JList list; private JButton addBtn; private JButton removeBtn; private JButton upBtn; private JButton downBtn; private Listener listener = new Listener(); private Renderer renderer = new Renderer(); private boolean duplicatesAllowed; }