|
|||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
ScrollableListSelectionDialog.java | 45.6% | 64.7% | 44% | 57.2% |
|
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 "Select All" and "Select None" 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 | * "OK" 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 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 "Select All" and | |
612 | * "Select None" 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 | } |
|