1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.interview.wizard;
  28 
  29 import java.awt.BorderLayout;
  30 import java.awt.Component;
  31 import java.awt.Dimension;
  32 import java.awt.event.ActionEvent;
  33 import java.awt.event.ActionListener;
  34 import java.awt.event.MouseEvent;
  35 import java.awt.event.MouseListener;
  36 import java.lang.reflect.Array;
  37 import java.text.MessageFormat;
  38 import javax.swing.BorderFactory;
  39 import javax.swing.DefaultListCellRenderer;
  40 import javax.swing.DefaultListModel;
  41 import javax.swing.JButton;
  42 import javax.swing.JList;
  43 import javax.swing.JOptionPane;
  44 import javax.swing.JPanel;
  45 import javax.swing.JScrollPane;
  46 import javax.swing.JToolBar;
  47 import javax.swing.ListSelectionModel;
  48 import javax.swing.event.ListDataListener;
  49 import javax.swing.event.ListSelectionEvent;
  50 import javax.swing.event.ListSelectionListener;
  51 
  52 /**
  53  * A component that displays an editable list of values.
  54  */
  55 public class EditableList extends JPanel
  56 {
  57     /**
  58      * Create an EditableList.
  59      * @param uiKey A string used as the base of a key to look up resource values
  60      * for this item.
  61      * @param items An array of strings to display as initial values in the list.
  62      */
  63     public EditableList(String uiKey, Object[] items) {
  64         setLayout(new BorderLayout());
  65         setName(uiKey);
  66         setFocusable(false);
  67 
  68         listModel = new DefaultListModel<>(); // need to force the type of model
  69         if (items != null) {
  70             for (int i = 0; i < items.length; i++)
  71                 listModel.addElement(items[i]);
  72         }
  73         list = new JList<>(listModel);
  74         list.setName(uiKey);
  75         list.setToolTipText(i18n.getString(uiKey + ".tip"));
  76         list.getAccessibleContext().setAccessibleName(list.getName());
  77         list.getAccessibleContext().setAccessibleDescription(list.getToolTipText());
  78         list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  79         list.setCellRenderer(renderer);
  80         list.addListSelectionListener(listener);
  81         list.addMouseListener(listener);
  82         list.setVisibleRowCount(5);
  83 
  84         JScrollPane sp = new JScrollPane(list);
  85         sp.setName(uiKey + ".sp");
  86         sp.setFocusable(false);
  87         add(sp, BorderLayout.CENTER);
  88 
  89         JToolBar bar = new JToolBar(JToolBar.VERTICAL);
  90         bar.setName(uiKey + "bar");
  91         bar.setFocusable(false);
  92         bar.setFloatable(false);
  93         bar.add(addBtn  = createButton("elst.add"));
  94         bar.add(removeBtn = createButton("elst.remove"));
  95         bar.add(upBtn = createButton("elst.up"));
  96         bar.add(downBtn = createButton("elst.down"));
  97         add(bar, BorderLayout.EAST);
  98         updateButtons();
  99         setBorder(BorderFactory.createEtchedBorder());
 100     }
 101 
 102     public int getItemCount() {
 103         return listModel.getSize();
 104     }
 105 
 106     public Object getItem(int index) {
 107         return listModel.elementAt(index);
 108     }
 109 
 110     /**
 111      * Get the set of items in the list.
 112      * @return the set of items currently in the list
 113      */
 114     public Object[] getItems() {
 115         return listModel.toArray();
 116     }
 117 
 118     /**
 119      * Get the items currently in the list, in an array of a specific type.
 120      * @param c the component type of the array to be returned
 121      * @return an array containing the items currently in the list
 122      */
 123     public Object[] getItems(Class<?> c) {
 124         Object[] items = (Object[]) (Array.newInstance(c, listModel.size()));
 125         listModel.copyInto(items);
 126         return items;
 127     }
 128 
 129     public int getSelectedIndex() {
 130         return list.getSelectedIndex();
 131     }
 132 
 133     public Object getSelectedItem() {
 134         return list.getSelectedValue();
 135     }
 136 
 137     public void setSelectedItem(Object item) {
 138         list.setSelectedValue(item, true);
 139     }
 140 
 141     public void addListDataListener(ListDataListener l) {
 142         listModel.addListDataListener(l);
 143     }
 144 
 145     public void removeListDataListener(ListDataListener l) {
 146         listModel.removeListDataListener(l);
 147     }
 148 
 149     /**
 150      * Specify whether or not duplicates should be allowed in the list.
 151      * @param b true if duplicates should be allowed, and false otherwise
 152      * @see #isDuplicatesAllowed
 153      */
 154     public void setDuplicatesAllowed(boolean b) {
 155         duplicatesAllowed = b;
 156     }
 157 
 158     /**
 159      * Check whether or not duplicates should be allowed in the list.
 160      * @return true if duplicates should be allowed, and false otherwise
 161      * @see #setDuplicatesAllowed
 162      */
 163     public boolean isDuplicatesAllowed() {
 164         return duplicatesAllowed;
 165     }
 166 
 167     protected Object getDisplayValue(Object item) {
 168         return item;
 169     }
 170 
 171     /**
 172      * Invoked to get a new item to put in the list, when the user clicks
 173      * the "Add" button". The default is to show an input dialog to allow
 174      * the user to type in a new string. Subtypes may override this method
 175      * to provide other ways of specifying items to be added, such as a
 176      * file chooser.
 177      * @return an object to be added to the list, or null if no object
 178      * to be added.
 179      */
 180     protected Object getNewItem() {
 181         return JOptionPane.showInputDialog(this, i18n.getString("elst.add.title"));
 182     }
 183 
 184     /**
 185      * Invoked to get a new item to replace an existing item in the list.
 186      * The default is to show an input dialog to allow
 187      * the user to type in a new string. Subtypes may override this method
 188      * to provide other ways of specifying items to be added, such as a
 189      * file chooser.
 190      * @param the item to be replaced in the list
 191      * @return an object to replace the old item the list, or null if no
 192      * replacement should occur.
 193      */
 194     protected Object getNewItem(Object oldItem) {
 195         return JOptionPane.showInputDialog(this, i18n.getString("elst.change.title"), oldItem);
 196     }
 197 
 198     protected void itemsChanged() {
 199     }
 200 
 201     protected void selectedItemChanged() {
 202     }
 203 
 204     protected void insertItem() {
 205         Object newItem = getNewItem();
 206 
 207         if (!duplicatesAllowed && listModel.contains(newItem)) {
 208             showDuplicateError(newItem);
 209             return;
 210         }
 211 
 212         if (newItem != null) {
 213             if (list.isSelectionEmpty())
 214                 listModel.addElement(newItem);
 215             else
 216                 listModel.add(1 + list.getSelectedIndex(), newItem);
 217             list.setSelectedValue(newItem, true);
 218         }
 219     }
 220 
 221     protected void removeSelectedItem() {
 222         if (!list.isSelectionEmpty())
 223             listModel.remove(list.getSelectedIndex());
 224     }
 225 
 226     protected void moveSelectedItemUp() {
 227         if (!list.isSelectionEmpty()) {
 228             int i = list.getSelectedIndex();
 229             if (i > 0) {
 230                 swap(i, i - 1);
 231                 list.setSelectedIndex(i - 1);
 232             }
 233         }
 234     }
 235 
 236     protected void moveSelectedItemDown() {
 237         if (!list.isSelectionEmpty()) {
 238             int i = list.getSelectedIndex();
 239             if (i + 1 < listModel.size()) {
 240                 swap(i, i + 1);
 241                 list.setSelectedIndex(i + 1);
 242             }
 243         }
 244     }
 245 
 246     protected void editItem(int index) {
 247         Object newItem = getNewItem(listModel.getElementAt(index));
 248 
 249         if (!duplicatesAllowed && listModel.contains(newItem)) {
 250             showDuplicateError(newItem);
 251             return;
 252         }
 253 
 254         if (newItem != null)
 255             listModel.set(index, newItem);
 256     }
 257 
 258 
 259     protected JButton createButton(String uiKey) {
 260         JButton b = new JButton(i18n.getString(uiKey + ".btn"));
 261         b.setName(uiKey);
 262         b.setToolTipText(i18n.getString(uiKey + ".tip"));
 263         b.setMnemonic(i18n.getString(uiKey + ".mne").charAt(0));
 264         // set max size so button can grow within toolbar
 265         b.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
 266         b.addActionListener(listener);
 267         return b;
 268     }
 269 
 270     private void showDuplicateError(Object item) {
 271         String text = MessageFormat.format(i18n.getString("elst.duplicate.text"),
 272                                            new Object[] { getDisplayValue(item) });
 273 
 274         String title = i18n.getString("elst.duplicate.title");
 275 
 276         JOptionPane.showMessageDialog(this,
 277                                       text,
 278                                       title,
 279                                       JOptionPane.INFORMATION_MESSAGE);
 280     }
 281 
 282     protected class Renderer
 283         extends DefaultListCellRenderer {
 284         public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
 285             return super.getListCellRendererComponent(list,
 286                                                       getDisplayValue(value),
 287                                                       index,
 288                                                       isSelected,
 289                                                       cellHasFocus);
 290         }
 291     }
 292 
 293     protected class Listener
 294         implements ActionListener, ListSelectionListener, MouseListener {
 295         // ActionListener, for add, remove, up, down buttons
 296         public void actionPerformed(ActionEvent e) {
 297             Object src = e.getSource();
 298             if (src == addBtn) {
 299                 insertItem();
 300             }
 301             else if (src == removeBtn) {
 302                 removeSelectedItem();
 303             }
 304             else if (src == upBtn) {
 305                 moveSelectedItemUp();
 306             }
 307             else if (src == downBtn) {
 308                 moveSelectedItemDown();
 309             }
 310             itemsChanged();
 311             updateButtons();
 312         }
 313 
 314         // ListSelectionListener, to track list selection changes
 315         public void valueChanged(ListSelectionEvent e) {
 316             selectedItemChanged();
 317             updateButtons();
 318         }
 319 
 320         // MouseListener, to react to double click in list
 321         public void mouseClicked(MouseEvent e) {
 322             if (e.getClickCount() == 2) {
 323                 int index = list.locationToIndex(e.getPoint());
 324                 if(index != -1)
 325                     editItem(index);
 326             }
 327         }
 328 
 329         public void mouseEntered(MouseEvent e) { }
 330         public void mouseExited(MouseEvent e) { }
 331         public void mousePressed(MouseEvent e) { }
 332         public void mouseReleased(MouseEvent e) { }
 333     }
 334 
 335     protected void updateButtons() {
 336         if (list.isSelectionEmpty()) {
 337             removeBtn.setEnabled(false);
 338             upBtn.setEnabled(false);
 339             downBtn.setEnabled(false);
 340         }
 341         else {
 342             removeBtn.setEnabled(true);
 343             int i = list.getSelectedIndex();
 344             upBtn.setEnabled(i > 0);
 345             downBtn.setEnabled((i + 1 < listModel.size()));
 346         }
 347     }
 348 
 349 
 350     private void swap(int i1, int i2) {
 351         Object o1 = listModel.elementAt(i1);
 352         Object o2 = listModel.elementAt(i2);
 353         listModel.set(i1, o2);
 354         listModel.set(i2, o1);
 355     }
 356 
 357     protected Listener createListener() {
 358         return new Listener();
 359     }
 360 
 361     protected Renderer createRenderer() {
 362         return new Renderer();
 363     }
 364 
 365     protected boolean duplicatesAllowed;
 366     protected Listener listener = createListener();
 367     protected Renderer renderer = createRenderer();
 368     protected DefaultListModel<Object> listModel;
 369     protected JList<Object> list;
 370     protected JButton addBtn;
 371     protected JButton removeBtn;
 372     protected JButton upBtn;
 373     protected JButton downBtn;
 374 
 375     private static final I18NResourceBundle i18n = I18NResourceBundle.getDefaultBundle();
 376 }