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 }