1 /* 2 * Copyright (c) 2003, 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 package sun.swing.plaf.synth; 26 27 import java.awt.*; 28 import java.awt.event.*; 29 import java.beans.*; 30 import java.io.*; 31 import java.util.*; 32 import java.security.AccessController; 33 import java.security.PrivilegedAction; 34 35 import javax.swing.*; 36 import javax.swing.event.*; 37 import javax.swing.filechooser.*; 38 import javax.swing.filechooser.FileFilter; 39 import javax.swing.plaf.basic.*; 40 import javax.swing.plaf.synth.*; 41 import javax.swing.plaf.ActionMapUIResource; 42 43 import sun.awt.shell.ShellFolder; 44 import sun.swing.*; 45 46 /** 47 * Synth FileChooserUI implementation. 48 * <p> 49 * Note that the classes in the com.sun.java.swing.plaf.synth 50 * package are not 51 * part of the core Java APIs. They are a part of Sun's JDK and JRE 52 * distributions. Although other licensees may choose to distribute 53 * these classes, developers cannot depend on their availability in 54 * non-Sun implementations. Additionally this API may change in 55 * incompatible ways between releases. While this class is public, it 56 * shoud be considered an implementation detail, and subject to change. 57 * 58 * @author Leif Samuelsson 59 * @author Jeff Dinkins 60 */ 61 public class SynthFileChooserUIImpl extends SynthFileChooserUI { 62 private JLabel lookInLabel; 63 private JComboBox<File> directoryComboBox; 64 private DirectoryComboBoxModel directoryComboBoxModel; 65 private Action directoryComboBoxAction = new DirectoryComboBoxAction(); 66 67 private FilterComboBoxModel filterComboBoxModel; 68 69 private JTextField fileNameTextField; 70 71 private FilePane filePane; 72 private JToggleButton listViewButton; 73 private JToggleButton detailsViewButton; 74 75 private boolean readOnly; 76 77 private JPanel buttonPanel; 78 private JPanel bottomPanel; 79 80 private JComboBox<FileFilter> filterComboBox; 81 82 private static final Dimension hstrut5 = new Dimension(5, 1); 83 84 private static final Insets shrinkwrap = new Insets(0,0,0,0); 85 86 // Preferred and Minimum sizes for the dialog box 87 private static Dimension LIST_PREF_SIZE = new Dimension(405, 135); 88 89 // Labels, mnemonics, and tooltips (oh my!) 90 private int lookInLabelMnemonic = 0; 91 private String lookInLabelText = null; 92 private String saveInLabelText = null; 93 94 private int fileNameLabelMnemonic = 0; 95 private String fileNameLabelText = null; 96 private int folderNameLabelMnemonic = 0; 97 private String folderNameLabelText = null; 98 99 private int filesOfTypeLabelMnemonic = 0; 100 private String filesOfTypeLabelText = null; 101 102 private String upFolderToolTipText = null; 103 private String upFolderAccessibleName = null; 104 105 private String homeFolderToolTipText = null; 106 private String homeFolderAccessibleName = null; 107 108 private String newFolderToolTipText = null; 109 private String newFolderAccessibleName = null; 110 111 private String listViewButtonToolTipText = null; 112 private String listViewButtonAccessibleName = null; 113 114 private String detailsViewButtonToolTipText = null; 115 private String detailsViewButtonAccessibleName = null; 116 117 private AlignedLabel fileNameLabel; 118 private final PropertyChangeListener modeListener = new PropertyChangeListener() { 119 public void propertyChange(PropertyChangeEvent event) { 120 if (fileNameLabel != null) { 121 populateFileNameLabel(); 122 } 123 } 124 }; 125 126 private void populateFileNameLabel() { 127 if (getFileChooser().getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) { 128 fileNameLabel.setText(folderNameLabelText); 129 fileNameLabel.setDisplayedMnemonic(folderNameLabelMnemonic); 130 } else { 131 fileNameLabel.setText(fileNameLabelText); 132 fileNameLabel.setDisplayedMnemonic(fileNameLabelMnemonic); 133 } 134 } 135 136 public SynthFileChooserUIImpl(JFileChooser b) { 137 super(b); 138 } 139 140 141 private class SynthFileChooserUIAccessor implements FilePane.FileChooserUIAccessor { 142 public JFileChooser getFileChooser() { 143 return SynthFileChooserUIImpl.this.getFileChooser(); 144 } 145 146 public BasicDirectoryModel getModel() { 147 return SynthFileChooserUIImpl.this.getModel(); 148 } 149 150 public JPanel createList() { 151 return null; 152 } 153 154 public JPanel createDetailsView() { 155 return null; 156 } 157 158 public boolean isDirectorySelected() { 159 return SynthFileChooserUIImpl.this.isDirectorySelected(); 160 } 161 162 public File getDirectory() { 163 return SynthFileChooserUIImpl.this.getDirectory(); 164 } 165 166 public Action getChangeToParentDirectoryAction() { 167 return SynthFileChooserUIImpl.this.getChangeToParentDirectoryAction(); 168 } 169 170 public Action getApproveSelectionAction() { 171 return SynthFileChooserUIImpl.this.getApproveSelectionAction(); 172 } 173 174 public Action getNewFolderAction() { 175 return SynthFileChooserUIImpl.this.getNewFolderAction(); 176 } 177 178 public MouseListener createDoubleClickListener(JList<?> list) { 179 return SynthFileChooserUIImpl.this.createDoubleClickListener(getFileChooser(), 180 list); 181 } 182 183 public ListSelectionListener createListSelectionListener() { 184 return SynthFileChooserUIImpl.this.createListSelectionListener(getFileChooser()); 185 } 186 } 187 188 protected void installDefaults(JFileChooser fc) { 189 super.installDefaults(fc); 190 readOnly = UIManager.getBoolean("FileChooser.readOnly"); 191 } 192 193 @SuppressWarnings("serial") // anonymous classes inside 194 public void installComponents(JFileChooser fc) { 195 super.installComponents(fc); 196 197 SynthContext context = getContext(fc, ENABLED); 198 199 fc.setLayout(new BorderLayout(0, 11)); 200 201 // ********************************* // 202 // **** Construct the top panel **** // 203 // ********************************* // 204 205 // Directory manipulation buttons 206 JPanel topPanel = new JPanel(new BorderLayout(11, 0)); 207 JPanel topButtonPanel = new JPanel(); 208 topButtonPanel.setLayout(new BoxLayout(topButtonPanel, BoxLayout.LINE_AXIS)); 209 topPanel.add(topButtonPanel, BorderLayout.AFTER_LINE_ENDS); 210 211 // Add the top panel to the fileChooser 212 fc.add(topPanel, BorderLayout.NORTH); 213 214 // ComboBox Label 215 lookInLabel = new JLabel(lookInLabelText); 216 lookInLabel.setDisplayedMnemonic(lookInLabelMnemonic); 217 topPanel.add(lookInLabel, BorderLayout.BEFORE_LINE_BEGINS); 218 219 // CurrentDir ComboBox 220 directoryComboBox = new JComboBox<File>(); 221 directoryComboBox.getAccessibleContext().setAccessibleDescription(lookInLabelText); 222 directoryComboBox.putClientProperty( "JComboBox.isTableCellEditor", Boolean.TRUE ); 223 lookInLabel.setLabelFor(directoryComboBox); 224 directoryComboBoxModel = createDirectoryComboBoxModel(fc); 225 directoryComboBox.setModel(directoryComboBoxModel); 226 directoryComboBox.addActionListener(directoryComboBoxAction); 227 directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc)); 228 directoryComboBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); 229 directoryComboBox.setAlignmentY(JComponent.TOP_ALIGNMENT); 230 directoryComboBox.setMaximumRowCount(8); 231 topPanel.add(directoryComboBox, BorderLayout.CENTER); 232 233 filePane = new FilePane(new SynthFileChooserUIAccessor()); 234 fc.addPropertyChangeListener(filePane); 235 236 // Add 'Go Up' to context menu, plus 'Go Home' if on Unix 237 JPopupMenu contextMenu = filePane.getComponentPopupMenu(); 238 if (contextMenu != null) { 239 contextMenu.insert(getChangeToParentDirectoryAction(), 0); 240 if (File.separatorChar == '/') { 241 contextMenu.insert(getGoHomeAction(), 1); 242 } 243 } 244 245 FileSystemView fsv = fc.getFileSystemView(); 246 247 // Up Button 248 JButton upFolderButton = new JButton(getChangeToParentDirectoryAction()); 249 upFolderButton.setText(null); 250 upFolderButton.setIcon(upFolderIcon); 251 upFolderButton.setToolTipText(upFolderToolTipText); 252 upFolderButton.getAccessibleContext().setAccessibleName(upFolderAccessibleName); 253 upFolderButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); 254 upFolderButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); 255 upFolderButton.setMargin(shrinkwrap); 256 257 topButtonPanel.add(upFolderButton); 258 topButtonPanel.add(Box.createRigidArea(hstrut5)); 259 260 // Home Button 261 File homeDir = fsv.getHomeDirectory(); 262 String toolTipText = homeFolderToolTipText; 263 if (fsv.isRoot(homeDir)) { 264 toolTipText = getFileView(fc).getName(homeDir); // Probably "Desktop". 265 } 266 267 JButton b = new JButton(homeFolderIcon); 268 b.setToolTipText(toolTipText); 269 b.getAccessibleContext().setAccessibleName(homeFolderAccessibleName); 270 b.setAlignmentX(JComponent.LEFT_ALIGNMENT); 271 b.setAlignmentY(JComponent.CENTER_ALIGNMENT); 272 b.setMargin(shrinkwrap); 273 274 b.addActionListener(getGoHomeAction()); 275 topButtonPanel.add(b); 276 topButtonPanel.add(Box.createRigidArea(hstrut5)); 277 278 // New Directory Button 279 if (!readOnly) { 280 b = new JButton(filePane.getNewFolderAction()); 281 b.setText(null); 282 b.setIcon(newFolderIcon); 283 b.setToolTipText(newFolderToolTipText); 284 b.getAccessibleContext().setAccessibleName(newFolderAccessibleName); 285 b.setAlignmentX(JComponent.LEFT_ALIGNMENT); 286 b.setAlignmentY(JComponent.CENTER_ALIGNMENT); 287 b.setMargin(shrinkwrap); 288 topButtonPanel.add(b); 289 topButtonPanel.add(Box.createRigidArea(hstrut5)); 290 } 291 292 // View button group 293 ButtonGroup viewButtonGroup = new ButtonGroup(); 294 295 // List Button 296 listViewButton = new JToggleButton(listViewIcon); 297 listViewButton.setToolTipText(listViewButtonToolTipText); 298 listViewButton.getAccessibleContext().setAccessibleName(listViewButtonAccessibleName); 299 listViewButton.setSelected(true); 300 listViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); 301 listViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); 302 listViewButton.setMargin(shrinkwrap); 303 listViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_LIST)); 304 topButtonPanel.add(listViewButton); 305 viewButtonGroup.add(listViewButton); 306 307 // Details Button 308 detailsViewButton = new JToggleButton(detailsViewIcon); 309 detailsViewButton.setToolTipText(detailsViewButtonToolTipText); 310 detailsViewButton.getAccessibleContext().setAccessibleName(detailsViewButtonAccessibleName); 311 detailsViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); 312 detailsViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); 313 detailsViewButton.setMargin(shrinkwrap); 314 detailsViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_DETAILS)); 315 topButtonPanel.add(detailsViewButton); 316 viewButtonGroup.add(detailsViewButton); 317 318 filePane.addPropertyChangeListener(new PropertyChangeListener() { 319 public void propertyChange(PropertyChangeEvent e) { 320 if ("viewType".equals(e.getPropertyName())) { 321 int viewType = filePane.getViewType(); 322 switch (viewType) { 323 case FilePane.VIEWTYPE_LIST: 324 listViewButton.setSelected(true); 325 break; 326 case FilePane.VIEWTYPE_DETAILS: 327 detailsViewButton.setSelected(true); 328 break; 329 } 330 } 331 } 332 }); 333 334 // ************************************** // 335 // ******* Add the directory pane ******* // 336 // ************************************** // 337 fc.add(getAccessoryPanel(), BorderLayout.AFTER_LINE_ENDS); 338 JComponent accessory = fc.getAccessory(); 339 if (accessory != null) { 340 getAccessoryPanel().add(accessory); 341 } 342 filePane.setPreferredSize(LIST_PREF_SIZE); 343 fc.add(filePane, BorderLayout.CENTER); 344 345 346 // ********************************** // 347 // **** Construct the bottom panel ** // 348 // ********************************** // 349 bottomPanel = new JPanel(); 350 bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS)); 351 fc.add(bottomPanel, BorderLayout.SOUTH); 352 353 // FileName label and textfield 354 JPanel fileNamePanel = new JPanel(); 355 fileNamePanel.setLayout(new BoxLayout(fileNamePanel, BoxLayout.LINE_AXIS)); 356 bottomPanel.add(fileNamePanel); 357 bottomPanel.add(Box.createRigidArea(new Dimension(1, 5))); 358 359 fileNameLabel = new AlignedLabel(); 360 populateFileNameLabel(); 361 fileNamePanel.add(fileNameLabel); 362 363 fileNameTextField = new JTextField(35) { 364 public Dimension getMaximumSize() { 365 return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height); 366 } 367 }; 368 fileNamePanel.add(fileNameTextField); 369 fileNameLabel.setLabelFor(fileNameTextField); 370 fileNameTextField.addFocusListener( 371 new FocusAdapter() { 372 public void focusGained(FocusEvent e) { 373 if (!getFileChooser().isMultiSelectionEnabled()) { 374 filePane.clearSelection(); 375 } 376 } 377 } 378 ); 379 if (fc.isMultiSelectionEnabled()) { 380 setFileName(fileNameString(fc.getSelectedFiles())); 381 } else { 382 setFileName(fileNameString(fc.getSelectedFile())); 383 } 384 385 386 // Filetype label and combobox 387 JPanel filesOfTypePanel = new JPanel(); 388 filesOfTypePanel.setLayout(new BoxLayout(filesOfTypePanel, BoxLayout.LINE_AXIS)); 389 bottomPanel.add(filesOfTypePanel); 390 391 AlignedLabel filesOfTypeLabel = new AlignedLabel(filesOfTypeLabelText); 392 filesOfTypeLabel.setDisplayedMnemonic(filesOfTypeLabelMnemonic); 393 filesOfTypePanel.add(filesOfTypeLabel); 394 395 filterComboBoxModel = createFilterComboBoxModel(); 396 fc.addPropertyChangeListener(filterComboBoxModel); 397 filterComboBox = new JComboBox<FileFilter>(filterComboBoxModel); 398 filterComboBox.getAccessibleContext().setAccessibleDescription(filesOfTypeLabelText); 399 filesOfTypeLabel.setLabelFor(filterComboBox); 400 filterComboBox.setRenderer(createFilterComboBoxRenderer()); 401 filesOfTypePanel.add(filterComboBox); 402 403 404 // buttons 405 buttonPanel = new JPanel(); 406 buttonPanel.setLayout(new ButtonAreaLayout()); 407 408 buttonPanel.add(getApproveButton(fc)); 409 buttonPanel.add(getCancelButton(fc)); 410 411 if (fc.getControlButtonsAreShown()) { 412 addControlButtons(); 413 } 414 415 groupLabels(new AlignedLabel[] { fileNameLabel, filesOfTypeLabel }); 416 } 417 418 protected void installListeners(JFileChooser fc) { 419 super.installListeners(fc); 420 fc.addPropertyChangeListener(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY, modeListener); 421 } 422 423 protected void uninstallListeners(JFileChooser fc) { 424 fc.removePropertyChangeListener(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY, modeListener); 425 super.uninstallListeners(fc); 426 } 427 428 private String fileNameString(File file) { 429 if (file == null) { 430 return null; 431 } else { 432 JFileChooser fc = getFileChooser(); 433 if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) { 434 return file.getPath(); 435 } else { 436 return file.getName(); 437 } 438 } 439 } 440 441 private String fileNameString(File[] files) { 442 StringBuilder sb = new StringBuilder(); 443 for (int i = 0; files != null && i < files.length; i++) { 444 if (i > 0) { 445 sb.append(" "); 446 } 447 if (files.length > 1) { 448 sb.append("\""); 449 } 450 sb.append(fileNameString(files[i])); 451 if (files.length > 1) { 452 sb.append("\""); 453 } 454 } 455 return sb.toString(); 456 } 457 458 public void uninstallUI(JComponent c) { 459 // Remove listeners 460 c.removePropertyChangeListener(filterComboBoxModel); 461 c.removePropertyChangeListener(filePane); 462 463 if (filePane != null) { 464 filePane.uninstallUI(); 465 filePane = null; 466 } 467 468 super.uninstallUI(c); 469 } 470 471 protected void installStrings(JFileChooser fc) { 472 super.installStrings(fc); 473 474 Locale l = fc.getLocale(); 475 476 lookInLabelMnemonic = getMnemonic("FileChooser.lookInLabelMnemonic", l); 477 lookInLabelText = UIManager.getString("FileChooser.lookInLabelText", l); 478 saveInLabelText = UIManager.getString("FileChooser.saveInLabelText", l); 479 480 fileNameLabelMnemonic = getMnemonic("FileChooser.fileNameLabelMnemonic", l); 481 fileNameLabelText = UIManager.getString("FileChooser.fileNameLabelText", l); 482 folderNameLabelMnemonic = getMnemonic("FileChooser.folderNameLabelMnemonic", l); 483 folderNameLabelText = UIManager.getString("FileChooser.folderNameLabelText", l); 484 485 filesOfTypeLabelMnemonic = getMnemonic("FileChooser.filesOfTypeLabelMnemonic", l); 486 filesOfTypeLabelText = UIManager.getString("FileChooser.filesOfTypeLabelText", l); 487 488 upFolderToolTipText = UIManager.getString("FileChooser.upFolderToolTipText",l); 489 upFolderAccessibleName = UIManager.getString("FileChooser.upFolderAccessibleName",l); 490 491 homeFolderToolTipText = UIManager.getString("FileChooser.homeFolderToolTipText",l); 492 homeFolderAccessibleName = UIManager.getString("FileChooser.homeFolderAccessibleName",l); 493 494 newFolderToolTipText = UIManager.getString("FileChooser.newFolderToolTipText",l); 495 newFolderAccessibleName = UIManager.getString("FileChooser.newFolderAccessibleName",l); 496 497 listViewButtonToolTipText = UIManager.getString("FileChooser.listViewButtonToolTipText",l); 498 listViewButtonAccessibleName = UIManager.getString("FileChooser.listViewButtonAccessibleName",l); 499 500 detailsViewButtonToolTipText = UIManager.getString("FileChooser.detailsViewButtonToolTipText",l); 501 detailsViewButtonAccessibleName = UIManager.getString("FileChooser.detailsViewButtonAccessibleName",l); 502 } 503 504 private int getMnemonic(String key, Locale l) { 505 return SwingUtilities2.getUIDefaultsInt(key, l); 506 } 507 508 509 public String getFileName() { 510 if (fileNameTextField != null) { 511 return fileNameTextField.getText(); 512 } else { 513 return null; 514 } 515 } 516 517 public void setFileName(String fileName) { 518 if (fileNameTextField != null) { 519 fileNameTextField.setText(fileName); 520 } 521 } 522 523 @Override public void rescanCurrentDirectory(JFileChooser fc) { 524 filePane.rescanCurrentDirectory(); 525 } 526 527 protected void doSelectedFileChanged(PropertyChangeEvent e) { 528 super.doSelectedFileChanged(e); 529 530 File f = (File) e.getNewValue(); 531 JFileChooser fc = getFileChooser(); 532 if (f != null 533 && ((fc.isFileSelectionEnabled() && !f.isDirectory()) 534 || (f.isDirectory() && fc.isDirectorySelectionEnabled()))) { 535 536 setFileName(fileNameString(f)); 537 } 538 } 539 540 protected void doSelectedFilesChanged(PropertyChangeEvent e) { 541 super.doSelectedFilesChanged(e); 542 543 File[] files = (File[]) e.getNewValue(); 544 JFileChooser fc = getFileChooser(); 545 if (files != null 546 && files.length > 0 547 && (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) { 548 setFileName(fileNameString(files)); 549 } 550 } 551 552 protected void doDirectoryChanged(PropertyChangeEvent e) { 553 super.doDirectoryChanged(e); 554 555 JFileChooser fc = getFileChooser(); 556 FileSystemView fsv = fc.getFileSystemView(); 557 File currentDirectory = fc.getCurrentDirectory(); 558 559 if (!readOnly && currentDirectory != null) { 560 getNewFolderAction().setEnabled(filePane.canWrite(currentDirectory)); 561 } 562 563 if (currentDirectory != null) { 564 JComponent cb = getDirectoryComboBox(); 565 if (cb instanceof JComboBox) { 566 ComboBoxModel<?> model = ((JComboBox)cb).getModel(); 567 if (model instanceof DirectoryComboBoxModel) { 568 ((DirectoryComboBoxModel)model).addItem(currentDirectory); 569 } 570 } 571 572 if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) { 573 if (fsv.isFileSystem(currentDirectory)) { 574 setFileName(currentDirectory.getPath()); 575 } else { 576 setFileName(null); 577 } 578 } 579 } 580 } 581 582 583 protected void doFileSelectionModeChanged(PropertyChangeEvent e) { 584 super.doFileSelectionModeChanged(e); 585 586 JFileChooser fc = getFileChooser(); 587 File currentDirectory = fc.getCurrentDirectory(); 588 if (currentDirectory != null 589 && fc.isDirectorySelectionEnabled() 590 && !fc.isFileSelectionEnabled() 591 && fc.getFileSystemView().isFileSystem(currentDirectory)) { 592 593 setFileName(currentDirectory.getPath()); 594 } else { 595 setFileName(null); 596 } 597 } 598 599 protected void doAccessoryChanged(PropertyChangeEvent e) { 600 if (getAccessoryPanel() != null) { 601 if (e.getOldValue() != null) { 602 getAccessoryPanel().remove((JComponent)e.getOldValue()); 603 } 604 JComponent accessory = (JComponent)e.getNewValue(); 605 if (accessory != null) { 606 getAccessoryPanel().add(accessory, BorderLayout.CENTER); 607 } 608 } 609 } 610 611 protected void doControlButtonsChanged(PropertyChangeEvent e) { 612 super.doControlButtonsChanged(e); 613 614 if (getFileChooser().getControlButtonsAreShown()) { 615 addControlButtons(); 616 } else { 617 removeControlButtons(); 618 } 619 } 620 621 protected void addControlButtons() { 622 if (bottomPanel != null) { 623 bottomPanel.add(buttonPanel); 624 } 625 } 626 627 protected void removeControlButtons() { 628 if (bottomPanel != null) { 629 bottomPanel.remove(buttonPanel); 630 } 631 } 632 633 634 635 636 // ******************************************************* 637 // ************ FileChooser UI PLAF methods ************** 638 // ******************************************************* 639 640 protected ActionMap createActionMap() { 641 ActionMap map = new ActionMapUIResource(); 642 // add standard actions 643 FilePane.addActionsToMap(map, filePane.getActions()); 644 // add synth only actions 645 map.put("fileNameCompletion", getFileNameCompletionAction()); 646 return map; 647 } 648 649 // ***************************** 650 // ***** Directory Actions ***** 651 // ***************************** 652 653 protected JComponent getDirectoryComboBox() { 654 return directoryComboBox; 655 } 656 657 protected Action getDirectoryComboBoxAction() { 658 return directoryComboBoxAction; 659 } 660 661 protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer(JFileChooser fc) { 662 return new DirectoryComboBoxRenderer(directoryComboBox.getRenderer()); 663 } 664 665 // 666 // Renderer for DirectoryComboBox 667 // 668 // Synth has some odd behavior with regards to renderers. Renderers are styled 669 // in a specific manner by the SynthComboBoxUI. If we extend DefaultListCellRenderer 670 // here, then we get none of those benefits or behaviors, leading to poor 671 // looking combo boxes. 672 // So what we do here is delegate most jobs to the "real" or original renderer, 673 // and simply monkey with the icon and text of the renderer. 674 private class DirectoryComboBoxRenderer implements ListCellRenderer<File> { 675 private ListCellRenderer<? super File> delegate; 676 IndentIcon ii = new IndentIcon(); 677 678 private DirectoryComboBoxRenderer(ListCellRenderer<? super File> delegate) { 679 this.delegate = delegate; 680 } 681 682 @Override 683 public Component getListCellRendererComponent(JList<? extends File> list, File value, int index, boolean isSelected, boolean cellHasFocus) { 684 Component c = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 685 686 assert c instanceof JLabel; 687 JLabel label = (JLabel)c; 688 if (value == null) { 689 label.setText(""); 690 return label; 691 } 692 label.setText(getFileChooser().getName(value)); 693 Icon icon = getFileChooser().getIcon(value); 694 ii.icon = icon; 695 ii.depth = directoryComboBoxModel.getDepth(index); 696 label.setIcon(ii); 697 698 return label; 699 } 700 } 701 702 final static int space = 10; 703 class IndentIcon implements Icon { 704 705 Icon icon = null; 706 int depth = 0; 707 708 public void paintIcon(Component c, Graphics g, int x, int y) { 709 if (icon != null) { 710 if (c.getComponentOrientation().isLeftToRight()) { 711 icon.paintIcon(c, g, x+depth*space, y); 712 } else { 713 icon.paintIcon(c, g, x, y); 714 } 715 } 716 } 717 718 public int getIconWidth() { 719 return ((icon != null) ? icon.getIconWidth() : 0) + depth*space; 720 } 721 722 public int getIconHeight() { 723 return (icon != null) ? icon.getIconHeight() : 0; 724 } 725 726 } 727 728 // 729 // DataModel for DirectoryComboxbox 730 // 731 protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) { 732 return new DirectoryComboBoxModel(); 733 } 734 735 /** 736 * Data model for a type-face selection combo-box. 737 */ 738 @SuppressWarnings("serial") // JDK-implementation class 739 protected class DirectoryComboBoxModel extends AbstractListModel<File> implements ComboBoxModel<File> { 740 Vector<File> directories = new Vector<File>(); 741 int[] depths = null; 742 File selectedDirectory = null; 743 JFileChooser chooser = getFileChooser(); 744 FileSystemView fsv = chooser.getFileSystemView(); 745 746 public DirectoryComboBoxModel() { 747 // Add the current directory to the model, and make it the 748 // selectedDirectory 749 File dir = getFileChooser().getCurrentDirectory(); 750 if (dir != null) { 751 addItem(dir); 752 } 753 } 754 755 /** 756 * Adds the directory to the model and sets it to be selected, 757 * additionally clears out the previous selected directory and 758 * the paths leading up to it, if any. 759 */ 760 public void addItem(File directory) { 761 762 if (directory == null) { 763 return; 764 } 765 766 boolean useShellFolder = FilePane.usesShellFolder(chooser); 767 768 int oldSize = directories.size(); 769 directories.clear(); 770 if (oldSize > 0) { 771 fireIntervalRemoved(this, 0, oldSize); 772 } 773 774 File[] baseFolders = (useShellFolder) 775 ? (File[]) ShellFolder.get("fileChooserComboBoxFolders") 776 : fsv.getRoots(); 777 directories.addAll(Arrays.asList(baseFolders)); 778 779 // Get the canonical (full) path. This has the side 780 // benefit of removing extraneous chars from the path, 781 // for example /foo/bar/ becomes /foo/bar 782 File canonical; 783 try { 784 canonical = ShellFolder.getNormalizedFile(directory); 785 } catch (IOException e) { 786 // Maybe drive is not ready. Can't abort here. 787 canonical = directory; 788 } 789 790 // create File instances of each directory leading up to the top 791 try { 792 File sf = useShellFolder ? ShellFolder.getShellFolder(canonical) 793 : canonical; 794 File f = sf; 795 Vector<File> path = new Vector<File>(10); 796 do { 797 path.addElement(f); 798 } while ((f = f.getParentFile()) != null); 799 800 int pathCount = path.size(); 801 // Insert chain at appropriate place in vector 802 for (int i = 0; i < pathCount; i++) { 803 f = path.get(i); 804 if (directories.contains(f)) { 805 int topIndex = directories.indexOf(f); 806 for (int j = i-1; j >= 0; j--) { 807 directories.insertElementAt(path.get(j), topIndex+i-j); 808 } 809 break; 810 } 811 } 812 calculateDepths(); 813 setSelectedItem(sf); 814 } catch (FileNotFoundException ex) { 815 calculateDepths(); 816 } 817 } 818 819 private void calculateDepths() { 820 depths = new int[directories.size()]; 821 for (int i = 0; i < depths.length; i++) { 822 File dir = directories.get(i); 823 File parent = dir.getParentFile(); 824 depths[i] = 0; 825 if (parent != null) { 826 for (int j = i-1; j >= 0; j--) { 827 if (parent.equals(directories.get(j))) { 828 depths[i] = depths[j] + 1; 829 break; 830 } 831 } 832 } 833 } 834 } 835 836 public int getDepth(int i) { 837 return (depths != null && i >= 0 && i < depths.length) ? depths[i] : 0; 838 } 839 840 public void setSelectedItem(Object selectedDirectory) { 841 this.selectedDirectory = (File)selectedDirectory; 842 fireContentsChanged(this, -1, -1); 843 } 844 845 public Object getSelectedItem() { 846 return selectedDirectory; 847 } 848 849 public int getSize() { 850 return directories.size(); 851 } 852 853 public File getElementAt(int index) { 854 return directories.elementAt(index); 855 } 856 } 857 858 /** 859 * Acts when DirectoryComboBox has changed the selected item. 860 */ 861 @SuppressWarnings("serial") // JDK-implementation class 862 protected class DirectoryComboBoxAction extends AbstractAction { 863 protected DirectoryComboBoxAction() { 864 super("DirectoryComboBoxAction"); 865 } 866 867 public void actionPerformed(ActionEvent e) { 868 directoryComboBox.hidePopup(); 869 JComponent cb = getDirectoryComboBox(); 870 if (cb instanceof JComboBox) { 871 File f = (File)((JComboBox)cb).getSelectedItem(); 872 getFileChooser().setCurrentDirectory(f); 873 } 874 } 875 } 876 877 // 878 // Renderer for Types ComboBox 879 // 880 protected FilterComboBoxRenderer createFilterComboBoxRenderer() { 881 return new FilterComboBoxRenderer(filterComboBox.getRenderer()); 882 } 883 884 /** 885 * Render different type sizes and styles. 886 */ 887 public class FilterComboBoxRenderer implements ListCellRenderer<FileFilter> { 888 private ListCellRenderer<? super FileFilter> delegate; 889 private FilterComboBoxRenderer(ListCellRenderer<? super FileFilter> delegate) { 890 this.delegate = delegate; 891 } 892 893 public Component getListCellRendererComponent(JList<? extends FileFilter> list, FileFilter value, int index, 894 boolean isSelected, boolean cellHasFocus) { 895 Component c = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 896 897 String text = null; 898 if (value != null) { 899 text = value.getDescription(); 900 } 901 902 //this should always be true, since SynthComboBoxUI's SynthComboBoxRenderer 903 //extends JLabel 904 assert c instanceof JLabel; 905 if (text != null) { 906 ((JLabel)c).setText(text); 907 } 908 return c; 909 } 910 } 911 912 // 913 // DataModel for Types Comboxbox 914 // 915 protected FilterComboBoxModel createFilterComboBoxModel() { 916 return new FilterComboBoxModel(); 917 } 918 919 /** 920 * Data model for a type-face selection combo-box. 921 */ 922 @SuppressWarnings("serial") // JDK-implementation class 923 protected class FilterComboBoxModel extends AbstractListModel<FileFilter> implements ComboBoxModel<FileFilter>, 924 PropertyChangeListener { 925 protected FileFilter[] filters; 926 protected FilterComboBoxModel() { 927 super(); 928 filters = getFileChooser().getChoosableFileFilters(); 929 } 930 931 public void propertyChange(PropertyChangeEvent e) { 932 String prop = e.getPropertyName(); 933 if(prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) { 934 filters = (FileFilter[]) e.getNewValue(); 935 fireContentsChanged(this, -1, -1); 936 } else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) { 937 fireContentsChanged(this, -1, -1); 938 } 939 } 940 941 public void setSelectedItem(Object filter) { 942 if(filter != null) { 943 getFileChooser().setFileFilter((FileFilter) filter); 944 fireContentsChanged(this, -1, -1); 945 } 946 } 947 948 public Object getSelectedItem() { 949 // Ensure that the current filter is in the list. 950 // NOTE: we shouldnt' have to do this, since JFileChooser adds 951 // the filter to the choosable filters list when the filter 952 // is set. Lets be paranoid just in case someone overrides 953 // setFileFilter in JFileChooser. 954 FileFilter currentFilter = getFileChooser().getFileFilter(); 955 boolean found = false; 956 if(currentFilter != null) { 957 for (FileFilter filter : filters) { 958 if (filter == currentFilter) { 959 found = true; 960 } 961 } 962 if(found == false) { 963 getFileChooser().addChoosableFileFilter(currentFilter); 964 } 965 } 966 return getFileChooser().getFileFilter(); 967 } 968 969 public int getSize() { 970 if(filters != null) { 971 return filters.length; 972 } else { 973 return 0; 974 } 975 } 976 977 public FileFilter getElementAt(int index) { 978 if(index > getSize() - 1) { 979 // This shouldn't happen. Try to recover gracefully. 980 return getFileChooser().getFileFilter(); 981 } 982 if(filters != null) { 983 return filters[index]; 984 } else { 985 return null; 986 } 987 } 988 } 989 990 991 992 /** 993 * <code>ButtonAreaLayout</code> behaves in a similar manner to 994 * <code>FlowLayout</code>. It lays out all components from left to 995 * right, flushed right. The widths of all components will be set 996 * to the largest preferred size width. 997 */ 998 private static class ButtonAreaLayout implements LayoutManager { 999 private int hGap = 5; 1000 private int topMargin = 17; 1001 1002 public void addLayoutComponent(String string, Component comp) { 1003 } 1004 1005 public void layoutContainer(Container container) { 1006 Component[] children = container.getComponents(); 1007 1008 if (children != null && children.length > 0) { 1009 int numChildren = children.length; 1010 Dimension[] sizes = new Dimension[numChildren]; 1011 Insets insets = container.getInsets(); 1012 int yLocation = insets.top + topMargin; 1013 int maxWidth = 0; 1014 1015 for (int counter = 0; counter < numChildren; counter++) { 1016 sizes[counter] = children[counter].getPreferredSize(); 1017 maxWidth = Math.max(maxWidth, sizes[counter].width); 1018 } 1019 int xLocation, xOffset; 1020 if (container.getComponentOrientation().isLeftToRight()) { 1021 xLocation = container.getSize().width - insets.left - maxWidth; 1022 xOffset = hGap + maxWidth; 1023 } else { 1024 xLocation = insets.left; 1025 xOffset = -(hGap + maxWidth); 1026 } 1027 for (int counter = numChildren - 1; counter >= 0; counter--) { 1028 children[counter].setBounds(xLocation, yLocation, 1029 maxWidth, sizes[counter].height); 1030 xLocation -= xOffset; 1031 } 1032 } 1033 } 1034 1035 public Dimension minimumLayoutSize(Container c) { 1036 if (c != null) { 1037 Component[] children = c.getComponents(); 1038 1039 if (children != null && children.length > 0) { 1040 int numChildren = children.length; 1041 int height = 0; 1042 Insets cInsets = c.getInsets(); 1043 int extraHeight = topMargin + cInsets.top + cInsets.bottom; 1044 int extraWidth = cInsets.left + cInsets.right; 1045 int maxWidth = 0; 1046 1047 for (int counter = 0; counter < numChildren; counter++) { 1048 Dimension aSize = children[counter].getPreferredSize(); 1049 height = Math.max(height, aSize.height); 1050 maxWidth = Math.max(maxWidth, aSize.width); 1051 } 1052 return new Dimension(extraWidth + numChildren * maxWidth + 1053 (numChildren - 1) * hGap, 1054 extraHeight + height); 1055 } 1056 } 1057 return new Dimension(0, 0); 1058 } 1059 1060 public Dimension preferredLayoutSize(Container c) { 1061 return minimumLayoutSize(c); 1062 } 1063 1064 public void removeLayoutComponent(Component c) { } 1065 } 1066 1067 private static void groupLabels(AlignedLabel[] group) { 1068 for (int i = 0; i < group.length; i++) { 1069 group[i].group = group; 1070 } 1071 } 1072 1073 @SuppressWarnings("serial") // JDK-implementation class 1074 private class AlignedLabel extends JLabel { 1075 private AlignedLabel[] group; 1076 private int maxWidth = 0; 1077 1078 AlignedLabel() { 1079 super(); 1080 setAlignmentX(JComponent.LEFT_ALIGNMENT); 1081 } 1082 1083 AlignedLabel(String text) { 1084 super(text); 1085 setAlignmentX(JComponent.LEFT_ALIGNMENT); 1086 } 1087 1088 public Dimension getPreferredSize() { 1089 Dimension d = super.getPreferredSize(); 1090 // Align the width with all other labels in group. 1091 return new Dimension(getMaxWidth() + 11, d.height); 1092 } 1093 1094 private int getMaxWidth() { 1095 if (maxWidth == 0 && group != null) { 1096 int max = 0; 1097 for (int i = 0; i < group.length; i++) { 1098 max = Math.max(group[i].getSuperPreferredWidth(), max); 1099 } 1100 for (int i = 0; i < group.length; i++) { 1101 group[i].maxWidth = max; 1102 } 1103 } 1104 return maxWidth; 1105 } 1106 1107 private int getSuperPreferredWidth() { 1108 return super.getPreferredSize().width; 1109 } 1110 } 1111 }