Clover coverage report - DrJava Test Coverage (drjava-20120422-r5456)
Coverage timestamp: Sun Apr 22 2012 03:13:25 CDT
file stats: LOC: 680   Methods: 25
NCLOC: 334   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
ScrollableListSelectionDialog.java 45.6% 64.7% 44% 57.2%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.util.swing;
 38   
 39    import javax.swing.*;
 40    import java.awt.*;
 41    import java.awt.event.*;
 42    import java.util.*;
 43   
 44    import javax.swing.table.AbstractTableModel;
 45   
 46    /**
 47    * <p>The ScrollableListSelectionDialog is a popup dialog with a message
 48    * and a scrollable list of items. Each item may be either selected or
 49    * unselected. A ScrollableListSelectionDialog should be used when
 50    * an operation needs to act on a variable number of items, for
 51    * example, when saving modified files.</p>
 52    *
 53    * <p>The message (also know as the leader text) is displayed above the
 54    * items with an optional icon. The items are displayed in a scrollable
 55    * table. A column of checkboxes allows selection of the items. Buttons
 56    * are added below the list of items.</p>
 57    *
 58    * <p>This dialog is somewhat styled after
 59    * {@link javax.swing.JOptionPane} and uses the message-type constants
 60    * from JOptionPane.</p>
 61    *
 62    * @author Chris Warrington
 63    * @version $Id: ScrollableListSelectionDialog.java 5443 2011-08-17 04:58:50Z rcartwright $
 64    * @since 2007-04-08
 65    */
 66    public class ScrollableListSelectionDialog extends JDialog {
 67    /** A enumeration of the various selection states.
 68    */
 69    public enum SelectionState {
 70    /** Indicates that an item is selected. */
 71    SELECTED,
 72    /** Indicates that an item is not selected. */
 73    UNSELECTED
 74    };
 75   
 76    /** The default width for this dialog. */
 77    private static final int DEFAULT_WIDTH = 400;
 78    /** The default height for this dialog. */
 79    private static final int DEFAULT_HEIGHT = 450;
 80   
 81    /** The ratio of the screen width to use by default. */
 82    private static final double WIDTH_RATIO = .75;
 83    /** The ratio of the screen height to use by default. */
 84    private static final double HEIGHT_RATIO = .50;
 85   
 86    /** The table displaying the items. */
 87    protected final JTable table;
 88    /** The AbstractTableModel backing the table. */
 89    protected final AbstractTableModel tableModel;
 90   
 91    /** The number of columns in the table. */
 92    private static final int NUM_COLUMNS = 2;
 93    /** The column index of the checkboxes column. */
 94    private static final int CHECKBOXES_COLUMN_INDEX = 0;
 95    /** The column index of the strings column. */
 96    private static final int STRINGS_COLUMN_INDEX = 1;
 97   
 98    /** The items in the table. */
 99    protected final Vector<String> dataAsStrings;
 100    /** The selected items in the table. This Vector maps to
 101    * _dataAsStrings by index. This value may be accessed by multiple
 102    * threads. Threads wishing to access it should acquire its
 103    * intrinsic lock. */
 104    protected final Vector<Boolean> selectedItems;
 105   
 106    /** <p>Creates a new ScrollableListSelectionDialog with the given
 107    * title, leader text, and items. The list of items is used to
 108    * construct an internal string list that is not backed by the original
 109    * list. Changes made to the list or items after dialog construction
 110    * will not be reflected in the dialog.</p>
 111    *
 112    * <p>The default sizing, message type, and icon are used. All the
 113    * items are selected by default.</p>
 114    *
 115    * @param owner The frame that owns this dialog. May be {@code null}.
 116    * @param dialogTitle The text to use as the dialog title.
 117    * @param leaderText Text to display before the list of items.
 118    * @param listItems The items to display in the list.
 119    * @param itemDescription A textual description of the items. This is used as the column heading for the items.
 120    *
 121    * @throws IllegalArgumentException if {@code listItems} is {@code null.}
 122    */
 123  2 public ScrollableListSelectionDialog(final Frame owner,
 124    final String dialogTitle,
 125    final String leaderText,
 126    final Collection<?> listItems,
 127    final String itemDescription) {
 128  2 this(owner, dialogTitle, leaderText, listItems, itemDescription, SelectionState.SELECTED, JOptionPane.PLAIN_MESSAGE);
 129    }
 130   
 131    /** <p>Creates a new ScrollableListSelectionDialog with the given
 132    * title, leader text, items, and message type. The list of items is
 133    * used to construct an internal string list that is not backed by the
 134    * original list. Changes made to the list or items after dialog
 135    * construction will not be reflected in the dialog.</p>
 136    *
 137    * <p>The message type must be one of the message types from
 138    * {@link javax.swing.JOptionPane}. The message type controlls which
 139    * default icon is used.</p>
 140    *
 141    * <p>The default sizing and icon are used.</p>
 142    *
 143    * @param owner The frame that owns this dialog. May be {@code null}.
 144    * @param dialogTitle The text to use as the dialog title.
 145    * @param leaderText Text to display before the list of items.
 146    * @param listItems The items to display in the list.
 147    * @param itemDescription A textual description of the items. This is used as the column heading for the items.
 148    * @param defaultSelection The default selection state (selected or unselected) for the items.
 149    * @param messageType The type of dialog message.
 150    *
 151    * @throws IllegalArgumentException if {@code listItems} is {@code null.}
 152    * @throws IllegalArgumentException if the message type is unknown or {@code listItems} is {@code null.}
 153    */
 154  9 public ScrollableListSelectionDialog(final Frame owner,
 155    final String dialogTitle,
 156    final String leaderText,
 157    final Collection<?> listItems,
 158    final String itemDescription,
 159    final SelectionState defaultSelection,
 160    final int messageType) {
 161  9 this(owner,
 162    dialogTitle,
 163    leaderText,
 164    listItems,
 165    itemDescription,
 166    defaultSelection,
 167    messageType,
 168    DEFAULT_WIDTH,
 169    DEFAULT_HEIGHT,
 170    null,
 171    true);
 172    }
 173   
 174    /** <p>Creates a new ScrollableListSelectionDialog with the given
 175    * title, leader text, items, message type, width, height, and icon.
 176    * The list of items is used to construct an internal string list that
 177    * is not backed by the original list. Changes made to the list or
 178    * items after dialog construction will not be reflected in the
 179    * dialog.</p>
 180    *
 181    * <p>The message type must be one of the message types from
 182    * {@link javax.swing.JOptionPane}. The message type controlls which
 183    * default icon is used. If {@code icon} is non-null, it is used
 184    * instead of the default icon.</p>
 185    *
 186    * @param owner The frame that owns this dialog. May be {@code null}.
 187    * @param dialogTitle The text to use as the dialog title.
 188    * @param leaderText Text to display before the list of items.
 189    * @param listItems The items to display in the list.
 190    * @param itemDescription A textual description of the items. This is used as the column heading for the items.
 191    * @param defaultSelection The default selection state (selected or unselected) for the items.
 192    * @param messageType The type of dialog message.
 193    * @param width The width of the dialog box.
 194    * @param height The height of the dialog box.
 195    * @param icon The icon to display. May be {@code null}.
 196    *
 197    * @throws IllegalArgumentException if {@code listItems} is {@code null.}
 198    * @throws IllegalArgumentException if the message type is unknown or {@code listItems} is {@code null.}
 199    */
 200  0 public ScrollableListSelectionDialog(final Frame owner,
 201    final String dialogTitle,
 202    final String leaderText,
 203    final Collection<?> listItems,
 204    final String itemDescription,
 205    final SelectionState defaultSelection,
 206    final int messageType,
 207    final int width,
 208    final int height,
 209    final Icon icon) {
 210  0 this(owner,
 211    dialogTitle,
 212    leaderText,
 213    listItems,
 214    itemDescription,
 215    defaultSelection,
 216    messageType,
 217    width,
 218    height,
 219    icon,
 220    false);
 221    }
 222   
 223    /** <p>Creates a new ScrollableListSelectionDialog with the given
 224    * title, leader text, items, message type, width, height, and icon.
 225    * The list of items is used to construct an internal string list that
 226    * is not backed by the original list. Changes made to the list or
 227    * items after dialog construction will not be reflected in the
 228    * dialog.</p>
 229    *
 230    * <p>The message type must be one of the message types from
 231    * {@link javax.swing.JOptionPane}. The message type controlls which
 232    * default icon is used. If {@code icon} is non-null, it is used
 233    * instead of the default icon.</p>
 234    *
 235    * @param owner The frame that owns this dialog. May be {@code null}.
 236    * @param dialogTitle The text to use as the dialog title.
 237    * @param leaderText Text to display before the list of items.
 238    * @param listItems The items to display in the list.
 239    * @param itemDescription A textual description of the items. This is used as the column heading for the items.
 240    * @param defaultSelection The default selection state (selected or unselected) for the items.
 241    * @param messageType The type of dialog message.
 242    * @param width The width of the dialog box.
 243    * @param height The height of the dialog box.
 244    * @param icon The icon to display. May be {@code null}.
 245    * @param fitToScreen If {@code true}, the width and height of the dialog will be calculated using the screen
 246    * dimensions, {@link #WIDTH_RATIO}, and {@link #HEIGHT_RATIO}. If {@code false}, the provided width and
 247    * height will be used.
 248    * @throws IllegalArgumentException if {@code listItems} is {@code null.}
 249    * @throws IllegalArgumentException if the message type is unknown or {@code listItems} is {@code null.}
 250    */
 251  12 private ScrollableListSelectionDialog(final Frame owner,
 252    final String dialogTitle,
 253    final String leaderText,
 254    final Collection<?> listItems,
 255    final String itemDescription,
 256    final SelectionState defaultSelection,
 257    final int messageType,
 258    final int width,
 259    final int height,
 260    final Icon icon,
 261    final boolean fitToScreen) {
 262  12 super(owner, dialogTitle, true);
 263   
 264  12 if (!_isknownMessageType(messageType)) {
 265  2 throw new IllegalArgumentException("The message type \"" + messageType + "\" is unknown");
 266    }
 267   
 268  10 if (listItems == null) {
 269  1 throw new IllegalArgumentException("listItems cannot be null");
 270    }
 271   
 272    /* create the leader text panel */
 273  9 JLabel dialogIconLabel = null;
 274  9 if (icon != null) {
 275    //use the user-provided icon
 276  0 dialogIconLabel = new JLabel(icon);
 277    } else {
 278    //lookup the message-dependent icon
 279  9 Icon messageIcon = _getIcon(messageType);
 280  9 if (messageIcon != null) {
 281  4 dialogIconLabel = new JLabel(messageIcon);
 282    }
 283    }
 284   
 285  9 final JPanel leaderPanel = new JPanel();
 286  9 final JLabel leaderLabel = new JLabel(leaderText);
 287  9 leaderPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
 288  9 if (dialogIconLabel != null) {
 289  4 leaderPanel.add(dialogIconLabel);
 290    }
 291  9 leaderPanel.add(leaderLabel);
 292   
 293    /* create the table */
 294    //copy the items string representations into a vector
 295  9 dataAsStrings = new Vector<String>(listItems.size());
 296  9 for (Object obj : listItems) {
 297  18 if (obj != null) {
 298  18 final String objAsString = obj.toString();
 299  18 dataAsStrings.add(objAsString);
 300    }
 301    }
 302  9 dataAsStrings.trimToSize();
 303   
 304  9 final int numItems = dataAsStrings.size();
 305   
 306  9 selectedItems = new Vector<Boolean>(numItems);
 307  9 synchronized(selectedItems) {
 308  9 for (int i = 0; i < numItems; ++i) {
 309  18 selectedItems.add(i, defaultSelection == SelectionState.SELECTED);
 310    }
 311  9 selectedItems.trimToSize();
 312    }
 313  9 assert selectedItems.size() == dataAsStrings.size();
 314   
 315  9 tableModel = new AbstractTableModel() {
 316    //@Override - uncomment when we start compiling with Java 6
 317  0 public int getRowCount() {
 318  0 return numItems;
 319    }
 320   
 321    //@Override - uncomment when we start compiling with Java 6
 322  27 public int getColumnCount() {
 323  27 return NUM_COLUMNS;
 324    }
 325   
 326    //@Override - uncomment when we start compiling with Java 6
 327  0 public Object getValueAt(int row, int column) {
 328  0 if (column == CHECKBOXES_COLUMN_INDEX) {
 329  0 assert row >= 0;
 330  0 assert row < numItems;
 331  0 synchronized(selectedItems) { return selectedItems.get(row); }
 332  0 } else if (column == STRINGS_COLUMN_INDEX) {
 333  0 assert row >= 0;
 334  0 assert row < numItems;
 335  0 return dataAsStrings.get(row);
 336    } else {
 337    assert false;
 338  0 return null;
 339    }
 340    }
 341   
 342  18 @Override
 343    public String getColumnName(int column) {
 344  18 if (column == CHECKBOXES_COLUMN_INDEX) {
 345  9 return "";
 346  9 } else if (column == STRINGS_COLUMN_INDEX) {
 347  9 return itemDescription;
 348    } else {
 349    assert false;
 350  0 return "";
 351    }
 352    }
 353   
 354  0 @Override
 355    public Class<?> getColumnClass(final int columnIndex) {
 356  0 if (columnIndex == CHECKBOXES_COLUMN_INDEX) {
 357  0 return Boolean.class;
 358  0 } else if (columnIndex == STRINGS_COLUMN_INDEX) {
 359  0 return String.class;
 360    } else {
 361    assert false;
 362  0 return Object.class;
 363    }
 364    }
 365   
 366  0 @Override
 367    public boolean isCellEditable(final int rowIndex, final int columnIndex) {
 368  0 return columnIndex == CHECKBOXES_COLUMN_INDEX; //only checkboxes are editable
 369    }
 370   
 371  0 @Override
 372    public void setValueAt(final Object newValue, final int rowIndex, final int columnIndex) {
 373  0 assert columnIndex == CHECKBOXES_COLUMN_INDEX;
 374  0 assert rowIndex >= 0;
 375  0 assert rowIndex < numItems;
 376  0 assert newValue instanceof Boolean;
 377   
 378  0 final Boolean booleanValue = (Boolean)newValue;
 379   
 380  0 synchronized(selectedItems) { selectedItems.set(rowIndex, booleanValue); }
 381    }
 382    };
 383   
 384  9 table = new JTable(tableModel);
 385   
 386    /*
 387    * this listener enabled clicking in the string column to update the
 388    * checkbox.
 389    */
 390  9 table.addMouseListener(new MouseAdapter() {
 391  0 @Override
 392    public void mouseClicked(final MouseEvent e) {
 393  0 final Point clickPoint = e.getPoint();
 394    // which column was clicked on
 395  0 final int clickColumn = table.columnAtPoint(clickPoint);
 396   
 397  0 if (clickColumn == STRINGS_COLUMN_INDEX) {
 398    //it was the strings column, so update the check status of the row
 399    //Swing does not do this automatically
 400  0 final int clickRow = table.rowAtPoint(clickPoint);
 401   
 402  0 if (clickRow >= 0 && clickRow < numItems) {
 403  0 synchronized(selectedItems) {
 404  0 final boolean currentValue = selectedItems.get(clickRow);
 405  0 final boolean newValue = !currentValue;
 406   
 407  0 selectedItems.set(clickRow, newValue);
 408    /* We are deliberately holding on to the lock while the
 409    * listeners are notified. This, in theory, speeds up the
 410    * listeners because they don't have to re-acquire the
 411    * lock. Because the internals of Swing are unknown, the
 412    * lock may need to be released before the listeners are
 413    * notified. Only time will tell.
 414    *
 415    * PS: If it turns out that holding the lock during
 416    * the listener updates is a problem, modify this comment
 417    * accordingly. Thank you.
 418    */
 419  0 tableModel.fireTableCellUpdated(clickRow, CHECKBOXES_COLUMN_INDEX);
 420    }
 421    }
 422    }
 423    }
 424    });
 425   
 426    //set the column sizes
 427  9 table.getColumnModel().getColumn(CHECKBOXES_COLUMN_INDEX).setMinWidth(15);
 428  9 table.getColumnModel().getColumn(CHECKBOXES_COLUMN_INDEX).setMaxWidth(30);
 429  9 table.getColumnModel().getColumn(CHECKBOXES_COLUMN_INDEX).setPreferredWidth(20);
 430  9 table.getColumnModel().getColumn(CHECKBOXES_COLUMN_INDEX).sizeWidthToFit();
 431   
 432    //create a scrollable view around the table
 433  9 final JScrollPane scrollPane = new JScrollPane(table);
 434   
 435    /* create the select all/select none panel */
 436  9 final JPanel selectButtonsPanel = new JPanel();
 437  9 selectButtonsPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
 438  9 _addSelectButtons(selectButtonsPanel);
 439   
 440    /* create the button panel */
 441  9 final JPanel buttonPanel = new JPanel();
 442  9 buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
 443    //allow children to add additional buttons, if overridden
 444  9 _addButtons(buttonPanel);
 445   
 446    /* create the center panel which contains the scroll pane and the
 447    * select all/select none buttons */
 448  9 final JPanel centerPanel = new JPanel();
 449  9 centerPanel.setLayout(new BorderLayout());
 450  9 centerPanel.add(selectButtonsPanel, BorderLayout.NORTH);
 451  9 centerPanel.add(scrollPane, BorderLayout.CENTER);
 452   
 453    /* create the dialog */
 454  9 final JPanel contentPanel = new JPanel();
 455  9 contentPanel.setLayout(new BorderLayout(10, 5));
 456  9 contentPanel.setBorder(BorderFactory.createEmptyBorder(5, 10, 0, 10));
 457   
 458  9 contentPanel.add(leaderPanel, BorderLayout.NORTH);
 459  9 contentPanel.add(centerPanel, BorderLayout.CENTER);
 460  9 contentPanel.add(buttonPanel, BorderLayout.SOUTH);
 461   
 462  9 getContentPane().add(contentPanel);
 463   
 464    /* calculate the dialog's dimensions */
 465  9 final Dimension dialogSize = new Dimension();
 466   
 467  9 if (fitToScreen) {
 468    //use the screen dimensions to calculate the dialog's
 469  9 final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
 470  9 int screenBasedWidth = (int) (WIDTH_RATIO * screenSize.getWidth());
 471  9 int screenBasedHeight = (int) (HEIGHT_RATIO * screenSize.getHeight());
 472   
 473  9 dialogSize.setSize(Math.max(DEFAULT_WIDTH, screenBasedWidth),
 474    Math.max(DEFAULT_HEIGHT, screenBasedHeight));
 475    } else {
 476    //use the user-provided dimensions
 477  0 dialogSize.setSize(width, height);
 478    }
 479   
 480  9 setSize(dialogSize);
 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  21 private boolean _isknownMessageType(final int messageType) {
 490  21 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    /** Lookup the icon associated with the given messageType. The message
 498    * type must be one of the message types from
 499    * {@link javax.swing.JOptionPane}.
 500    *
 501    * @param messageType The message for which the icon is requested.
 502    * @return The message's icon or {@code null} is no icon was found.
 503    */
 504  9 private Icon _getIcon(final int messageType) {
 505  9 assert _isknownMessageType(messageType);
 506   
 507    /* The OptionPane.xxxIcon constants were taken from
 508    * javax.swing.plaf.basic.BasicOptionPaneUI, which may changed
 509    * without notice.
 510    */
 511  9 if (messageType == JOptionPane.ERROR_MESSAGE) {
 512  1 return UIManager.getIcon("OptionPane.errorIcon");
 513  8 } else if (messageType == JOptionPane.INFORMATION_MESSAGE) {
 514  1 return UIManager.getIcon("OptionPane.informationIcon");
 515  7 } else if (messageType == JOptionPane.WARNING_MESSAGE) {
 516  1 return UIManager.getIcon("OptionPane.warningIcon");
 517  6 } else if (messageType == JOptionPane.QUESTION_MESSAGE) {
 518  1 return UIManager.getIcon("OptionPane.questionIcon");
 519  5 } else if (messageType == JOptionPane.PLAIN_MESSAGE) {
 520  5 return null;
 521    } else {
 522    //should never get here
 523    assert false;
 524    }
 525   
 526  0 return null;
 527    }
 528   
 529    /** Adds the &quot;Select All&quot; and &quot;Select None&quot; buttons
 530    * to the given panel.
 531    *
 532    * @param selectButtonsPanel The panel that should contain the buttons.
 533    */
 534  9 private void _addSelectButtons(final JPanel selectButtonsPanel) {
 535  9 final JButton selectAllButton = new JButton("Select All");
 536  9 edu.rice.cs.drjava.platform.PlatformFactory.ONLY.setMnemonic(selectAllButton,KeyEvent.VK_A);
 537  9 selectAllButton.addActionListener(new SelectAllNoneActionListener(SelectionState.SELECTED));
 538  9 selectButtonsPanel.add(selectAllButton);
 539   
 540  9 final JButton selectNoneButton = new JButton("Select None");
 541  9 edu.rice.cs.drjava.platform.PlatformFactory.ONLY.setMnemonic(selectNoneButton,KeyEvent.VK_N);
 542  9 selectNoneButton.addActionListener(new SelectAllNoneActionListener(SelectionState.UNSELECTED));
 543  9 selectButtonsPanel.add(selectNoneButton);
 544    }
 545   
 546    /** Adds buttons to the bottom of the dialog. By default, a single
 547    * &quot;OK&quot; button is added that calls {@link #closeDialog}. It
 548    * is also set as the dialog's default button.
 549    *
 550    * Inheritors should feel free the change settings of the panel such
 551    * as the layout manager. However, no guarantees are made that every
 552    * change will work with every version of this class.
 553    *
 554    * @param buttonPanel The JPanel that should contain the buttons.
 555    */
 556  9 protected void _addButtons(final JPanel buttonPanel) {
 557  9 final JButton okButton = new JButton("OK");
 558  9 okButton.addActionListener(new ActionListener() {
 559  0 public void actionPerformed(ActionEvent notUsed) {
 560  0 closeDialog();
 561    }
 562    });
 563   
 564  9 buttonPanel.add(okButton);
 565  9 getRootPane().setDefaultButton(okButton);
 566    }
 567   
 568    /**
 569    * Shows the dialog.
 570    */
 571  0 public void showDialog() {
 572  0 pack();
 573  0 Utilities.setPopupLoc(this, getOwner());
 574  0 setVisible(true);
 575    }
 576   
 577    /** Should be called when the dialog should be closed. The default implementation
 578    * simply hides the dialog.
 579    */
 580  0 protected void closeDialog() {
 581  0 setVisible(false);
 582    }
 583   
 584    /** Returns the string representation of those items that are
 585    * currently selected. The items will be in the same relative order
 586    * as they were at construction time. The resultant collection may be
 587    * empty. The resultant collection is unmodifiable. The resultant
 588    * collection is simply a snapshot (i.e., It will not be updated as
 589    * more items are selected.). This method may be called from
 590    * non-event&nbsp;queue threads.
 591    *
 592    * @return The currently selected items.
 593    */
 594  3 public java.util.List<String> selectedItems() {
 595  3 final java.util.List<String> results = new ArrayList<String>();
 596   
 597  3 synchronized(selectedItems) {
 598    /* This entire loop is synchronized so that we get a consistent
 599    * view of the selected items. It is also faster.
 600    */
 601  3 for (int i = 0; i < dataAsStrings.size(); ++i) {
 602  6 if (selectedItems.get(i)) {
 603  4 results.add(dataAsStrings.get(i));
 604    }
 605    }
 606    }
 607   
 608  3 return Collections.unmodifiableList(results);
 609    }
 610   
 611    /** An ActionListener that handles the &quot;Select All&quot; and
 612    * &quot;Select None&quot; buttons. It will set the selection state
 613    * of every item to the given selection state.
 614    */
 615    private class SelectAllNoneActionListener implements ActionListener {
 616    /** The value that the selection state will be set to when this
 617    * listener runs. */
 618    private final boolean _setToValue;
 619   
 620    /**
 621    * Creates a new SelectAllNoneActionListener that will set the state
 622    * of every item to the given state.
 623    *
 624    * @param setToState The state to set all the items to.
 625    */
 626  18 public SelectAllNoneActionListener(SelectionState setToState) {
 627  18 _setToValue = setToState == SelectionState.SELECTED;
 628    }
 629   
 630    /**
 631    * The code that runs in response to the button's action.
 632    * This is the code that actually sets the selection state of the
 633    * items.
 634    *
 635    * @param notUsed Not used.
 636    */
 637  0 public void actionPerformed(ActionEvent notUsed) {
 638    /* See comment in the table's mouse listener for a discussion
 639    * about the duration of the lock.
 640    */
 641  0 synchronized(selectedItems) {
 642  0 for (int i = 0; i < selectedItems.size(); ++i) {
 643  0 selectedItems.set(i, _setToValue);
 644    }
 645  0 tableModel.fireTableRowsUpdated(0, Math.max(0, selectedItems.size() - 1));
 646    }
 647    }
 648    }
 649   
 650    /** A simple main method for testing purposes.
 651    *
 652    * @param args Not used.
 653    */
 654  0 public static void main(String args[]) {
 655  0 final Collection<String> data = new java.util.ArrayList<String>();
 656  0 data.add("how");
 657  0 data.add("now");
 658  0 data.add("brown");
 659  0 data.add("cow");
 660   
 661  0 EventQueue.invokeLater(new Runnable() {
 662  0 public void run() {
 663  0 ScrollableListSelectionDialog ld =
 664    new ScrollableListSelectionDialog(null, "TITLE", "LEADER", data, "Words", SelectionState.SELECTED,
 665    JOptionPane.ERROR_MESSAGE) {
 666  0 @Override
 667    protected void closeDialog() {
 668  0 super.closeDialog();
 669  0 Collection<String> si = selectedItems();
 670  0 for (String i : si) {
 671  0 System.out.println(i);
 672    }
 673    }
 674    };
 675  0 ld.pack();
 676  0 ld.setVisible(true);
 677    }
 678    });
 679    }
 680    }