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: import javax.swing.table.AbstractTableModel;
045:
046: /**
047: * <p>The ScrollableListSelectionDialog is a popup dialog with a message
048: * and a scrollable list of items. Each item may be either selected or
049: * unselected. A ScrollableListSelectionDialog should be used when
050: * an operation needs to act on a variable number of items, for
051: * example, when saving modified files.</p>
052: *
053: * <p>The message (also know as the leader text) is displayed above the
054: * items with an optional icon. The items are displayed in a scrollable
055: * table. A column of checkboxes allows selection of the items. Buttons
056: * are added below the list of items.</p>
057: *
058: * <p>This dialog is somewhat styled after
059: * {@link javax.swing.JOptionPane} and uses the message-type constants
060: * from JOptionPane.</p>
061: *
062: * @author Chris Warrington
063: * @version $Id$
064: * @since 2007-04-08
065: */
066: public class ScrollableListSelectionDialog extends JDialog {
067: /**
068: * A enumeration of the various selection states.
069: */
070: public enum SelectionState {
071: /** Indicates that an item is selected. */
072: SELECTED,
073: /** Indicates that an item is not selected. */
074: UNSELECTED
075: };
076:
077: /** The default width for this dialog. */
078: private static final int DEFAULT_WIDTH = 400;
079: /** The default height for this dialog. */
080: private static final int DEFAULT_HEIGHT = 450;
081:
082: /** The ratio of the screen width to use by default. */
083: private static final double WIDTH_RATIO = .75;
084: /** The ratio of the screen height to use by default. */
085: private static final double HEIGHT_RATIO = .50;
086:
087: /** The table displaying the items. */
088: protected final JTable table;
089: /** The AbstractTableModel backing the table. */
090: protected final AbstractTableModel tableModel;
091:
092: /** The number of columns in the table. */
093: private static final int NUM_COLUMNS = 2;
094: /** The column index of the checkboxes column. */
095: private static final int CHECKBOXES_COLUMN_INDEX = 0;
096: /** The column index of the strings column. */
097: private static final int STRINGS_COLUMN_INDEX = 1;
098:
099: /** The items in the table. */
100: protected final Vector<String> dataAsStrings;
101: /** The selected items in the table. This Vector maps to
102: * _dataAsStrings by index. This value may be accessed by multiple
103: * threads. Threads wishing to access it should acquire its
104: * intrinsic lock. */
105: protected final Vector<Boolean> selectedItems;
106:
107: /**
108: * <p>Creates a new ScrollableListSelectionDialog with the given
109: * title, leader text, and items. The list of items is used to
110: * construct an internal string list that is not backed by the original
111: * list. Changes made to the list or items after dialog construction
112: * will not be reflected in the dialog.</p>
113: *
114: * <p>The default sizing, message type, and icon are used. All the
115: * items are selected by default.</p>
116: *
117: * @param owner The frame that owns this dialog. May be {@code null}.
118: * @param dialogTitle The text to use as the dialog title.
119: * @param leaderText Text to display before the list of items.
120: * @param listItems The items to display in the list.
121: * @param itemDescription A textual description of the items. This is used as the column heading for the items.
122: *
123: * @throws IllegalArgumentException if {@code listItems} is {@code null.}
124: */
125: public ScrollableListSelectionDialog(final Frame owner,
126: final String dialogTitle, final String leaderText,
127: final Collection<?> listItems, final String itemDescription) {
128: this (owner, dialogTitle, leaderText, listItems,
129: itemDescription, SelectionState.SELECTED,
130: JOptionPane.PLAIN_MESSAGE);
131: }
132:
133: /**
134: * <p>Creates a new ScrollableListSelectionDialog with the given
135: * title, leader text, items, and message type. The list of items is
136: * used to construct an internal string list that is not backed by the
137: * original list. Changes made to the list or items after dialog
138: * construction will not be reflected in the dialog.</p>
139: *
140: * <p>The message type must be one of the message types from
141: * {@link javax.swing.JOptionPane}. The message type controlls which
142: * default icon is used.</p>
143: *
144: * <p>The default sizing and icon are used.</p>
145: *
146: * @param owner The frame that owns this dialog. May be {@code null}.
147: * @param dialogTitle The text to use as the dialog title.
148: * @param leaderText Text to display before the list of items.
149: * @param listItems The items to display in the list.
150: * @param itemDescription A textual description of the items. This is used as the column heading for the items.
151: * @param defaultSelection The default selection state (selected or unselected) for the items.
152: * @param messageType The type of dialog message.
153: *
154: * @throws IllegalArgumentException if {@code listItems} is {@code null.}
155: * @throws IllegalArgumentException if the message type is unknown or {@code listItems} is {@code null.}
156: */
157: public ScrollableListSelectionDialog(final Frame owner,
158: final String dialogTitle, final String leaderText,
159: final Collection<?> listItems,
160: final String itemDescription,
161: final SelectionState defaultSelection, final int messageType) {
162: this (owner, dialogTitle, leaderText, listItems,
163: itemDescription, defaultSelection, messageType,
164: DEFAULT_WIDTH, DEFAULT_HEIGHT, null, true);
165: }
166:
167: /**
168: * <p>Creates a new ScrollableListSelectionDialog with the given
169: * title, leader text, items, message type, width, height, and icon.
170: * The list of items is used to construct an internal string list that
171: * is not backed by the original list. Changes made to the list or
172: * items after dialog construction will not be reflected in the
173: * dialog.</p>
174: *
175: * <p>The message type must be one of the message types from
176: * {@link javax.swing.JOptionPane}. The message type controlls which
177: * default icon is used. If {@code icon} is non-null, it is used
178: * instead of the default icon.</p>
179: *
180: * @param owner The frame that owns this dialog. May be {@code null}.
181: * @param dialogTitle The text to use as the dialog title.
182: * @param leaderText Text to display before the list of items.
183: * @param listItems The items to display in the list.
184: * @param itemDescription A textual description of the items. This is used as the column heading for the items.
185: * @param defaultSelection The default selection state (selected or unselected) for the items.
186: * @param messageType The type of dialog message.
187: * @param width The width of the dialog box.
188: * @param height The height of the dialog box.
189: * @param icon The icon to display. May be {@code null}.
190: *
191: * @throws IllegalArgumentException if {@code listItems} is {@code null.}
192: * @throws IllegalArgumentException if the message type is unknown or {@code listItems} is {@code null.}
193: */
194: public ScrollableListSelectionDialog(final Frame owner,
195: final String dialogTitle, final String leaderText,
196: final Collection<?> listItems,
197: final String itemDescription,
198: final SelectionState defaultSelection,
199: final int messageType, final int width, final int height,
200: final Icon icon) {
201: this (owner, dialogTitle, leaderText, listItems,
202: itemDescription, defaultSelection, messageType, width,
203: height, icon, false);
204: }
205:
206: /**
207: * <p>Creates a new ScrollableListSelectionDialog with the given
208: * title, leader text, items, message type, width, height, and icon.
209: * The list of items is used to construct an internal string list that
210: * is not backed by the original list. Changes made to the list or
211: * items after dialog construction will not be reflected in the
212: * dialog.</p>
213: *
214: * <p>The message type must be one of the message types from
215: * {@link javax.swing.JOptionPane}. The message type controlls which
216: * default icon is used. If {@code icon} is non-null, it is used
217: * instead of the default icon.</p>
218: *
219: * @param owner The frame that owns this dialog. May be {@code null}.
220: * @param dialogTitle The text to use as the dialog title.
221: * @param leaderText Text to display before the list of items.
222: * @param listItems The items to display in the list.
223: * @param itemDescription A textual description of the items. This is used as the column heading for the items.
224: * @param defaultSelection The default selection state (selected or unselected) for the items.
225: * @param messageType The type of dialog message.
226: * @param width The width of the dialog box.
227: * @param height The height of the dialog box.
228: * @param icon The icon to display. May be {@code null}.
229: * @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.
230: *
231: * @throws IllegalArgumentException if {@code listItems} is {@code null.}
232: * @throws IllegalArgumentException if the message type is unknown or {@code listItems} is {@code null.}
233: */
234: private ScrollableListSelectionDialog(final Frame owner,
235: final String dialogTitle, final String leaderText,
236: final Collection<?> listItems,
237: final String itemDescription,
238: final SelectionState defaultSelection,
239: final int messageType, final int width, final int height,
240: final Icon icon, final boolean fitToScreen) {
241: super (owner, dialogTitle, true);
242:
243: if (!_isknownMessageType(messageType)) {
244: throw new IllegalArgumentException("The message type \""
245: + messageType + "\" is unknown");
246: }
247:
248: if (listItems == null) {
249: throw new IllegalArgumentException(
250: "listItems cannot be null");
251: }
252:
253: /* create the leader text panel */
254: JLabel dialogIconLabel = null;
255: if (icon != null) {
256: //use the user-provided icon
257: dialogIconLabel = new JLabel(icon);
258: } else {
259: //lookup the message-dependent icon
260: Icon messageIcon = _getIcon(messageType);
261: if (messageIcon != null) {
262: dialogIconLabel = new JLabel(messageIcon);
263: }
264: }
265:
266: final JPanel leaderPanel = new JPanel();
267: final JLabel leaderLabel = new JLabel(leaderText);
268: leaderPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
269: if (dialogIconLabel != null) {
270: leaderPanel.add(dialogIconLabel);
271: }
272: leaderPanel.add(leaderLabel);
273:
274: /* create the table */
275: //copy the items string representations into a vector
276: dataAsStrings = new Vector<String>(listItems.size());
277: for (Object obj : listItems) {
278: if (obj != null) {
279: final String objAsString = obj.toString();
280: dataAsStrings.add(objAsString);
281: }
282: }
283: dataAsStrings.trimToSize();
284:
285: final int numItems = dataAsStrings.size();
286:
287: selectedItems = new Vector<Boolean>(numItems);
288: synchronized (selectedItems) {
289: for (int i = 0; i < numItems; ++i) {
290: selectedItems.add(i,
291: defaultSelection == SelectionState.SELECTED);
292: }
293: selectedItems.trimToSize();
294: }
295: assert selectedItems.size() == dataAsStrings.size();
296:
297: tableModel = new AbstractTableModel() {
298: //@Override - uncomment when we start compiling with Java 6
299: public int getRowCount() {
300: return numItems;
301: }
302:
303: //@Override - uncomment when we start compiling with Java 6
304: public int getColumnCount() {
305: return NUM_COLUMNS;
306: }
307:
308: //@Override - uncomment when we start compiling with Java 6
309: public Object getValueAt(int row, int column) {
310: if (column == CHECKBOXES_COLUMN_INDEX) {
311: assert row >= 0;
312: assert row < numItems;
313: synchronized (selectedItems) {
314: return selectedItems.get(row);
315: }
316: } else if (column == STRINGS_COLUMN_INDEX) {
317: assert row >= 0;
318: assert row < numItems;
319: return dataAsStrings.get(row);
320: } else {
321: assert false;
322: return null;
323: }
324: }
325:
326: @Override
327: public String getColumnName(int column) {
328: if (column == CHECKBOXES_COLUMN_INDEX) {
329: return "";
330: } else if (column == STRINGS_COLUMN_INDEX) {
331: return itemDescription;
332: } else {
333: assert false;
334: return "";
335: }
336: }
337:
338: @Override
339: public Class<?> getColumnClass(final int columnIndex) {
340: if (columnIndex == CHECKBOXES_COLUMN_INDEX) {
341: return Boolean.class;
342: } else if (columnIndex == STRINGS_COLUMN_INDEX) {
343: return String.class;
344: } else {
345: assert false;
346: return Object.class;
347: }
348: }
349:
350: @Override
351: public boolean isCellEditable(final int rowIndex,
352: final int columnIndex) {
353: return columnIndex == CHECKBOXES_COLUMN_INDEX; //only checkboxes are editable
354: }
355:
356: @Override
357: public void setValueAt(final Object newValue,
358: final int rowIndex, final int columnIndex) {
359: assert columnIndex == CHECKBOXES_COLUMN_INDEX;
360: assert rowIndex >= 0;
361: assert rowIndex < numItems;
362: assert newValue instanceof Boolean;
363:
364: final Boolean booleanValue = (Boolean) newValue;
365:
366: synchronized (selectedItems) {
367: selectedItems.set(rowIndex, booleanValue);
368: }
369: }
370: };
371:
372: table = new JTable(tableModel);
373:
374: /*
375: * this listener enabled clicking in the string column to update the
376: * checkbox.
377: */
378: table.addMouseListener(new MouseAdapter() {
379: @Override
380: public void mouseClicked(final MouseEvent e) {
381: final Point clickPoint = e.getPoint();
382: // which column was clicked on
383: final int clickColumn = table.columnAtPoint(clickPoint);
384:
385: if (clickColumn == STRINGS_COLUMN_INDEX) {
386: //it was the strings column, so update the check status of the row
387: //Swing does not do this automatically
388: final int clickRow = table.rowAtPoint(clickPoint);
389:
390: if (clickRow >= 0 && clickRow < numItems) {
391: synchronized (selectedItems) {
392: final boolean currentValue = selectedItems
393: .get(clickRow);
394: final boolean newValue = !currentValue;
395:
396: selectedItems.set(clickRow, newValue);
397: /* We are deliberately holding on to the lock while the
398: * listeners are notified. This, in theory, speeds up the
399: * listeners because they don't have to re-acquire the
400: * lock. Because the internals of Swing are unknown, the
401: * lock may need to be released before the listeners are
402: * notified. Only time will tell.
403: *
404: * PS: If it turns out that holding the lock during
405: * the listener updates is a problem, modify this comment
406: * accordingly. Thank you.
407: */
408: tableModel.fireTableCellUpdated(clickRow,
409: CHECKBOXES_COLUMN_INDEX);
410: }
411: }
412: }
413: }
414: });
415:
416: //set the column sizes
417: table.getColumnModel().getColumn(CHECKBOXES_COLUMN_INDEX)
418: .setMinWidth(15);
419: table.getColumnModel().getColumn(CHECKBOXES_COLUMN_INDEX)
420: .setMaxWidth(30);
421: table.getColumnModel().getColumn(CHECKBOXES_COLUMN_INDEX)
422: .setPreferredWidth(20);
423: table.getColumnModel().getColumn(CHECKBOXES_COLUMN_INDEX)
424: .sizeWidthToFit();
425:
426: //create a scrollable view around the table
427: final JScrollPane scrollPane = new JScrollPane(table);
428:
429: /* create the select all/select none panel */
430: final JPanel selectButtonsPanel = new JPanel();
431: selectButtonsPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
432: _addSelectButtons(selectButtonsPanel);
433:
434: /* create the button panel */
435: final JPanel buttonPanel = new JPanel();
436: buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
437: //allow children to add additional buttons, if overridden
438: _addButtons(buttonPanel);
439:
440: /* create the center panel which contains the scroll pane and the
441: * select all/select none buttons */
442: final JPanel centerPanel = new JPanel();
443: centerPanel.setLayout(new BorderLayout());
444: centerPanel.add(selectButtonsPanel, BorderLayout.NORTH);
445: centerPanel.add(scrollPane, BorderLayout.CENTER);
446:
447: /* create the dialog */
448: final JPanel contentPanel = new JPanel();
449: contentPanel.setLayout(new BorderLayout(10, 5));
450: contentPanel.setBorder(BorderFactory.createEmptyBorder(5, 10,
451: 0, 10));
452:
453: contentPanel.add(leaderPanel, BorderLayout.NORTH);
454: contentPanel.add(centerPanel, BorderLayout.CENTER);
455: contentPanel.add(buttonPanel, BorderLayout.SOUTH);
456:
457: getContentPane().add(contentPanel);
458:
459: /* calculate the dialog's dimensions */
460: final Dimension dialogSize = new Dimension();
461:
462: if (fitToScreen) {
463: //use the screen dimensions to calculate the dialog's
464: final Dimension screenSize = Toolkit.getDefaultToolkit()
465: .getScreenSize();
466: int screenBasedWidth = (int) (WIDTH_RATIO * screenSize
467: .getWidth());
468: int screenBasedHeight = (int) (HEIGHT_RATIO * screenSize
469: .getHeight());
470:
471: dialogSize.setSize(Math
472: .max(DEFAULT_WIDTH, screenBasedWidth), Math.max(
473: DEFAULT_HEIGHT, screenBasedHeight));
474: } else {
475: //use the user-provided dimensions
476: dialogSize.setSize(width, height);
477: }
478:
479: setSize(dialogSize);
480: }
481:
482: /**
483: * A method to check if they given message type is a know message
484: * type.
485: *
486: * @param messageType The message type to check
487: * @return {@code true} if the message type is known, {@code false} otherwise
488: */
489: private boolean _isknownMessageType(final int messageType) {
490: return messageType == JOptionPane.ERROR_MESSAGE
491: || messageType == JOptionPane.INFORMATION_MESSAGE
492: || messageType == JOptionPane.WARNING_MESSAGE
493: || messageType == JOptionPane.QUESTION_MESSAGE
494: || messageType == JOptionPane.PLAIN_MESSAGE;
495: }
496:
497: /**
498: * Lookup the icon associated with the given messageType. The message
499: * type must be one of the message types from
500: * {@link javax.swing.JOptionPane}.
501: *
502: * @param messageType The message for which the icon is requested.
503: * @return The message's icon or {@code null} is no icon was found.
504: */
505: private Icon _getIcon(final int messageType) {
506: assert _isknownMessageType(messageType);
507:
508: /* The OptionPane.xxxIcon constants were taken from
509: * javax.swing.plaf.basic.BasicOptionPaneUI, which may changed
510: * without notice.
511: */
512: if (messageType == JOptionPane.ERROR_MESSAGE) {
513: return UIManager.getIcon("OptionPane.errorIcon");
514: } else if (messageType == JOptionPane.INFORMATION_MESSAGE) {
515: return UIManager.getIcon("OptionPane.informationIcon");
516: } else if (messageType == JOptionPane.WARNING_MESSAGE) {
517: return UIManager.getIcon("OptionPane.warningIcon");
518: } else if (messageType == JOptionPane.QUESTION_MESSAGE) {
519: return UIManager.getIcon("OptionPane.questionIcon");
520: } else if (messageType == JOptionPane.PLAIN_MESSAGE) {
521: return null;
522: } else {
523: //should never get here
524: assert false;
525: }
526:
527: return null;
528: }
529:
530: /**
531: * Adds the "Select All" and "Select None" buttons
532: * to the given panel.
533: *
534: * @param selectButtonsPanel The panel that should contain the buttons.
535: */
536: private void _addSelectButtons(final JPanel selectButtonsPanel) {
537: final JButton selectAllButton = new JButton("Select All");
538: selectAllButton.setMnemonic(KeyEvent.VK_A);
539: selectAllButton
540: .addActionListener(new SelectAllNoneActionListener(
541: SelectionState.SELECTED));
542: selectButtonsPanel.add(selectAllButton);
543:
544: final JButton selectNoneButton = new JButton("Select None");
545: selectNoneButton.setMnemonic(KeyEvent.VK_N);
546: selectNoneButton
547: .addActionListener(new SelectAllNoneActionListener(
548: SelectionState.UNSELECTED));
549: selectButtonsPanel.add(selectNoneButton);
550: }
551:
552: /**
553: * Adds buttons to the bottom of the dialog. By default, a single
554: * "OK" button is added that calls {@link #closeDialog}. It
555: * is also set as the dialog's default button.
556: *
557: * Inheritors should feel free the change settings of the panel such
558: * as the layout manager. However, no guarantees are made that every
559: * change will work with every version of this class.
560: *
561: * @param buttonPanel The JPanel that should contain the buttons.
562: */
563: protected void _addButtons(final JPanel buttonPanel) {
564: final JButton okButton = new JButton("OK");
565: okButton.addActionListener(new ActionListener() {
566: public void actionPerformed(ActionEvent notUsed) {
567: closeDialog();
568: }
569: });
570:
571: buttonPanel.add(okButton);
572: getRootPane().setDefaultButton(okButton);
573: }
574:
575: /**
576: * Shows the dialog.
577: */
578: public void showDialog() {
579: pack();
580: setVisible(true);
581: }
582:
583: /**
584: * Should be called when the dialog should be closed. The default implementation
585: * simply hides the dialog.
586: */
587: protected void closeDialog() {
588: setVisible(false);
589: }
590:
591: /**
592: * Returns the string representation of those items that are
593: * currently selected. The items will be in the same relative order
594: * as they were at construction time. The resultant collection may be
595: * empty. The resultant collection is unmodifiable. The resultant
596: * collection is simply a snapshot (i.e., It will not be updated as
597: * more items are selected.). This method may be called from
598: * non-event queue threads.
599: *
600: * @return The currently selected items.
601: */
602: public java.util.List<String> selectedItems() {
603: final java.util.List<String> results = new ArrayList<String>();
604:
605: synchronized (selectedItems) {
606: /* This entire loop is synchronized so that we get a consistent
607: * view of the selected items. It is also faster.
608: */
609: for (int i = 0; i < dataAsStrings.size(); ++i) {
610: if (selectedItems.get(i)) {
611: results.add(dataAsStrings.get(i));
612: }
613: }
614: }
615:
616: return Collections.unmodifiableList(results);
617: }
618:
619: /**
620: * An ActionListener that handles the "Select All" and
621: * "Select None" buttons. It will set the selection state
622: * of every item to the given selection state.
623: */
624: private class SelectAllNoneActionListener implements ActionListener {
625: /** The value that the selection state will be set to when this
626: * listener runs. */
627: private final boolean _setToValue;
628:
629: /**
630: * Creates a new SelectAllNoneActionListener that will set the state
631: * of every item to the given state.
632: *
633: * @param setToState The state to set all the items to.
634: */
635: public SelectAllNoneActionListener(SelectionState setToState) {
636: _setToValue = setToState == SelectionState.SELECTED;
637: }
638:
639: /**
640: * The code that runs in response to the button's action.
641: * This is the code that actually sets the selection state of the
642: * items.
643: *
644: * @param notUsed Not used.
645: */
646: public void actionPerformed(ActionEvent notUsed) {
647: /* See comment in the table's mouse listener for a discussion
648: * about the duration of the lock.
649: */
650: synchronized (selectedItems) {
651: for (int i = 0; i < selectedItems.size(); ++i) {
652: selectedItems.set(i, _setToValue);
653: }
654: tableModel.fireTableRowsUpdated(0, Math.max(0,
655: selectedItems.size() - 1));
656: }
657: }
658: }
659:
660: /**
661: * A simple main method for testing purposes.
662: *
663: * @param args Not used.
664: */
665: public static void main(String args[]) {
666: final Collection<String> data = new java.util.ArrayList<String>();
667: data.add("how");
668: data.add("now");
669: data.add("brown");
670: data.add("cow");
671:
672: SwingUtilities.invokeLater(new Runnable() {
673: public void run() {
674: ScrollableListSelectionDialog ld = new ScrollableListSelectionDialog(
675: null, "TITLE", "LEADER", data, "Words",
676: SelectionState.SELECTED,
677: JOptionPane.ERROR_MESSAGE) {
678: @Override
679: protected void closeDialog() {
680: super .closeDialog();
681: Collection<String> si = selectedItems();
682: for (String i : si) {
683: System.out.println(i);
684: }
685: }
686: };
687: ld.pack();
688: ld.setVisible(true);
689: }
690: });
691: }
692: }
|