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