1 /* 2 * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing.plaf.metal; 27 28 import javax.swing.*; 29 import javax.swing.border.EmptyBorder; 30 import javax.swing.filechooser.*; 31 import javax.swing.event.*; 32 import javax.swing.plaf.*; 33 import javax.swing.plaf.basic.*; 34 import java.awt.*; 35 import java.awt.event.*; 36 import java.beans.*; 37 import java.io.File; 38 import java.io.FileNotFoundException; 39 import java.io.IOException; 40 import java.util.*; 41 import java.security.AccessController; 42 import java.security.PrivilegedAction; 43 import javax.accessibility.*; 44 45 import sun.awt.shell.ShellFolder; 46 import sun.swing.*; 47 48 /** 49 * Metal L&F implementation of a FileChooser. 50 * 51 * @author Jeff Dinkins 52 */ 53 public class MetalFileChooserUI extends BasicFileChooserUI { 54 55 // Much of the Metal UI for JFilechooser is just a copy of 56 // the windows implementation, but using Metal themed buttons, lists, 57 // icons, etc. We are planning a complete rewrite, and hence we've 58 // made most things in this class private. 59 60 private JLabel lookInLabel; 61 private JComboBox<Object> directoryComboBox; 62 private DirectoryComboBoxModel directoryComboBoxModel; 63 private Action directoryComboBoxAction = new DirectoryComboBoxAction(); 64 65 private FilterComboBoxModel filterComboBoxModel; 66 67 private JTextField fileNameTextField; 68 69 private FilePane filePane; 70 private JToggleButton listViewButton; 71 private JToggleButton detailsViewButton; 72 73 private JButton approveButton; 74 private JButton cancelButton; 75 76 private JPanel buttonPanel; 77 private JPanel bottomPanel; 78 79 private JComboBox<?> filterComboBox; 80 81 private static final Dimension hstrut5 = new Dimension(5, 1); 82 private static final Dimension hstrut11 = new Dimension(11, 1); 83 84 private static final Dimension vstrut5 = new Dimension(1, 5); 85 86 private static final Insets shrinkwrap = new Insets(0,0,0,0); 87 88 // Preferred and Minimum sizes for the dialog box 89 private static int PREF_WIDTH = 500; 90 private static int PREF_HEIGHT = 326; 91 private static Dimension PREF_SIZE = new Dimension(PREF_WIDTH, PREF_HEIGHT); 92 93 private static int MIN_WIDTH = 500; 94 private static int MIN_HEIGHT = 326; 95 private static int LIST_PREF_WIDTH = 405; 96 private static int LIST_PREF_HEIGHT = 135; 97 private static Dimension LIST_PREF_SIZE = new Dimension(LIST_PREF_WIDTH, LIST_PREF_HEIGHT); 98 99 // Labels, mnemonics, and tooltips (oh my!) 100 private int lookInLabelMnemonic = 0; 101 private String lookInLabelText = null; 102 private String saveInLabelText = null; 103 104 private int fileNameLabelMnemonic = 0; 105 private String fileNameLabelText = null; 106 private int folderNameLabelMnemonic = 0; 107 private String folderNameLabelText = null; 108 109 private int filesOfTypeLabelMnemonic = 0; 110 private String filesOfTypeLabelText = null; 111 112 private String upFolderToolTipText = null; 113 private String upFolderAccessibleName = null; 114 115 private String homeFolderToolTipText = null; 116 private String homeFolderAccessibleName = null; 117 118 private String newFolderToolTipText = null; 119 private String newFolderAccessibleName = null; 120 121 private String listViewButtonToolTipText = null; 122 private String listViewButtonAccessibleName = null; 123 124 private String detailsViewButtonToolTipText = null; 125 private String detailsViewButtonAccessibleName = null; 126 127 private AlignedLabel fileNameLabel; 128 populateFileNameLabel()129 private void populateFileNameLabel() { 130 if (getFileChooser().getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) { 131 fileNameLabel.setText(folderNameLabelText); 132 fileNameLabel.setDisplayedMnemonic(folderNameLabelMnemonic); 133 } else { 134 fileNameLabel.setText(fileNameLabelText); 135 fileNameLabel.setDisplayedMnemonic(fileNameLabelMnemonic); 136 } 137 } 138 139 /** 140 * Constructs a new instance of {@code MetalFileChooserUI}. 141 * 142 * @param c a component 143 * @return a new instance of {@code MetalFileChooserUI} 144 */ createUI(JComponent c)145 public static ComponentUI createUI(JComponent c) { 146 return new MetalFileChooserUI((JFileChooser) c); 147 } 148 149 /** 150 * Constructs a new instance of {@code MetalFileChooserUI}. 151 * 152 * @param filechooser a {@code JFileChooser} 153 */ MetalFileChooserUI(JFileChooser filechooser)154 public MetalFileChooserUI(JFileChooser filechooser) { 155 super(filechooser); 156 } 157 installUI(JComponent c)158 public void installUI(JComponent c) { 159 super.installUI(c); 160 } 161 uninstallComponents(JFileChooser fc)162 public void uninstallComponents(JFileChooser fc) { 163 fc.removeAll(); 164 bottomPanel = null; 165 buttonPanel = null; 166 } 167 168 private class MetalFileChooserUIAccessor implements FilePane.FileChooserUIAccessor { getFileChooser()169 public JFileChooser getFileChooser() { 170 return MetalFileChooserUI.this.getFileChooser(); 171 } 172 getModel()173 public BasicDirectoryModel getModel() { 174 return MetalFileChooserUI.this.getModel(); 175 } 176 createList()177 public JPanel createList() { 178 return MetalFileChooserUI.this.createList(getFileChooser()); 179 } 180 createDetailsView()181 public JPanel createDetailsView() { 182 return MetalFileChooserUI.this.createDetailsView(getFileChooser()); 183 } 184 isDirectorySelected()185 public boolean isDirectorySelected() { 186 return MetalFileChooserUI.this.isDirectorySelected(); 187 } 188 getDirectory()189 public File getDirectory() { 190 return MetalFileChooserUI.this.getDirectory(); 191 } 192 getChangeToParentDirectoryAction()193 public Action getChangeToParentDirectoryAction() { 194 return MetalFileChooserUI.this.getChangeToParentDirectoryAction(); 195 } 196 getApproveSelectionAction()197 public Action getApproveSelectionAction() { 198 return MetalFileChooserUI.this.getApproveSelectionAction(); 199 } 200 getNewFolderAction()201 public Action getNewFolderAction() { 202 return MetalFileChooserUI.this.getNewFolderAction(); 203 } 204 createDoubleClickListener(JList<?> list)205 public MouseListener createDoubleClickListener(JList<?> list) { 206 return MetalFileChooserUI.this.createDoubleClickListener(getFileChooser(), 207 list); 208 } 209 createListSelectionListener()210 public ListSelectionListener createListSelectionListener() { 211 return MetalFileChooserUI.this.createListSelectionListener(getFileChooser()); 212 } 213 } 214 installComponents(JFileChooser fc)215 public void installComponents(JFileChooser fc) { 216 FileSystemView fsv = fc.getFileSystemView(); 217 218 fc.setBorder(new EmptyBorder(12, 12, 11, 11)); 219 fc.setLayout(new BorderLayout(0, 11)); 220 221 filePane = new FilePane(new MetalFileChooserUIAccessor()); 222 fc.addPropertyChangeListener(filePane); 223 224 // ********************************* // 225 // **** Construct the top panel **** // 226 // ********************************* // 227 228 // Directory manipulation buttons 229 JPanel topPanel = new JPanel(new BorderLayout(11, 0)); 230 JPanel topButtonPanel = new JPanel(); 231 topButtonPanel.setLayout(new BoxLayout(topButtonPanel, BoxLayout.LINE_AXIS)); 232 topPanel.add(topButtonPanel, BorderLayout.AFTER_LINE_ENDS); 233 234 // Add the top panel to the fileChooser 235 fc.add(topPanel, BorderLayout.NORTH); 236 237 // ComboBox Label 238 lookInLabel = new JLabel(lookInLabelText); 239 lookInLabel.setDisplayedMnemonic(lookInLabelMnemonic); 240 topPanel.add(lookInLabel, BorderLayout.BEFORE_LINE_BEGINS); 241 242 // CurrentDir ComboBox 243 @SuppressWarnings("serial") // anonymous class 244 JComboBox<Object> tmp1 = new JComboBox<Object>() { 245 public Dimension getPreferredSize() { 246 Dimension d = super.getPreferredSize(); 247 // Must be small enough to not affect total width. 248 d.width = 150; 249 return d; 250 } 251 }; 252 directoryComboBox = tmp1; 253 directoryComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY, 254 lookInLabelText); 255 directoryComboBox.putClientProperty( "JComboBox.isTableCellEditor", Boolean.TRUE ); 256 lookInLabel.setLabelFor(directoryComboBox); 257 directoryComboBoxModel = createDirectoryComboBoxModel(fc); 258 directoryComboBox.setModel(directoryComboBoxModel); 259 directoryComboBox.addActionListener(directoryComboBoxAction); 260 directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc)); 261 directoryComboBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); 262 directoryComboBox.setAlignmentY(JComponent.TOP_ALIGNMENT); 263 directoryComboBox.setMaximumRowCount(8); 264 265 topPanel.add(directoryComboBox, BorderLayout.CENTER); 266 267 // Up Button 268 JButton upFolderButton = new JButton(getChangeToParentDirectoryAction()); 269 upFolderButton.setText(null); 270 upFolderButton.setIcon(upFolderIcon); 271 upFolderButton.setToolTipText(upFolderToolTipText); 272 upFolderButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 273 upFolderAccessibleName); 274 upFolderButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); 275 upFolderButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); 276 upFolderButton.setMargin(shrinkwrap); 277 278 topButtonPanel.add(upFolderButton); 279 topButtonPanel.add(Box.createRigidArea(hstrut5)); 280 281 // Home Button 282 File homeDir = fsv.getHomeDirectory(); 283 String toolTipText = homeFolderToolTipText; 284 285 286 JButton b = new JButton(homeFolderIcon); 287 b.setToolTipText(toolTipText); 288 b.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 289 homeFolderAccessibleName); 290 b.setAlignmentX(JComponent.LEFT_ALIGNMENT); 291 b.setAlignmentY(JComponent.CENTER_ALIGNMENT); 292 b.setMargin(shrinkwrap); 293 294 b.addActionListener(getGoHomeAction()); 295 topButtonPanel.add(b); 296 topButtonPanel.add(Box.createRigidArea(hstrut5)); 297 298 // New Directory Button 299 if (!UIManager.getBoolean("FileChooser.readOnly")) { 300 b = new JButton(filePane.getNewFolderAction()); 301 b.setText(null); 302 b.setIcon(newFolderIcon); 303 b.setToolTipText(newFolderToolTipText); 304 b.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 305 newFolderAccessibleName); 306 b.setAlignmentX(JComponent.LEFT_ALIGNMENT); 307 b.setAlignmentY(JComponent.CENTER_ALIGNMENT); 308 b.setMargin(shrinkwrap); 309 } 310 topButtonPanel.add(b); 311 topButtonPanel.add(Box.createRigidArea(hstrut5)); 312 313 // View button group 314 ButtonGroup viewButtonGroup = new ButtonGroup(); 315 316 // List Button 317 listViewButton = new JToggleButton(listViewIcon); 318 listViewButton.setToolTipText(listViewButtonToolTipText); 319 listViewButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 320 listViewButtonAccessibleName); 321 listViewButton.setSelected(true); 322 listViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); 323 listViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); 324 listViewButton.setMargin(shrinkwrap); 325 listViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_LIST)); 326 topButtonPanel.add(listViewButton); 327 viewButtonGroup.add(listViewButton); 328 329 // Details Button 330 detailsViewButton = new JToggleButton(detailsViewIcon); 331 detailsViewButton.setToolTipText(detailsViewButtonToolTipText); 332 detailsViewButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 333 detailsViewButtonAccessibleName); 334 detailsViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); 335 detailsViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); 336 detailsViewButton.setMargin(shrinkwrap); 337 detailsViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_DETAILS)); 338 topButtonPanel.add(detailsViewButton); 339 viewButtonGroup.add(detailsViewButton); 340 341 filePane.addPropertyChangeListener(new PropertyChangeListener() { 342 public void propertyChange(PropertyChangeEvent e) { 343 if ("viewType".equals(e.getPropertyName())) { 344 int viewType = filePane.getViewType(); 345 switch (viewType) { 346 case FilePane.VIEWTYPE_LIST: 347 listViewButton.setSelected(true); 348 break; 349 350 case FilePane.VIEWTYPE_DETAILS: 351 detailsViewButton.setSelected(true); 352 break; 353 } 354 } 355 } 356 }); 357 358 // ************************************** // 359 // ******* Add the directory pane ******* // 360 // ************************************** // 361 fc.add(getAccessoryPanel(), BorderLayout.AFTER_LINE_ENDS); 362 JComponent accessory = fc.getAccessory(); 363 if(accessory != null) { 364 getAccessoryPanel().add(accessory); 365 } 366 filePane.setPreferredSize(LIST_PREF_SIZE); 367 fc.add(filePane, BorderLayout.CENTER); 368 369 // ********************************** // 370 // **** Construct the bottom panel ** // 371 // ********************************** // 372 JPanel bottomPanel = getBottomPanel(); 373 bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS)); 374 fc.add(bottomPanel, BorderLayout.SOUTH); 375 376 // FileName label and textfield 377 JPanel fileNamePanel = new JPanel(); 378 fileNamePanel.setLayout(new BoxLayout(fileNamePanel, BoxLayout.LINE_AXIS)); 379 bottomPanel.add(fileNamePanel); 380 bottomPanel.add(Box.createRigidArea(vstrut5)); 381 382 fileNameLabel = new AlignedLabel(); 383 populateFileNameLabel(); 384 fileNamePanel.add(fileNameLabel); 385 386 @SuppressWarnings("serial") // anonymous class 387 JTextField tmp2 = new JTextField(35) { 388 public Dimension getMaximumSize() { 389 return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height); 390 } 391 }; 392 fileNameTextField = tmp2; 393 fileNamePanel.add(fileNameTextField); 394 fileNameLabel.setLabelFor(fileNameTextField); 395 fileNameTextField.addFocusListener( 396 new FocusAdapter() { 397 public void focusGained(FocusEvent e) { 398 if (!getFileChooser().isMultiSelectionEnabled()) { 399 filePane.clearSelection(); 400 } 401 } 402 } 403 ); 404 if (fc.isMultiSelectionEnabled()) { 405 setFileName(fileNameString(fc.getSelectedFiles())); 406 } else { 407 setFileName(fileNameString(fc.getSelectedFile())); 408 } 409 410 411 // Filetype label and combobox 412 JPanel filesOfTypePanel = new JPanel(); 413 filesOfTypePanel.setLayout(new BoxLayout(filesOfTypePanel, BoxLayout.LINE_AXIS)); 414 bottomPanel.add(filesOfTypePanel); 415 416 AlignedLabel filesOfTypeLabel = new AlignedLabel(filesOfTypeLabelText); 417 filesOfTypeLabel.setDisplayedMnemonic(filesOfTypeLabelMnemonic); 418 filesOfTypePanel.add(filesOfTypeLabel); 419 420 filterComboBoxModel = createFilterComboBoxModel(); 421 fc.addPropertyChangeListener(filterComboBoxModel); 422 filterComboBox = new JComboBox<>(filterComboBoxModel); 423 filterComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY, 424 filesOfTypeLabelText); 425 filesOfTypeLabel.setLabelFor(filterComboBox); 426 filterComboBox.setRenderer(createFilterComboBoxRenderer()); 427 filesOfTypePanel.add(filterComboBox); 428 429 // buttons 430 getButtonPanel().setLayout(new ButtonAreaLayout()); 431 432 approveButton = new JButton(getApproveButtonText(fc)); 433 // Note: Metal does not use mnemonics for approve and cancel 434 approveButton.addActionListener(getApproveSelectionAction()); 435 approveButton.setToolTipText(getApproveButtonToolTipText(fc)); 436 getButtonPanel().add(approveButton); 437 438 cancelButton = new JButton(cancelButtonText); 439 cancelButton.setToolTipText(cancelButtonToolTipText); 440 cancelButton.addActionListener(getCancelSelectionAction()); 441 getButtonPanel().add(cancelButton); 442 443 if(fc.getControlButtonsAreShown()) { 444 addControlButtons(); 445 } 446 447 groupLabels(new AlignedLabel[] { fileNameLabel, filesOfTypeLabel }); 448 } 449 450 /** 451 * Returns the button panel. 452 * 453 * @return the button panel 454 */ getButtonPanel()455 protected JPanel getButtonPanel() { 456 if (buttonPanel == null) { 457 buttonPanel = new JPanel(); 458 } 459 return buttonPanel; 460 } 461 462 /** 463 * Returns the bottom panel. 464 * 465 * @return the bottom panel 466 */ getBottomPanel()467 protected JPanel getBottomPanel() { 468 if(bottomPanel == null) { 469 bottomPanel = new JPanel(); 470 } 471 return bottomPanel; 472 } 473 installStrings(JFileChooser fc)474 protected void installStrings(JFileChooser fc) { 475 super.installStrings(fc); 476 477 Locale l = fc.getLocale(); 478 479 lookInLabelMnemonic = getMnemonic("FileChooser.lookInLabelMnemonic", l); 480 lookInLabelText = UIManager.getString("FileChooser.lookInLabelText",l); 481 saveInLabelText = UIManager.getString("FileChooser.saveInLabelText",l); 482 483 fileNameLabelMnemonic = getMnemonic("FileChooser.fileNameLabelMnemonic", l); 484 fileNameLabelText = UIManager.getString("FileChooser.fileNameLabelText",l); 485 folderNameLabelMnemonic = getMnemonic("FileChooser.folderNameLabelMnemonic", l); 486 folderNameLabelText = UIManager.getString("FileChooser.folderNameLabelText",l); 487 488 filesOfTypeLabelMnemonic = getMnemonic("FileChooser.filesOfTypeLabelMnemonic", l); 489 filesOfTypeLabelText = UIManager.getString("FileChooser.filesOfTypeLabelText",l); 490 491 upFolderToolTipText = UIManager.getString("FileChooser.upFolderToolTipText",l); 492 upFolderAccessibleName = UIManager.getString("FileChooser.upFolderAccessibleName",l); 493 494 homeFolderToolTipText = UIManager.getString("FileChooser.homeFolderToolTipText",l); 495 homeFolderAccessibleName = UIManager.getString("FileChooser.homeFolderAccessibleName",l); 496 497 newFolderToolTipText = UIManager.getString("FileChooser.newFolderToolTipText",l); 498 newFolderAccessibleName = UIManager.getString("FileChooser.newFolderAccessibleName",l); 499 500 listViewButtonToolTipText = UIManager.getString("FileChooser.listViewButtonToolTipText",l); 501 listViewButtonAccessibleName = UIManager.getString("FileChooser.listViewButtonAccessibleName",l); 502 503 detailsViewButtonToolTipText = UIManager.getString("FileChooser.detailsViewButtonToolTipText",l); 504 detailsViewButtonAccessibleName = UIManager.getString("FileChooser.detailsViewButtonAccessibleName",l); 505 } 506 getMnemonic(String key, Locale l)507 private Integer getMnemonic(String key, Locale l) { 508 return SwingUtilities2.getUIDefaultsInt(key, l); 509 } 510 installListeners(JFileChooser fc)511 protected void installListeners(JFileChooser fc) { 512 super.installListeners(fc); 513 ActionMap actionMap = getActionMap(); 514 SwingUtilities.replaceUIActionMap(fc, actionMap); 515 } 516 517 /** 518 * Returns an instance of {@code ActionMap}. 519 * 520 * @return an instance of {@code ActionMap} 521 */ getActionMap()522 protected ActionMap getActionMap() { 523 return createActionMap(); 524 } 525 526 /** 527 * Constructs an instance of {@code ActionMap}. 528 * 529 * @return an instance of {@code ActionMap} 530 */ createActionMap()531 protected ActionMap createActionMap() { 532 ActionMap map = new ActionMapUIResource(); 533 FilePane.addActionsToMap(map, filePane.getActions()); 534 return map; 535 } 536 537 /** 538 * Constructs a details view. 539 * 540 * @param fc a {@code JFileChooser} 541 * @return the list 542 */ createList(JFileChooser fc)543 protected JPanel createList(JFileChooser fc) { 544 return filePane.createList(); 545 } 546 547 /** 548 * Constructs a details view. 549 * 550 * @param fc a {@code JFileChooser} 551 * @return the details view 552 */ createDetailsView(JFileChooser fc)553 protected JPanel createDetailsView(JFileChooser fc) { 554 return filePane.createDetailsView(); 555 } 556 557 /** 558 * Creates a selection listener for the list of files and directories. 559 * 560 * @param fc a <code>JFileChooser</code> 561 * @return a <code>ListSelectionListener</code> 562 */ createListSelectionListener(JFileChooser fc)563 public ListSelectionListener createListSelectionListener(JFileChooser fc) { 564 return super.createListSelectionListener(fc); 565 } 566 567 /** 568 * Obsolete class, not used in this version. 569 * @deprecated As of JDK version 9. Obsolete class. 570 */ 571 @Deprecated(since = "9") 572 protected class SingleClickListener extends MouseAdapter { 573 /** 574 * Constructs an instance of {@code SingleClickListener}. 575 * 576 * @param list an instance of {@code JList} 577 */ SingleClickListener(JList<?> list)578 public SingleClickListener(JList<?> list) { 579 } 580 } 581 582 /** 583 * Obsolete class, not used in this version. 584 * @deprecated As of JDK version 9. Obsolete class. 585 */ 586 @Deprecated(since = "9") 587 @SuppressWarnings("serial") // Superclass is not serializable across versions 588 protected class FileRenderer extends DefaultListCellRenderer { 589 } 590 uninstallUI(JComponent c)591 public void uninstallUI(JComponent c) { 592 // Remove listeners 593 c.removePropertyChangeListener(filterComboBoxModel); 594 c.removePropertyChangeListener(filePane); 595 cancelButton.removeActionListener(getCancelSelectionAction()); 596 approveButton.removeActionListener(getApproveSelectionAction()); 597 fileNameTextField.removeActionListener(getApproveSelectionAction()); 598 599 if (filePane != null) { 600 filePane.uninstallUI(); 601 filePane = null; 602 } 603 604 super.uninstallUI(c); 605 } 606 607 /** 608 * Returns the preferred size of the specified 609 * <code>JFileChooser</code>. 610 * The preferred size is at least as large, 611 * in both height and width, 612 * as the preferred size recommended 613 * by the file chooser's layout manager. 614 * 615 * @param c a <code>JFileChooser</code> 616 * @return a <code>Dimension</code> specifying the preferred 617 * width and height of the file chooser 618 */ 619 @Override getPreferredSize(JComponent c)620 public Dimension getPreferredSize(JComponent c) { 621 int prefWidth = PREF_SIZE.width; 622 Dimension d = c.getLayout().preferredLayoutSize(c); 623 if (d != null) { 624 return new Dimension(d.width < prefWidth ? prefWidth : d.width, 625 d.height < PREF_SIZE.height ? PREF_SIZE.height : d.height); 626 } else { 627 return new Dimension(prefWidth, PREF_SIZE.height); 628 } 629 } 630 631 /** 632 * Returns the minimum size of the <code>JFileChooser</code>. 633 * 634 * @param c a <code>JFileChooser</code> 635 * @return a <code>Dimension</code> specifying the minimum 636 * width and height of the file chooser 637 */ 638 @Override getMinimumSize(JComponent c)639 public Dimension getMinimumSize(JComponent c) { 640 return new Dimension(MIN_WIDTH, MIN_HEIGHT); 641 } 642 643 /** 644 * Returns the maximum size of the <code>JFileChooser</code>. 645 * 646 * @param c a <code>JFileChooser</code> 647 * @return a <code>Dimension</code> specifying the maximum 648 * width and height of the file chooser 649 */ 650 @Override getMaximumSize(JComponent c)651 public Dimension getMaximumSize(JComponent c) { 652 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 653 } 654 fileNameString(File file)655 private String fileNameString(File file) { 656 if (file == null) { 657 return null; 658 } else { 659 JFileChooser fc = getFileChooser(); 660 if ((fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) || 661 (fc.isDirectorySelectionEnabled() && fc.isFileSelectionEnabled() 662 && fc.getFileSystemView().isFileSystemRoot(file))) { 663 return file.getPath(); 664 } else { 665 return file.getName(); 666 } 667 } 668 } 669 fileNameString(File[] files)670 private String fileNameString(File[] files) { 671 StringBuilder sb = new StringBuilder(); 672 for (int i = 0; files != null && i < files.length; i++) { 673 if (i > 0) { 674 sb.append(" "); 675 } 676 if (files.length > 1) { 677 sb.append("\""); 678 } 679 sb.append(fileNameString(files[i])); 680 if (files.length > 1) { 681 sb.append("\""); 682 } 683 } 684 return sb.toString(); 685 } 686 687 /* The following methods are used by the PropertyChange Listener */ 688 doSelectedFileChanged(PropertyChangeEvent e)689 private void doSelectedFileChanged(PropertyChangeEvent e) { 690 File f = (File) e.getNewValue(); 691 JFileChooser fc = getFileChooser(); 692 if (f != null 693 && ((fc.isFileSelectionEnabled() && !f.isDirectory()) 694 || (f.isDirectory() && fc.isDirectorySelectionEnabled()))) { 695 696 setFileName(fileNameString(f)); 697 } 698 } 699 doSelectedFilesChanged(PropertyChangeEvent e)700 private void doSelectedFilesChanged(PropertyChangeEvent e) { 701 File[] files = (File[]) e.getNewValue(); 702 JFileChooser fc = getFileChooser(); 703 if (files != null 704 && files.length > 0 705 && (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) { 706 setFileName(fileNameString(files)); 707 } 708 } 709 doDirectoryChanged(PropertyChangeEvent e)710 private void doDirectoryChanged(PropertyChangeEvent e) { 711 JFileChooser fc = getFileChooser(); 712 FileSystemView fsv = fc.getFileSystemView(); 713 714 clearIconCache(); 715 File currentDirectory = fc.getCurrentDirectory(); 716 if(currentDirectory != null) { 717 directoryComboBoxModel.addItem(currentDirectory); 718 719 if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) { 720 if (fsv.isFileSystem(currentDirectory)) { 721 setFileName(currentDirectory.getPath()); 722 } else { 723 setFileName(null); 724 } 725 } 726 } 727 } 728 doFilterChanged(PropertyChangeEvent e)729 private void doFilterChanged(PropertyChangeEvent e) { 730 clearIconCache(); 731 } 732 doFileSelectionModeChanged(PropertyChangeEvent e)733 private void doFileSelectionModeChanged(PropertyChangeEvent e) { 734 if (fileNameLabel != null) { 735 populateFileNameLabel(); 736 } 737 clearIconCache(); 738 739 JFileChooser fc = getFileChooser(); 740 File currentDirectory = fc.getCurrentDirectory(); 741 if (currentDirectory != null 742 && fc.isDirectorySelectionEnabled() 743 && !fc.isFileSelectionEnabled() 744 && fc.getFileSystemView().isFileSystem(currentDirectory)) { 745 746 setFileName(currentDirectory.getPath()); 747 } else { 748 setFileName(null); 749 } 750 } 751 doAccessoryChanged(PropertyChangeEvent e)752 private void doAccessoryChanged(PropertyChangeEvent e) { 753 if(getAccessoryPanel() != null) { 754 if(e.getOldValue() != null) { 755 getAccessoryPanel().remove((JComponent) e.getOldValue()); 756 } 757 JComponent accessory = (JComponent) e.getNewValue(); 758 if(accessory != null) { 759 getAccessoryPanel().add(accessory, BorderLayout.CENTER); 760 } 761 } 762 } 763 doApproveButtonTextChanged(PropertyChangeEvent e)764 private void doApproveButtonTextChanged(PropertyChangeEvent e) { 765 JFileChooser chooser = getFileChooser(); 766 approveButton.setText(getApproveButtonText(chooser)); 767 approveButton.setToolTipText(getApproveButtonToolTipText(chooser)); 768 } 769 doDialogTypeChanged(PropertyChangeEvent e)770 private void doDialogTypeChanged(PropertyChangeEvent e) { 771 JFileChooser chooser = getFileChooser(); 772 approveButton.setText(getApproveButtonText(chooser)); 773 approveButton.setToolTipText(getApproveButtonToolTipText(chooser)); 774 if (chooser.getDialogType() == JFileChooser.SAVE_DIALOG) { 775 lookInLabel.setText(saveInLabelText); 776 } else { 777 lookInLabel.setText(lookInLabelText); 778 } 779 } 780 doApproveButtonMnemonicChanged(PropertyChangeEvent e)781 private void doApproveButtonMnemonicChanged(PropertyChangeEvent e) { 782 // Note: Metal does not use mnemonics for approve and cancel 783 } 784 doControlButtonsChanged(PropertyChangeEvent e)785 private void doControlButtonsChanged(PropertyChangeEvent e) { 786 if(getFileChooser().getControlButtonsAreShown()) { 787 addControlButtons(); 788 } else { 789 removeControlButtons(); 790 } 791 } 792 793 /* 794 * Listen for filechooser property changes, such as 795 * the selected file changing, or the type of the dialog changing. 796 */ createPropertyChangeListener(JFileChooser fc)797 public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { 798 return new PropertyChangeListener() { 799 public void propertyChange(PropertyChangeEvent e) { 800 String s = e.getPropertyName(); 801 if(s.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) { 802 doSelectedFileChanged(e); 803 } else if (s.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) { 804 doSelectedFilesChanged(e); 805 } else if(s.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) { 806 doDirectoryChanged(e); 807 } else if(s.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) { 808 doFilterChanged(e); 809 } else if(s.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) { 810 doFileSelectionModeChanged(e); 811 } else if(s.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) { 812 doAccessoryChanged(e); 813 } else if (s.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY) || 814 s.equals(JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) { 815 doApproveButtonTextChanged(e); 816 } else if(s.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY)) { 817 doDialogTypeChanged(e); 818 } else if(s.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) { 819 doApproveButtonMnemonicChanged(e); 820 } else if(s.equals(JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) { 821 doControlButtonsChanged(e); 822 } else if (s.equals("componentOrientation")) { 823 ComponentOrientation o = (ComponentOrientation)e.getNewValue(); 824 JFileChooser cc = (JFileChooser)e.getSource(); 825 if (o != e.getOldValue()) { 826 cc.applyComponentOrientation(o); 827 } 828 } else if (s == "FileChooser.useShellFolder") { 829 doDirectoryChanged(e); 830 } else if (s.equals("ancestor")) { 831 if (e.getOldValue() == null && e.getNewValue() != null) { 832 // Ancestor was added, set initial focus 833 fileNameTextField.selectAll(); 834 fileNameTextField.requestFocus(); 835 } 836 } 837 } 838 }; 839 } 840 841 /** 842 * Removes control buttons from bottom panel. 843 */ 844 protected void removeControlButtons() { 845 getBottomPanel().remove(getButtonPanel()); 846 } 847 848 /** 849 * Adds control buttons to bottom panel. 850 */ 851 protected void addControlButtons() { 852 getBottomPanel().add(getButtonPanel()); 853 } 854 855 public void ensureFileIsVisible(JFileChooser fc, File f) { 856 filePane.ensureFileIsVisible(fc, f); 857 } 858 859 public void rescanCurrentDirectory(JFileChooser fc) { 860 filePane.rescanCurrentDirectory(); 861 } 862 863 public String getFileName() { 864 if (fileNameTextField != null) { 865 return fileNameTextField.getText(); 866 } else { 867 return null; 868 } 869 } 870 871 public void setFileName(String filename) { 872 if (fileNameTextField != null) { 873 fileNameTextField.setText(filename); 874 } 875 } 876 877 /** 878 * Property to remember whether a directory is currently selected in the UI. 879 * This is normally called by the UI on a selection event. 880 * 881 * @param directorySelected if a directory is currently selected. 882 * @since 1.4 883 */ 884 protected void setDirectorySelected(boolean directorySelected) { 885 super.setDirectorySelected(directorySelected); 886 JFileChooser chooser = getFileChooser(); 887 if(directorySelected) { 888 if (approveButton != null) { 889 approveButton.setText(directoryOpenButtonText); 890 approveButton.setToolTipText(directoryOpenButtonToolTipText); 891 } 892 } else { 893 if (approveButton != null) { 894 approveButton.setText(getApproveButtonText(chooser)); 895 approveButton.setToolTipText(getApproveButtonToolTipText(chooser)); 896 } 897 } 898 } 899 900 /** 901 * Returns the directory name. 902 * 903 * @return the directory name 904 */ 905 public String getDirectoryName() { 906 // PENDING(jeff) - get the name from the directory combobox 907 return null; 908 } 909 910 /** 911 * Sets the directory name. 912 * 913 * @param dirname the directory name 914 */ 915 public void setDirectoryName(String dirname) { 916 // PENDING(jeff) - set the name in the directory combobox 917 } 918 919 /** 920 * Constructs a new instance of {@code DirectoryComboBoxRenderer}. 921 * 922 * @param fc a {@code JFileChooser} 923 * @return a new instance of {@code DirectoryComboBoxRenderer} 924 */ 925 private DefaultListCellRenderer createDirectoryComboBoxRenderer(JFileChooser fc) { 926 return new DirectoryComboBoxRenderer(); 927 } 928 929 // 930 // Renderer for DirectoryComboBox 931 // 932 @SuppressWarnings("serial") // Superclass is not serializable across versions 933 class DirectoryComboBoxRenderer extends DefaultListCellRenderer { 934 IndentIcon ii = new IndentIcon(); 935 public Component getListCellRendererComponent(JList<?> list, Object value, 936 int index, boolean isSelected, 937 boolean cellHasFocus) { 938 939 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 940 941 if (value == null) { 942 setText(""); 943 return this; 944 } 945 File directory = (File)value; 946 setText(getFileChooser().getName(directory)); 947 Icon icon = getFileChooser().getIcon(directory); 948 ii.icon = icon; 949 ii.depth = directoryComboBoxModel.getDepth(index); 950 setIcon(ii); 951 952 return this; 953 } 954 } 955 956 static final int space = 10; 957 class IndentIcon implements Icon { 958 959 Icon icon = null; 960 int depth = 0; 961 962 public void paintIcon(Component c, Graphics g, int x, int y) { 963 if (c.getComponentOrientation().isLeftToRight()) { 964 icon.paintIcon(c, g, x+depth*space, y); 965 } else { 966 icon.paintIcon(c, g, x, y); 967 } 968 } 969 970 public int getIconWidth() { 971 return icon.getIconWidth() + depth*space; 972 } 973 974 public int getIconHeight() { 975 return icon.getIconHeight(); 976 } 977 978 } 979 980 /** 981 * Constructs a new instance of {@code DataModel} for {@code DirectoryComboBox}. 982 * 983 * @param fc a {@code JFileChooser} 984 * @return a new instance of {@code DataModel} for {@code DirectoryComboBox} 985 */ 986 protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) { 987 return new DirectoryComboBoxModel(); 988 } 989 990 /** 991 * Data model for a type-face selection combo-box. 992 */ 993 @SuppressWarnings("serial") // Superclass is not serializable across versions 994 protected class DirectoryComboBoxModel extends AbstractListModel<Object> implements ComboBoxModel<Object> { 995 Vector<File> directories = new Vector<File>(); 996 int[] depths = null; 997 File selectedDirectory = null; 998 JFileChooser chooser = getFileChooser(); 999 FileSystemView fsv = chooser.getFileSystemView(); 1000 1001 /** 1002 * Constructs an instance of {@code DirectoryComboBoxModel}. 1003 */ 1004 public DirectoryComboBoxModel() { 1005 // Add the current directory to the model, and make it the 1006 // selectedDirectory 1007 File dir = getFileChooser().getCurrentDirectory(); 1008 if(dir != null) { 1009 addItem(dir); 1010 } 1011 } 1012 1013 /** 1014 * Adds the directory to the model and sets it to be selected, 1015 * additionally clears out the previous selected directory and 1016 * the paths leading up to it, if any. 1017 */ 1018 private void addItem(File directory) { 1019 1020 if(directory == null) { 1021 return; 1022 } 1023 1024 boolean useShellFolder = FilePane.usesShellFolder(chooser); 1025 1026 directories.clear(); 1027 1028 File[] baseFolders = (useShellFolder) 1029 ? (File[]) ShellFolder.get("fileChooserComboBoxFolders") 1030 : fsv.getRoots(); 1031 directories.addAll(Arrays.asList(baseFolders)); 1032 1033 // Get the canonical (full) path. This has the side 1034 // benefit of removing extraneous chars from the path, 1035 // for example /foo/bar/ becomes /foo/bar 1036 File canonical; 1037 try { 1038 canonical = ShellFolder.getNormalizedFile(directory); 1039 } catch (IOException e) { 1040 // Maybe drive is not ready. Can't abort here. 1041 canonical = directory; 1042 } 1043 1044 // create File instances of each directory leading up to the top 1045 try { 1046 File sf = useShellFolder ? ShellFolder.getShellFolder(canonical) 1047 : canonical; 1048 File f = sf; 1049 Vector<File> path = new Vector<File>(10); 1050 do { 1051 path.addElement(f); 1052 } while ((f = f.getParentFile()) != null); 1053 1054 int pathCount = path.size(); 1055 // Insert chain at appropriate place in vector 1056 for (int i = 0; i < pathCount; i++) { 1057 f = path.get(i); 1058 if (directories.contains(f)) { 1059 int topIndex = directories.indexOf(f); 1060 for (int j = i-1; j >= 0; j--) { 1061 directories.insertElementAt(path.get(j), topIndex+i-j); 1062 } 1063 break; 1064 } 1065 } 1066 calculateDepths(); 1067 setSelectedItem(sf); 1068 } catch (FileNotFoundException ex) { 1069 calculateDepths(); 1070 } 1071 } 1072 1073 private void calculateDepths() { 1074 depths = new int[directories.size()]; 1075 for (int i = 0; i < depths.length; i++) { 1076 File dir = directories.get(i); 1077 File parent = dir.getParentFile(); 1078 depths[i] = 0; 1079 if (parent != null) { 1080 for (int j = i-1; j >= 0; j--) { 1081 if (parent.equals(directories.get(j))) { 1082 depths[i] = depths[j] + 1; 1083 break; 1084 } 1085 } 1086 } 1087 } 1088 } 1089 1090 /** 1091 * Returns the depth of {@code i}-th file. 1092 * 1093 * @param i an index 1094 * @return the depth of {@code i}-th file 1095 */ 1096 public int getDepth(int i) { 1097 return (depths != null && i >= 0 && i < depths.length) ? depths[i] : 0; 1098 } 1099 1100 public void setSelectedItem(Object selectedDirectory) { 1101 this.selectedDirectory = (File)selectedDirectory; 1102 fireContentsChanged(this, -1, -1); 1103 } 1104 1105 public Object getSelectedItem() { 1106 return selectedDirectory; 1107 } 1108 1109 public int getSize() { 1110 return directories.size(); 1111 } 1112 1113 public Object getElementAt(int index) { 1114 return directories.elementAt(index); 1115 } 1116 } 1117 1118 /** 1119 * Constructs a {@code Renderer} for types {@code ComboBox}. 1120 * 1121 * @return a {@code Renderer} for types {@code ComboBox} 1122 */ 1123 protected FilterComboBoxRenderer createFilterComboBoxRenderer() { 1124 return new FilterComboBoxRenderer(); 1125 } 1126 1127 /** 1128 * Render different type sizes and styles. 1129 */ 1130 @SuppressWarnings("serial") // Superclass is not serializable across versions 1131 public class FilterComboBoxRenderer extends DefaultListCellRenderer { 1132 public Component getListCellRendererComponent(JList<?> list, 1133 Object value, int index, boolean isSelected, 1134 boolean cellHasFocus) { 1135 1136 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 1137 1138 if (value != null && value instanceof FileFilter) { 1139 setText(((FileFilter)value).getDescription()); 1140 } 1141 1142 return this; 1143 } 1144 } 1145 1146 /** 1147 * Constructs a {@code DataModel} for types {@code ComboBox}. 1148 * 1149 * @return a {@code DataModel} for types {@code ComboBox} 1150 */ 1151 protected FilterComboBoxModel createFilterComboBoxModel() { 1152 return new FilterComboBoxModel(); 1153 } 1154 1155 /** 1156 * Data model for a type-face selection combo-box. 1157 */ 1158 @SuppressWarnings("serial") // Same-version serialization only 1159 protected class FilterComboBoxModel extends AbstractListModel<Object> implements ComboBoxModel<Object>, PropertyChangeListener { 1160 1161 /** 1162 * An array of file filters. 1163 */ 1164 protected FileFilter[] filters; 1165 1166 /** 1167 * Constructs an instance of {@code FilterComboBoxModel}. 1168 */ 1169 protected FilterComboBoxModel() { 1170 super(); 1171 filters = getFileChooser().getChoosableFileFilters(); 1172 } 1173 1174 public void propertyChange(PropertyChangeEvent e) { 1175 String prop = e.getPropertyName(); 1176 if(prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) { 1177 filters = (FileFilter[]) e.getNewValue(); 1178 fireContentsChanged(this, -1, -1); 1179 } else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) { 1180 fireContentsChanged(this, -1, -1); 1181 } 1182 } 1183 1184 public void setSelectedItem(Object filter) { 1185 if(filter != null) { 1186 getFileChooser().setFileFilter((FileFilter) filter); 1187 fireContentsChanged(this, -1, -1); 1188 } 1189 } 1190 1191 public Object getSelectedItem() { 1192 // Ensure that the current filter is in the list. 1193 // NOTE: we shouldnt' have to do this, since JFileChooser adds 1194 // the filter to the choosable filters list when the filter 1195 // is set. Lets be paranoid just in case someone overrides 1196 // setFileFilter in JFileChooser. 1197 FileFilter currentFilter = getFileChooser().getFileFilter(); 1198 boolean found = false; 1199 if(currentFilter != null) { 1200 for (FileFilter filter : filters) { 1201 if (filter == currentFilter) { 1202 found = true; 1203 } 1204 } 1205 if(found == false) { 1206 getFileChooser().addChoosableFileFilter(currentFilter); 1207 } 1208 } 1209 return getFileChooser().getFileFilter(); 1210 } 1211 1212 public int getSize() { 1213 if(filters != null) { 1214 return filters.length; 1215 } else { 1216 return 0; 1217 } 1218 } 1219 1220 public Object getElementAt(int index) { 1221 if(index > getSize() - 1) { 1222 // This shouldn't happen. Try to recover gracefully. 1223 return getFileChooser().getFileFilter(); 1224 } 1225 if(filters != null) { 1226 return filters[index]; 1227 } else { 1228 return null; 1229 } 1230 } 1231 } 1232 1233 /** 1234 * Invokes when {@code ListSelectionEvent} occurs. 1235 * 1236 * @param e an instance of {@code ListSelectionEvent} 1237 */ 1238 public void valueChanged(ListSelectionEvent e) { 1239 JFileChooser fc = getFileChooser(); 1240 File f = fc.getSelectedFile(); 1241 if (!e.getValueIsAdjusting() && f != null && !getFileChooser().isTraversable(f)) { 1242 setFileName(fileNameString(f)); 1243 } 1244 } 1245 1246 /** 1247 * Acts when DirectoryComboBox has changed the selected item. 1248 */ 1249 @SuppressWarnings("serial") // Superclass is not serializable across versions 1250 protected class DirectoryComboBoxAction extends AbstractAction { 1251 1252 /** 1253 * Constructs a new instance of {@code DirectoryComboBoxAction}. 1254 */ 1255 protected DirectoryComboBoxAction() { 1256 super("DirectoryComboBoxAction"); 1257 } 1258 1259 public void actionPerformed(ActionEvent e) { 1260 directoryComboBox.hidePopup(); 1261 File f = (File)directoryComboBox.getSelectedItem(); 1262 if (!getFileChooser().getCurrentDirectory().equals(f)) { 1263 getFileChooser().setCurrentDirectory(f); 1264 } 1265 } 1266 } 1267 1268 protected JButton getApproveButton(JFileChooser fc) { 1269 return approveButton; 1270 } 1271 1272 1273 /** 1274 * <code>ButtonAreaLayout</code> behaves in a similar manner to 1275 * <code>FlowLayout</code>. It lays out all components from left to 1276 * right, flushed right. The widths of all components will be set 1277 * to the largest preferred size width. 1278 */ 1279 private static class ButtonAreaLayout implements LayoutManager { 1280 private int hGap = 5; 1281 private int topMargin = 17; 1282 1283 public void addLayoutComponent(String string, Component comp) { 1284 } 1285 1286 public void layoutContainer(Container container) { 1287 Component[] children = container.getComponents(); 1288 1289 if (children != null && children.length > 0) { 1290 int numChildren = children.length; 1291 Dimension[] sizes = new Dimension[numChildren]; 1292 Insets insets = container.getInsets(); 1293 int yLocation = insets.top + topMargin; 1294 int maxWidth = 0; 1295 1296 for (int counter = 0; counter < numChildren; counter++) { 1297 sizes[counter] = children[counter].getPreferredSize(); 1298 maxWidth = Math.max(maxWidth, sizes[counter].width); 1299 } 1300 int xLocation, xOffset; 1301 if (container.getComponentOrientation().isLeftToRight()) { 1302 xLocation = container.getSize().width - insets.left - maxWidth; 1303 xOffset = hGap + maxWidth; 1304 } else { 1305 xLocation = insets.left; 1306 xOffset = -(hGap + maxWidth); 1307 } 1308 for (int counter = numChildren - 1; counter >= 0; counter--) { 1309 children[counter].setBounds(xLocation, yLocation, 1310 maxWidth, sizes[counter].height); 1311 xLocation -= xOffset; 1312 } 1313 } 1314 } 1315 1316 public Dimension minimumLayoutSize(Container c) { 1317 if (c != null) { 1318 Component[] children = c.getComponents(); 1319 1320 if (children != null && children.length > 0) { 1321 int numChildren = children.length; 1322 int height = 0; 1323 Insets cInsets = c.getInsets(); 1324 int extraHeight = topMargin + cInsets.top + cInsets.bottom; 1325 int extraWidth = cInsets.left + cInsets.right; 1326 int maxWidth = 0; 1327 1328 for (int counter = 0; counter < numChildren; counter++) { 1329 Dimension aSize = children[counter].getPreferredSize(); 1330 height = Math.max(height, aSize.height); 1331 maxWidth = Math.max(maxWidth, aSize.width); 1332 } 1333 return new Dimension(extraWidth + numChildren * maxWidth + 1334 (numChildren - 1) * hGap, 1335 extraHeight + height); 1336 } 1337 } 1338 return new Dimension(0, 0); 1339 } 1340 1341 public Dimension preferredLayoutSize(Container c) { 1342 return minimumLayoutSize(c); 1343 } 1344 1345 public void removeLayoutComponent(Component c) { } 1346 } 1347 1348 private static void groupLabels(AlignedLabel[] group) { 1349 for (int i = 0; i < group.length; i++) { 1350 group[i].group = group; 1351 } 1352 } 1353 1354 @SuppressWarnings("serial") // Superclass is not serializable across versions 1355 private class AlignedLabel extends JLabel { 1356 private AlignedLabel[] group; 1357 private int maxWidth = 0; 1358 1359 AlignedLabel() { 1360 super(); 1361 setAlignmentX(JComponent.LEFT_ALIGNMENT); 1362 } 1363 1364 1365 AlignedLabel(String text) { 1366 super(text); 1367 setAlignmentX(JComponent.LEFT_ALIGNMENT); 1368 } 1369 1370 public Dimension getPreferredSize() { 1371 Dimension d = super.getPreferredSize(); 1372 // Align the width with all other labels in group. 1373 return new Dimension(getMaxWidth() + 11, d.height); 1374 } 1375 1376 private int getMaxWidth() { 1377 if (maxWidth == 0 && group != null) { 1378 int max = 0; 1379 for (int i = 0; i < group.length; i++) { 1380 max = Math.max(group[i].getSuperPreferredWidth(), max); 1381 } 1382 for (int i = 0; i < group.length; i++) { 1383 group[i].maxWidth = max; 1384 } 1385 } 1386 return maxWidth; 1387 } 1388 1389 private int getSuperPreferredWidth() { 1390 return super.getPreferredSize().width; 1391 } 1392 } 1393 } 1394