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 }