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 {
clearSelection()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 
OurListModel(Object [] data)60     public OurListModel(Object [] data) {
61 	if (data != null) {
62 	    this.data.addAll(Arrays.asList(data));
63 	}
64     }
65 
addListDataListener(ListDataListener l)66     public void addListDataListener(ListDataListener l) {
67 	listenerList.add(ListDataListener.class, l);
68     }
69 
removeListDataListener(ListDataListener l)70     public void removeListDataListener(ListDataListener l) {
71 	listenerList.remove(ListDataListener.class, l);
72     }
73 
fireContentsChanged()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 
getElementAt(int index)91     public Object getElementAt(int index) {
92 	return data.get(index);
93     }
94 
getSize()95     public int getSize() {
96 	return data.size();
97     }
98 
addElement(Object o)99     public void addElement(Object o) {
100 	data.add(o);
101 	fireContentsChanged();
102     }
103 
removeElement(Object o)104     public void removeElement(Object o) {
105 	data.remove(data.indexOf(o));
106 	fireContentsChanged();
107     }
108 
toArray(Object [] arr)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 
ListPairLayout()123     public ListPairLayout() {
124 	leftComponent = centerComponent = rightComponent = null;
125     }
126 
addLayoutComponent(String name, Component comp)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 
layoutContainer(Container target)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 
minimumLayoutSize(Container parent)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 
preferredLayoutSize(Container parent)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 
removeLayoutComponent(Component comp)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      */
ListPair(String leftCaption, Object [] leftData, String rightCaption, Object [] rightData)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      */
getLeftContents(Object [] arr)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      */
getRightContents(Object [] arr)341     public Object [] getRightContents(Object [] arr) {
342 	return rightModel.toArray(arr);
343     }
344 }
345