001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.util.swing;
038:
039: import javax.swing.*;
040: import java.awt.*;
041: import java.awt.event.*;
042: import java.util.*;
043:
044: /**
045: * <p>The ScrollableListDialog is a popup dialog with a message and a
046: * scrollable list of items. A ScrollableListDialog should be used when
047: * a message may need to display a variable number of items, for
048: * example, when reporting missing files.</p>
049: *
050: * <p>The message (also know as the leader text) is displayed above the
051: * items with an optional icon. The items are displayed in a scrollable
052: * list. Buttons are added below the list of items.</p>
053: *
054: * <p>This dialog is somewhat styled after
055: * {@link javax.swing.JOptionPane} and uses the message-type constants
056: * from JOptionPane.</p>
057: *
058: * @author Chris Warrington
059: * @version $Id$
060: * @since 2007-03-06
061: */
062: public class ScrollableListDialog extends JDialog {
063: /** The default width for this dialog. */
064: private static final int DEFAULT_WIDTH = 400;
065: /** The default height for this dialog. */
066: private static final int DEFAULT_HEIGHT = 450;
067:
068: /** The ratio of the screen width to use by default. */
069: private static final double WIDTH_RATIO = .75;
070: /** The ratio of the screen height to use by default. */
071: private static final double HEIGHT_RATIO = .50;
072:
073: /** The list of items displayed. */
074: protected final JList list;
075:
076: /**
077: * <p>Creates a new ScrollableListDialog with the given title, leader
078: * text, and items. The list of items is used to construct an
079: * internal string list that is not backed by the original list.
080: * Changes made to the list or items after dialog construction will
081: * not be reflected in the dialog.</p>
082: *
083: * <p>The default sizing, message type, and icon are used.</p>
084: *
085: * @param owner The frame that owns this dialog. May be {@code null}.
086: * @param dialogTitle The text to use as the dialog title.
087: * @param leaderText Text to display before the list of items.
088: * @param listItems The items to display in the list.
089: *
090: * @throws IllegalArgumentException if {@code listItems} is {@code null.}
091: */
092: public ScrollableListDialog(Frame owner, String dialogTitle,
093: String leaderText, Collection<?> listItems) {
094: this (owner, dialogTitle, leaderText, listItems,
095: JOptionPane.PLAIN_MESSAGE);
096: }
097:
098: /**
099: * <p>Creates a new ScrollableListDialog with the given title, leader
100: * text, items, and message type. The list of items is used to
101: * construct an internal string list that is not backed by the
102: * original list. Changes made to the list or items after dialog
103: * construction will not be reflected in the dialog.</p>
104: *
105: * <p>The message type must be one of the message types from
106: * {@link javax.swing.JOptionPane}. The message type controlls which
107: * default icon is used.</p>
108: *
109: * <p>The default sizing and icon are used.</p>
110: *
111: * @param owner The frame that owns this dialog. May be {@code null}.
112: * @param dialogTitle The text to use as the dialog title.
113: * @param leaderText Text to display before the list of items.
114: * @param listItems The items to display in the list.
115: * @param messageType The type of dialog message.
116: *
117: * @throws IllegalArgumentException if {@code listItems} is {@code null.}
118: * @throws IllegalArgumentException if the message type is unknown or {@code listItems} is {@code null.}
119: */
120: public ScrollableListDialog(Frame owner, String dialogTitle,
121: String leaderText, Collection<?> listItems, int messageType) {
122: this (owner, dialogTitle, leaderText, listItems, messageType,
123: DEFAULT_WIDTH, DEFAULT_HEIGHT, null, true);
124: }
125:
126: /**
127: * <p>Creates a new ScrollableListDialog with the given title, leader
128: * text, items, message type, width, height, and icon. The list of
129: * items is used to construct an internal string list that is not
130: * backed by the original list. Changes made to the list or items
131: * after dialog construction will not be reflected in the dialog.</p>
132: *
133: * <p>The message type must be one of the message types from
134: * {@link javax.swing.JOptionPane}. The message type controlls which
135: * default icon is used. If {@code icon} is non-null, it is used
136: * instead of the default icon.</p>
137: *
138: * @param owner The frame that owns this dialog. May be {@code null}.
139: * @param dialogTitle The text to use as the dialog title.
140: * @param leaderText Text to display before the list of items.
141: * @param listItems The items to display in the list.
142: * @param messageType The type of dialog message.
143: * @param width The width of the dialog box.
144: * @param height The height of the dialog box.
145: * @param icon The icon to display. May be {@code null}.
146: *
147: * @throws IllegalArgumentException if {@code listItems} is {@code null.}
148: * @throws IllegalArgumentException if the message type is unknown or {@code listItems} is {@code null.}
149: */
150: public ScrollableListDialog(Frame owner, String dialogTitle,
151: String leaderText, Collection<?> listItems,
152: int messageType, int width, int height, Icon icon) {
153: this (owner, dialogTitle, leaderText, listItems, messageType,
154: width, height, icon, false);
155: }
156:
157: /**
158: * <p>Creates a new ScrollableListDialog with the given title, leader
159: * text, items, message type, width, height, and icon. The list of
160: * items is used to construct an internal string list that is not
161: * backed by the original list. Changes made to the list or items
162: * after dialog construction will not be reflected in the dialog.</p>
163: *
164: * <p>The message type must be one of the message types from
165: * {@link javax.swing.JOptionPane}. The message type controlls which
166: * default icon is used. If {@code icon} is non-null, it is used
167: * instead of the default icon.</p>
168: *
169: * @param owner The frame that owns this dialog. May be {@code null}.
170: * @param dialogTitle The text to use as the dialog title.
171: * @param leaderText Text to display before the list of items.
172: * @param listItems The items to display in the list.
173: * @param messageType The type of dialog message.
174: * @param width The width of the dialog box.
175: * @param height The height of the dialog box.
176: * @param icon The icon to display. May be {@code null}.
177: * @param fitToScreen If {@code true}, the width and height of the dialog will be calculated using the screen dimensions, {@link #WIDTH_RATIO}, and {@link #HEIGHT_RATIO}. If {@code false}, the provided width and height will be used.
178: *
179: * @throws IllegalArgumentException if {@code listItems} is {@code null.}
180: * @throws IllegalArgumentException if the message type is unknown or {@code listItems} is {@code null.}
181: */
182: private ScrollableListDialog(Frame owner, String dialogTitle,
183: String leaderText, Collection<?> listItems,
184: int messageType, int width, int height, Icon icon,
185: boolean fitToScreen) {
186: super (owner, dialogTitle, true);
187:
188: if (!_isknownMessageType(messageType)) {
189: throw new IllegalArgumentException("The message type \""
190: + messageType + "\" is unknown");
191: }
192:
193: if (listItems == null) {
194: throw new IllegalArgumentException(
195: "listItems cannot be null");
196: }
197:
198: /* create the leader text panel */
199: JLabel dialogIconLabel = null;
200: if (icon != null) {
201: //use the user-provided icon
202: dialogIconLabel = new JLabel(icon);
203: } else {
204: //lookup the message-dependent icon
205: Icon messageIcon = _getIcon(messageType);
206: if (messageIcon != null) {
207: dialogIconLabel = new JLabel(messageIcon);
208: }
209: }
210:
211: final JLabel leaderLabel = new JLabel(leaderText);
212: final JPanel leaderPanel = new JPanel();
213: leaderPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
214: if (dialogIconLabel != null) {
215: leaderPanel.add(dialogIconLabel);
216: }
217: leaderPanel.add(leaderLabel);
218:
219: /* create the item list box */
220: //copy the items string representations into a vector
221: final Vector<String> dataAsStrings = new Vector<String>(
222: listItems.size());
223: //keep track of the longest string for use later
224: String longestString = "";
225: for (Object obj : listItems) {
226: if (obj != null) {
227: final String objAsString = obj.toString();
228: //update longest string
229: if (objAsString.length() > longestString.length()) {
230: longestString = objAsString;
231: }
232: dataAsStrings.add(objAsString);
233: }
234: }
235:
236: list = new JList(dataAsStrings);
237: //since we are not using the selection, limit it to one item
238: list
239: .setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
240: //use the longest string to calculate item cell widths and heights
241: list.setPrototypeCellValue(longestString);
242:
243: //create a scrollable view around the list
244: final JScrollPane scrollPane = new JScrollPane(list);
245:
246: /* create the button panel */
247: final JPanel buttonPanel = new JPanel();
248: buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
249: //allow children to add additional buttons, if overridden
250: _addButtons(buttonPanel);
251:
252: /* create the dialog */
253: final JPanel contentPanel = new JPanel();
254: contentPanel.setLayout(new BorderLayout(10, 5));
255: contentPanel.setBorder(BorderFactory.createEmptyBorder(5, 10,
256: 0, 10));
257:
258: contentPanel.add(leaderPanel, BorderLayout.NORTH);
259: contentPanel.add(scrollPane, BorderLayout.CENTER);
260: contentPanel.add(buttonPanel, BorderLayout.SOUTH);
261:
262: getContentPane().add(contentPanel);
263:
264: /* calculate the dialog's dimensions */
265: Dimension dialogSize = new Dimension();
266:
267: if (fitToScreen) {
268: //use the screen dimensions to calculate the dialog's
269: Dimension screenSize = Toolkit.getDefaultToolkit()
270: .getScreenSize();
271: int screenBasedWidth = (int) (WIDTH_RATIO * screenSize
272: .getWidth());
273: int screenBasedHeight = (int) (HEIGHT_RATIO * screenSize
274: .getHeight());
275:
276: dialogSize.setSize(Math
277: .max(DEFAULT_WIDTH, screenBasedWidth), Math.max(
278: DEFAULT_HEIGHT, screenBasedHeight));
279: } else {
280: //use the user-provided dimensions
281: dialogSize.setSize(width, height);
282: }
283:
284: setSize(dialogSize);
285: }
286:
287: /**
288: * A method to check if they given message type is a know message
289: * type.
290: *
291: * @param messageType The message type to check
292: * @return {@code true} if the message type is known, {@code false} otherwise
293: */
294: private boolean _isknownMessageType(int messageType) {
295: return messageType == JOptionPane.ERROR_MESSAGE
296: || messageType == JOptionPane.INFORMATION_MESSAGE
297: || messageType == JOptionPane.WARNING_MESSAGE
298: || messageType == JOptionPane.QUESTION_MESSAGE
299: || messageType == JOptionPane.PLAIN_MESSAGE;
300: }
301:
302: /**
303: * Lookup the icon associated with the given messageType. The message
304: * type must be one of the message types from
305: * {@link javax.swing.JOptionPane}.
306: *
307: * @param messageType The message for which the icon is requested.
308: * @return The message's icon or {@code null} is no icon was found.
309: */
310: private Icon _getIcon(int messageType) {
311: assert _isknownMessageType(messageType);
312:
313: /* The OptionPane.xxxIcon constants were taken from
314: * javax.swing.plaf.basic.BasicOptionPaneUI, which may changed
315: * without notice.
316: */
317: if (messageType == JOptionPane.ERROR_MESSAGE) {
318: return UIManager.getIcon("OptionPane.errorIcon");
319: } else if (messageType == JOptionPane.INFORMATION_MESSAGE) {
320: return UIManager.getIcon("OptionPane.informationIcon");
321: } else if (messageType == JOptionPane.WARNING_MESSAGE) {
322: return UIManager.getIcon("OptionPane.warningIcon");
323: } else if (messageType == JOptionPane.QUESTION_MESSAGE) {
324: return UIManager.getIcon("OptionPane.questionIcon");
325: } else if (messageType == JOptionPane.PLAIN_MESSAGE) {
326: return null;
327: } else {
328: //should never get here
329: assert false;
330: }
331:
332: return null;
333: }
334:
335: /**
336: * Adds buttons to the bottom of the dialog. By default, a single
337: * "OK" button is added that calls {@link #closeDialog}. It
338: * is also set as the dialog's default button.
339: *
340: * Inheritors should feel free the change settings of the panel such
341: * as the layout manager. However, no guarantees are made that every
342: * change will work with every version of this class.
343: *
344: * @param buttonPanel The JPanel that should contain the buttons.
345: */
346: protected void _addButtons(JPanel buttonPanel) {
347: final JButton okButton = new JButton("OK");
348: okButton.addActionListener(new ActionListener() {
349: public void actionPerformed(ActionEvent notUsed) {
350: closeDialog();
351: }
352: });
353:
354: buttonPanel.add(okButton);
355: getRootPane().setDefaultButton(okButton);
356: }
357:
358: /**
359: * Shows the dialog.
360: */
361: public void showDialog() {
362: pack();
363: setVisible(true);
364: }
365:
366: /**
367: * Should be called when the dialog should be closed. The default
368: * implementation simply hides the dialog.
369: */
370: protected void closeDialog() {
371: setVisible(false);
372: }
373:
374: /**
375: * A simple main method for testing purposes.
376: *
377: * @param args Not used.
378: */
379: public static void main(String args[]) {
380: final Collection<String> data = new java.util.ArrayList<String>();
381: data.add("how");
382: data.add("now");
383: data.add("brown");
384: data.add("cow");
385:
386: SwingUtilities.invokeLater(new Runnable() {
387: public void run() {
388: ScrollableListDialog ld = new ScrollableListDialog(
389: null, "TITLE", "LEADER", data,
390: JOptionPane.ERROR_MESSAGE);
391: ld.pack();
392: ld.setVisible(true);
393: }
394: });
395: }
396: }
|