1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * ident "%Z%%M% %I% %E% SMI" 24 * 25 * Copyright 2002 by Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 package com.sun.dhcpmgr.ui; 29 30 import java.awt.*; 31 import java.awt.event.ActionListener; 32 import java.awt.event.ActionEvent; 33 import javax.swing.*; 34 import javax.swing.event.*; 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 38 /* 39 * This class is a hack to get around the fact that clearSelection() 40 * in DefaultListSelectionModel does not always fire an event to its listeners. 41 * We rely on such an event to enable & disable the arrow buttons which 42 * move data items between lists. See bug 4177723. 43 */ 44 class FixedSelectionModel extends DefaultListSelectionModel { 45 public void clearSelection() { 46 super.clearSelection(); 47 fireValueChanged(getMinSelectionIndex(), getMaxSelectionIndex(), false); 48 } 49 } 50 51 /* 52 * We implement our own list model rather than use the default so 53 * that we can take advantage of some of the more advanced collection 54 * features not supported by DefaultListModel 55 */ 56 class OurListModel implements ListModel { 57 ArrayList data = new ArrayList(); 58 EventListenerList listenerList = new EventListenerList(); 59 60 public OurListModel(Object [] data) { 61 if (data != null) { 62 this.data.addAll(Arrays.asList(data)); 63 } 64 } 65 66 public void addListDataListener(ListDataListener l) { 67 listenerList.add(ListDataListener.class, l); 68 } 69 70 public void removeListDataListener(ListDataListener l) { 71 listenerList.remove(ListDataListener.class, l); 72 } 73 74 protected void fireContentsChanged() { 75 ListDataEvent e = 76 new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 77 0, getSize()); 78 Object [] listeners = listenerList.getListenerList(); 79 /* 80 * Listener array is formatted as pairs of (class, listener); walk 81 * the array backwards and call each ListDataListener in turn with 82 * the event. See javax.swing.event.EventListenerList for more info. 83 */ 84 for (int i = listeners.length - 2; i >= 0; i -= 2) { 85 if (listeners[i] == ListDataListener.class) { 86 ((ListDataListener)listeners[i+1]).contentsChanged(e); 87 } 88 } 89 } 90 91 public Object getElementAt(int index) { 92 return data.get(index); 93 } 94 95 public int getSize() { 96 return data.size(); 97 } 98 99 public void addElement(Object o) { 100 data.add(o); 101 fireContentsChanged(); 102 } 103 104 public void removeElement(Object o) { 105 data.remove(data.indexOf(o)); 106 fireContentsChanged(); 107 } 108 109 public Object [] toArray(Object [] arr) { 110 return data.toArray(arr); 111 } 112 } 113 114 /* 115 * Our own layout manager which keeps the left & right lists the same size. 116 */ 117 class ListPairLayout implements LayoutManager { 118 Component leftComponent, centerComponent, rightComponent; 119 public static final String LEFT = "left"; 120 public static final String CENTER = "center"; 121 public static final String RIGHT = "right"; 122 123 public ListPairLayout() { 124 leftComponent = centerComponent = rightComponent = null; 125 } 126 127 public void addLayoutComponent(String name, Component comp) { 128 if (name.equals(LEFT)) { 129 leftComponent = comp; 130 } else if (name.equals(CENTER)) { 131 centerComponent = comp; 132 } else if (name.equals(RIGHT)) { 133 rightComponent = comp; 134 } 135 } 136 137 public void layoutContainer(Container target) { 138 // Make left & right components same size, center no smaller than min 139 Insets insets = target.getInsets(); 140 Dimension dim = target.getSize(); 141 int x = insets.left; 142 int y = insets.top; 143 int totalHeight = dim.height - insets.bottom; 144 int totalWidth = dim.width - insets.right; 145 146 // If preferred sizes don't fit, go to minimum. 147 Dimension cDim = centerComponent.getPreferredSize(); 148 Dimension d = preferredLayoutSize(target); 149 if (d.width > totalWidth || d.height > totalHeight) { 150 cDim = centerComponent.getMinimumSize(); 151 } 152 153 // Left & right each get half of what's left after center allocated 154 int lrWidth = (totalWidth - cDim.width) / 2; 155 156 // Now place each component 157 leftComponent.setBounds(x, y, lrWidth, totalHeight); 158 centerComponent.setBounds(x + lrWidth, y, cDim.width, totalHeight); 159 rightComponent.setBounds(x + lrWidth + cDim.width, y, lrWidth, 160 totalHeight); 161 } 162 163 public Dimension minimumLayoutSize(Container parent) { 164 Dimension retDim = new Dimension(); 165 // Compute minimum width as max(leftwidth, rightwidth) * 2 + centerwidth 166 int lrwidth = Math.max(leftComponent.getMinimumSize().width, 167 rightComponent.getMinimumSize().width); 168 retDim.width = lrwidth * 2 + centerComponent.getMinimumSize().width; 169 // Compute minimum height as max(leftheight, rightheight, centerheight) 170 int lrheight = Math.max(leftComponent.getMinimumSize().height, 171 rightComponent.getMinimumSize().height); 172 retDim.height = Math.max(centerComponent.getMinimumSize().height, 173 lrheight); 174 return retDim; 175 } 176 177 public Dimension preferredLayoutSize(Container parent) { 178 Dimension retDim = new Dimension(); 179 // Preferred width is max(leftwidth, rightwidth) * 2 + centerwidth 180 int lrwidth = Math.max(leftComponent.getPreferredSize().width, 181 rightComponent.getPreferredSize().width); 182 retDim.width = lrwidth * 2 + centerComponent.getPreferredSize().width; 183 // Preferred height is max(leftheight, rightheight, centerheight) 184 int lrheight = Math.max(leftComponent.getPreferredSize().height, 185 rightComponent.getPreferredSize().height); 186 retDim.height = Math.max(centerComponent.getPreferredSize().height, 187 lrheight); 188 return retDim; 189 } 190 191 public void removeLayoutComponent(Component comp) { 192 // Do nothing 193 } 194 } 195 196 /** 197 * A ListPair provides a way to display two lists of objects and to move 198 * objects from one list to another. It is initialized with the contents 199 * of each list, and can be queried at any time for the contents of each list 200 */ 201 public class ListPair extends JPanel { 202 private JList leftList, rightList; 203 private OurListModel leftModel, rightModel; 204 private ListSelectionModel leftSelectionModel, rightSelectionModel; 205 private LeftButton leftButton = new LeftButton(); 206 private RightButton rightButton = new RightButton(); 207 private JScrollPane leftPane, rightPane; 208 209 /** 210 * Construct a ListPair with the specified data and captions for each list 211 * @param leftCaption Caption for left list 212 * @param leftData An array of objects to display in the left list 213 * @param rightCaption Caption for right list 214 * @param rightData An array of objects to display in the right list 215 */ 216 public ListPair(String leftCaption, Object [] leftData, String rightCaption, 217 Object [] rightData) { 218 219 // Use our custom layout manager 220 setLayout(new ListPairLayout()); 221 222 // Store data into the list models 223 leftModel = new OurListModel(leftData); 224 rightModel = new OurListModel(rightData); 225 226 // Now create the lists 227 leftList = new JList(leftModel); 228 rightList = new JList(rightModel); 229 leftList.setSelectionModel(new FixedSelectionModel()); 230 rightList.setSelectionModel(new FixedSelectionModel()); 231 232 // Now do the layout 233 JPanel leftPanel = new JPanel(new BorderLayout()); 234 235 JLabel leftCapLbl = new JLabel(leftCaption); 236 leftCapLbl.setLabelFor(leftPanel); 237 leftCapLbl.setToolTipText(leftCaption); 238 leftPanel.add(leftCapLbl, BorderLayout.NORTH); 239 240 leftPane = new JScrollPane(leftList); 241 leftPanel.add(leftPane, BorderLayout.CENTER); 242 add(leftPanel, ListPairLayout.LEFT); 243 244 JPanel centerPanel = new JPanel(new VerticalButtonLayout()); 245 rightButton.setEnabled(false); 246 leftButton.setEnabled(false); 247 centerPanel.add(rightButton); 248 centerPanel.add(leftButton); 249 add(centerPanel, ListPairLayout.CENTER); 250 251 JPanel rightPanel = new JPanel(new BorderLayout()); 252 253 JLabel rightCapLbl = new JLabel(rightCaption); 254 rightCapLbl.setLabelFor(rightPanel); 255 rightCapLbl.setToolTipText(rightCaption); 256 rightPanel.add(rightCapLbl, BorderLayout.NORTH); 257 258 rightPane = new JScrollPane(rightList); 259 rightPanel.add(rightPane, BorderLayout.CENTER); 260 add(rightPanel, ListPairLayout.RIGHT); 261 262 // Now create and hook up the listeners for selection state 263 leftSelectionModel = leftList.getSelectionModel(); 264 leftSelectionModel.addListSelectionListener( 265 new ListSelectionListener() { 266 public void valueChanged(ListSelectionEvent e) { 267 // Ignore if user is dragging selection state 268 if (e.getValueIsAdjusting()) { 269 return; 270 } 271 // Right enabled only if something is selected in left list 272 rightButton.setEnabled(!leftSelectionModel.isSelectionEmpty()); 273 if (!leftSelectionModel.isSelectionEmpty()) { 274 rightSelectionModel.clearSelection(); 275 } 276 } 277 }); 278 279 rightSelectionModel = rightList.getSelectionModel(); 280 rightSelectionModel.addListSelectionListener( 281 new ListSelectionListener() { 282 public void valueChanged(ListSelectionEvent e) { 283 // Ignore if user is dragging selection state 284 if (e.getValueIsAdjusting()) { 285 return; 286 } 287 // Left enabled only if something is selected in the right list 288 leftButton.setEnabled(!rightSelectionModel.isSelectionEmpty()); 289 if (!rightSelectionModel.isSelectionEmpty()) { 290 leftSelectionModel.clearSelection(); 291 } 292 } 293 }); 294 295 // Now add listeners to buttons to move data between lists 296 rightButton.addActionListener(new ActionListener() { 297 public void actionPerformed(ActionEvent e) { 298 Object [] values = leftList.getSelectedValues(); 299 for (int i = 0; i < values.length; ++i) { 300 rightModel.addElement(values[i]); 301 leftModel.removeElement(values[i]); 302 } 303 /* 304 * Clear the selection state; this shouldn't be necessary, 305 * but the selection and data models are unfortunately not 306 * hooked up to handle this automatically 307 */ 308 leftSelectionModel.clearSelection(); 309 } 310 }); 311 312 leftButton.addActionListener(new ActionListener() { 313 public void actionPerformed(ActionEvent e) { 314 Object [] values = rightList.getSelectedValues(); 315 for (int i = 0; i < values.length; ++i) { 316 leftModel.addElement(values[i]); 317 rightModel.removeElement(values[i]); 318 } 319 /* 320 * Clear the selection state; this shouldn't be necessary, 321 * but the selection and data models are unfortunately not 322 * hooked up to handle this automatically 323 */ 324 rightSelectionModel.clearSelection(); 325 } 326 }); 327 } 328 329 /** 330 * Retrieve the contents of the left list 331 * @return the contents as an array of Object 332 */ 333 public Object [] getLeftContents(Object [] arr) { 334 return leftModel.toArray(arr); 335 } 336 337 /** 338 * Retrieve the contents of the right list 339 * @return the contents as an array of Object 340 */ 341 public Object [] getRightContents(Object [] arr) { 342 return rightModel.toArray(arr); 343 } 344 }