001/* MetalFileChooserUI.java -- 002 Copyright (C) 2005 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.swing.plaf.metal; 040 041import java.awt.BorderLayout; 042import java.awt.Component; 043import java.awt.Container; 044import java.awt.Dimension; 045import java.awt.Graphics; 046import java.awt.GridLayout; 047import java.awt.Insets; 048import java.awt.LayoutManager; 049import java.awt.Rectangle; 050import java.awt.Window; 051import java.awt.event.ActionEvent; 052import java.awt.event.ActionListener; 053import java.awt.event.MouseAdapter; 054import java.awt.event.MouseEvent; 055import java.awt.event.MouseListener; 056import java.beans.PropertyChangeEvent; 057import java.beans.PropertyChangeListener; 058import java.io.File; 059import java.text.DateFormat; 060import java.text.NumberFormat; 061import java.util.Date; 062import java.util.List; 063 064import javax.swing.AbstractAction; 065import javax.swing.AbstractListModel; 066import javax.swing.ActionMap; 067import javax.swing.BorderFactory; 068import javax.swing.ButtonGroup; 069import javax.swing.ComboBoxModel; 070import javax.swing.DefaultListCellRenderer; 071import javax.swing.Icon; 072import javax.swing.JButton; 073import javax.swing.JComboBox; 074import javax.swing.JComponent; 075import javax.swing.JDialog; 076import javax.swing.JFileChooser; 077import javax.swing.JLabel; 078import javax.swing.JList; 079import javax.swing.JPanel; 080import javax.swing.JScrollPane; 081import javax.swing.JTable; 082import javax.swing.JTextField; 083import javax.swing.JToggleButton; 084import javax.swing.ListModel; 085import javax.swing.ListSelectionModel; 086import javax.swing.SwingUtilities; 087import javax.swing.UIManager; 088import javax.swing.event.ListSelectionEvent; 089import javax.swing.event.ListSelectionListener; 090import javax.swing.filechooser.FileFilter; 091import javax.swing.filechooser.FileSystemView; 092import javax.swing.filechooser.FileView; 093import javax.swing.plaf.ComponentUI; 094import javax.swing.plaf.basic.BasicFileChooserUI; 095import javax.swing.table.DefaultTableCellRenderer; 096import javax.swing.table.DefaultTableModel; 097 098 099/** 100 * A UI delegate for the {@link JFileChooser} component. This class is only 101 * partially implemented and is not usable yet. 102 */ 103public class MetalFileChooserUI 104 extends BasicFileChooserUI 105{ 106 107 /** 108 * A renderer for the files and directories in the file chooser table. 109 */ 110 class TableFileRenderer 111 extends DefaultTableCellRenderer 112 { 113 114 /** 115 * Creates a new renderer. 116 */ 117 public TableFileRenderer() 118 { 119 super(); 120 } 121 122 /** 123 * Returns a component that can render the specified value. 124 * 125 * @param table the table 126 * @param value the string value of the cell 127 * @param isSelected is the item selected? 128 * @param hasFocus does the item have the focus? 129 * @param row the row 130 * @param column the column 131 * 132 * @return The renderer. 133 */ 134 public Component getTableCellRendererComponent(JTable table, Object value, 135 boolean isSelected, boolean hasFocus, int row, int column) 136 { 137 if (column == 0) 138 { 139 FileView v = getFileView(getFileChooser()); 140 ListModel lm = fileList.getModel(); 141 if (row < lm.getSize()) 142 setIcon(v.getIcon((File) lm.getElementAt(row))); 143 } 144 else 145 setIcon(null); 146 147 setText(value.toString()); 148 setOpaque(true); 149 setEnabled(table.isEnabled()); 150 setFont(fileList.getFont()); 151 152 if (startEditing && column == 0 || !isSelected) 153 { 154 setBackground(table.getBackground()); 155 setForeground(table.getForeground()); 156 } 157 else 158 { 159 setBackground(table.getSelectionBackground()); 160 setForeground(table.getSelectionForeground()); 161 } 162 163 if (hasFocus) 164 setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); 165 else 166 setBorder(noFocusBorder); 167 168 return this; 169 } 170 } 171 172 /** 173 * ActionListener for the list view. 174 */ 175 class ListViewActionListener implements ActionListener 176 { 177 178 /** 179 * This method is invoked when an action occurs. 180 * 181 * @param e - 182 * the <code>ActionEvent</code> that occurred 183 */ 184 public void actionPerformed(ActionEvent e) 185 { 186 if (!listView) 187 { 188 int[] index = fileTable.getSelectedRows(); 189 listView = true; 190 JFileChooser fc = getFileChooser(); 191 fc.remove(fileTablePanel); 192 createList(fc); 193 194 fileList.getSelectionModel().clearSelection(); 195 if (index.length > 0) 196 for (int i = 0; i < index.length; i++) 197 fileList.getSelectionModel().addSelectionInterval(index[i], index[i]); 198 199 fc.add(fileListPanel, BorderLayout.CENTER); 200 fc.revalidate(); 201 fc.repaint(); 202 } 203 } 204 } 205 206 /** 207 * ActionListener for the details view. 208 */ 209 class DetailViewActionListener implements ActionListener 210 { 211 212 /** 213 * This method is invoked when an action occurs. 214 * 215 * @param e - 216 * the <code>ActionEvent</code> that occurred 217 */ 218 public void actionPerformed(ActionEvent e) 219 { 220 if (listView) 221 { 222 int[] index = fileList.getSelectedIndices(); 223 JFileChooser fc = getFileChooser(); 224 listView = false; 225 fc.remove(fileListPanel); 226 227 if (fileTable == null) 228 createDetailsView(fc); 229 else 230 updateTable(); 231 232 fileTable.getSelectionModel().clearSelection(); 233 if (index.length > 0) 234 { 235 for (int i = 0; i < index.length; i++) 236 fileTable.getSelectionModel().addSelectionInterval(index[i], index[i]); 237 } 238 239 fc.add(fileTablePanel, BorderLayout.CENTER); 240 fc.revalidate(); 241 fc.repaint(); 242 } 243 } 244 } 245 246 /** 247 * A property change listener. 248 */ 249 class MetalFileChooserPropertyChangeListener 250 implements PropertyChangeListener 251 { 252 /** 253 * Default constructor. 254 */ 255 public MetalFileChooserPropertyChangeListener() 256 { 257 } 258 259 /** 260 * Handles a property change event. 261 * 262 * @param e the event. 263 */ 264 public void propertyChange(PropertyChangeEvent e) 265 { 266 JFileChooser filechooser = getFileChooser(); 267 268 String n = e.getPropertyName(); 269 if (n.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) 270 { 271 int mode = -1; 272 if (filechooser.isMultiSelectionEnabled()) 273 mode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION; 274 else 275 mode = ListSelectionModel.SINGLE_SELECTION; 276 277 if (listView) 278 fileList.setSelectionMode(mode); 279 else 280 fileTable.setSelectionMode(mode); 281 } 282 else if (n.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) 283 { 284 File file = filechooser.getSelectedFile(); 285 286 if (file != null 287 && filechooser.getDialogType() == JFileChooser.SAVE_DIALOG) 288 { 289 if (file.isDirectory() && filechooser.isTraversable(file)) 290 { 291 directoryLabel = look; 292 dirLabel.setText(directoryLabel); 293 filechooser.setApproveButtonText(openButtonText); 294 filechooser.setApproveButtonToolTipText(openButtonToolTipText); 295 } 296 else if (file.isFile()) 297 { 298 directoryLabel = save; 299 dirLabel.setText(directoryLabel); 300 filechooser.setApproveButtonText(saveButtonText); 301 filechooser.setApproveButtonToolTipText(saveButtonToolTipText); 302 } 303 } 304 305 if (file == null) 306 setFileName(null); 307 else if (file.isFile() || filechooser.getFileSelectionMode() 308 != JFileChooser.FILES_ONLY) 309 setFileName(file.getName()); 310 int index = -1; 311 index = getModel().indexOf(file); 312 if (index >= 0) 313 { 314 if (listView) 315 { 316 fileList.setSelectedIndex(index); 317 fileList.ensureIndexIsVisible(index); 318 fileList.revalidate(); 319 fileList.repaint(); 320 } 321 else 322 { 323 fileTable.getSelectionModel().addSelectionInterval(index, index); 324 fileTable.scrollRectToVisible(fileTable.getCellRect(index, 0, true)); 325 fileTable.revalidate(); 326 fileTable.repaint(); 327 } 328 } 329 } 330 331 else if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) 332 { 333 if (listView) 334 { 335 fileList.clearSelection(); 336 fileList.revalidate(); 337 fileList.repaint(); 338 } 339 else 340 { 341 fileTable.clearSelection(); 342 fileTable.revalidate(); 343 fileTable.repaint(); 344 } 345 346 setDirectorySelected(false); 347 File currentDirectory = filechooser.getCurrentDirectory(); 348 setDirectory(currentDirectory); 349 boolean hasParent = currentDirectory.getParentFile() != null; 350 getChangeToParentDirectoryAction().setEnabled(hasParent); 351 } 352 353 else if (n.equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY)) 354 { 355 filterModel.propertyChange(e); 356 } 357 else if (n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) 358 { 359 filterModel.propertyChange(e); 360 } 361 else if (n.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY) 362 || n.equals(JFileChooser.DIALOG_TITLE_CHANGED_PROPERTY)) 363 { 364 Window owner = SwingUtilities.windowForComponent(filechooser); 365 if (owner instanceof JDialog) 366 ((JDialog) owner).setTitle(getDialogTitle(filechooser)); 367 approveButton.setText(getApproveButtonText(filechooser)); 368 approveButton.setToolTipText( 369 getApproveButtonToolTipText(filechooser)); 370 approveButton.setMnemonic(getApproveButtonMnemonic(filechooser)); 371 } 372 373 else if (n.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY)) 374 approveButton.setText(getApproveButtonText(filechooser)); 375 376 else if (n.equals( 377 JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) 378 approveButton.setToolTipText(getApproveButtonToolTipText(filechooser)); 379 380 else if (n.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) 381 approveButton.setMnemonic(getApproveButtonMnemonic(filechooser)); 382 383 else if (n.equals( 384 JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) 385 { 386 if (filechooser.getControlButtonsAreShown()) 387 { 388 topPanel.add(controls, BorderLayout.EAST); 389 } 390 else 391 topPanel.remove(controls); 392 topPanel.revalidate(); 393 topPanel.repaint(); 394 topPanel.doLayout(); 395 } 396 397 else if (n.equals( 398 JFileChooser.ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY)) 399 { 400 if (filechooser.isAcceptAllFileFilterUsed()) 401 filechooser.addChoosableFileFilter( 402 getAcceptAllFileFilter(filechooser)); 403 else 404 filechooser.removeChoosableFileFilter( 405 getAcceptAllFileFilter(filechooser)); 406 } 407 408 else if (n.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) 409 { 410 JComponent old = (JComponent) e.getOldValue(); 411 if (old != null) 412 getAccessoryPanel().remove(old); 413 JComponent newval = (JComponent) e.getNewValue(); 414 if (newval != null) 415 getAccessoryPanel().add(newval); 416 } 417 418 if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY) 419 || n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY) 420 || n.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY)) 421 { 422 // Remove editing component 423 if (fileTable != null) 424 fileTable.removeAll(); 425 if (fileList != null) 426 fileList.removeAll(); 427 startEditing = false; 428 429 // Set text on button back to original. 430 if (filechooser.getDialogType() == JFileChooser.SAVE_DIALOG) 431 { 432 directoryLabel = save; 433 dirLabel.setText(directoryLabel); 434 filechooser.setApproveButtonText(saveButtonText); 435 filechooser.setApproveButtonToolTipText(saveButtonToolTipText); 436 } 437 438 rescanCurrentDirectory(filechooser); 439 } 440 441 filechooser.revalidate(); 442 filechooser.repaint(); 443 } 444 } 445 446 /** 447 * A combo box model containing the selected directory and all its parent 448 * directories. 449 */ 450 protected class DirectoryComboBoxModel 451 extends AbstractListModel 452 implements ComboBoxModel 453 { 454 /** Storage for the items in the model. */ 455 private List items; 456 457 /** The index of the selected item. */ 458 private int selectedIndex; 459 460 /** 461 * Creates a new model. 462 */ 463 public DirectoryComboBoxModel() 464 { 465 items = new java.util.ArrayList(); 466 selectedIndex = -1; 467 } 468 469 /** 470 * Returns the number of items in the model. 471 * 472 * @return The number of items in the model. 473 */ 474 public int getSize() 475 { 476 return items.size(); 477 } 478 479 /** 480 * Returns the item at the specified index. 481 * 482 * @param index the item index. 483 * 484 * @return The item. 485 */ 486 public Object getElementAt(int index) 487 { 488 return items.get(index); 489 } 490 491 /** 492 * Returns the depth of the item at the given <code>index</code>. 493 * 494 * @param index the item index. 495 * 496 * @return The depth. 497 */ 498 public int getDepth(int index) 499 { 500 return Math.max(index, 0); 501 } 502 503 /** 504 * Returns the selected item, or <code>null</code> if no item is selected. 505 * 506 * @return The selected item, or <code>null</code>. 507 */ 508 public Object getSelectedItem() 509 { 510 if (selectedIndex >= 0) 511 return items.get(selectedIndex); 512 else 513 return null; 514 } 515 516 /** 517 * Sets the selected item. This clears all the directories from the 518 * existing list, and repopulates it with the new selected directory 519 * and all its parent directories. 520 * 521 * @param selectedDirectory the selected directory. 522 */ 523 public void setSelectedItem(Object selectedDirectory) 524 { 525 items.clear(); 526 FileSystemView fsv = getFileChooser().getFileSystemView(); 527 File parent = (File) selectedDirectory; 528 while (parent != null) 529 { 530 items.add(0, parent); 531 parent = fsv.getParentDirectory(parent); 532 } 533 selectedIndex = items.indexOf(selectedDirectory); 534 fireContentsChanged(this, 0, items.size() - 1); 535 } 536 537 } 538 539 /** 540 * Handles changes to the selection in the directory combo box. 541 */ 542 protected class DirectoryComboBoxAction 543 extends AbstractAction 544 { 545 /** 546 * Creates a new action. 547 */ 548 protected DirectoryComboBoxAction() 549 { 550 // Nothing to do here. 551 } 552 553 /** 554 * Handles the action event. 555 * 556 * @param e the event. 557 */ 558 public void actionPerformed(ActionEvent e) 559 { 560 JFileChooser fc = getFileChooser(); 561 fc.setCurrentDirectory((File) directoryModel.getSelectedItem()); 562 } 563 } 564 565 /** 566 * A renderer for the items in the directory combo box. 567 */ 568 class DirectoryComboBoxRenderer 569 extends DefaultListCellRenderer 570 { 571 /** 572 * This is the icon that is displayed in the combobox. This wraps 573 * the standard icon and adds indendation. 574 */ 575 private IndentIcon indentIcon; 576 577 /** 578 * Creates a new renderer. 579 */ 580 public DirectoryComboBoxRenderer(JFileChooser fc) 581 { 582 indentIcon = new IndentIcon(); 583 } 584 585 /** 586 * Returns a component that can be used to paint the given value within 587 * the list. 588 * 589 * @param list the list. 590 * @param value the value (a {@link File}). 591 * @param index the item index. 592 * @param isSelected is the item selected? 593 * @param cellHasFocus does the list cell have focus? 594 * 595 * @return The list cell renderer. 596 */ 597 public Component getListCellRendererComponent(JList list, Object value, 598 int index, 599 boolean isSelected, 600 boolean cellHasFocus) 601 { 602 super.getListCellRendererComponent(list, value, index, isSelected, 603 cellHasFocus); 604 File file = (File) value; 605 setText(getFileChooser().getName(file)); 606 607 // Install indented icon. 608 Icon icon = getFileChooser().getIcon(file); 609 indentIcon.setIcon(icon); 610 int depth = directoryModel.getDepth(index); 611 indentIcon.setDepth(depth); 612 setIcon(indentIcon); 613 614 return this; 615 } 616 } 617 618 /** 619 * An icon that wraps another icon and adds indentation. 620 */ 621 class IndentIcon 622 implements Icon 623 { 624 625 /** 626 * The indentation level. 627 */ 628 private static final int INDENT = 10; 629 630 /** 631 * The wrapped icon. 632 */ 633 private Icon icon; 634 635 /** 636 * The current depth. 637 */ 638 private int depth; 639 640 /** 641 * Sets the icon to be wrapped. 642 * 643 * @param i the icon 644 */ 645 void setIcon(Icon i) 646 { 647 icon = i; 648 } 649 650 /** 651 * Sets the indentation depth. 652 * 653 * @param d the depth to set 654 */ 655 void setDepth(int d) 656 { 657 depth = d; 658 } 659 660 public int getIconHeight() 661 { 662 return icon.getIconHeight(); 663 } 664 665 public int getIconWidth() 666 { 667 return icon.getIconWidth() + depth * INDENT; 668 } 669 670 public void paintIcon(Component c, Graphics g, int x, int y) 671 { 672 icon.paintIcon(c, g, x + depth * INDENT, y); 673 } 674 675 } 676 677 /** 678 * A renderer for the files and directories in the file chooser. 679 */ 680 protected class FileRenderer 681 extends DefaultListCellRenderer 682 { 683 684 /** 685 * Creates a new renderer. 686 */ 687 protected FileRenderer() 688 { 689 // Nothing to do here. 690 } 691 692 /** 693 * Returns a component that can render the specified value. 694 * 695 * @param list the list. 696 * @param value the value (a {@link File}). 697 * @param index the index. 698 * @param isSelected is the item selected? 699 * @param cellHasFocus does the item have the focus? 700 * 701 * @return The renderer. 702 */ 703 public Component getListCellRendererComponent(JList list, Object value, 704 int index, boolean isSelected, boolean cellHasFocus) 705 { 706 FileView v = getFileView(getFileChooser()); 707 File f = (File) value; 708 if (f != null) 709 { 710 setText(v.getName(f)); 711 setIcon(v.getIcon(f)); 712 } 713 else 714 { 715 setText(""); 716 setIcon(null); 717 } 718 setOpaque(true); 719 if (isSelected) 720 { 721 setBackground(list.getSelectionBackground()); 722 setForeground(list.getSelectionForeground()); 723 } 724 else 725 { 726 setBackground(list.getBackground()); 727 setForeground(list.getForeground()); 728 } 729 730 setEnabled(list.isEnabled()); 731 setFont(list.getFont()); 732 733 if (cellHasFocus) 734 setBorder(UIManager.getBorder("List.focusCellHighlightBorder")); 735 else 736 setBorder(noFocusBorder); 737 return this; 738 } 739 } 740 741 /** 742 * A combo box model for the file selection filters. 743 */ 744 protected class FilterComboBoxModel 745 extends AbstractListModel 746 implements ComboBoxModel, PropertyChangeListener 747 { 748 749 /** Storage for the filters in the model. */ 750 protected FileFilter[] filters; 751 752 /** The index of the selected file filter. */ 753 private Object selected; 754 755 /** 756 * Creates a new model. 757 */ 758 protected FilterComboBoxModel() 759 { 760 filters = new FileFilter[1]; 761 filters[0] = getAcceptAllFileFilter(getFileChooser()); 762 selected = filters[0]; 763 } 764 765 /** 766 * Handles property changes. 767 * 768 * @param e the property change event. 769 */ 770 public void propertyChange(PropertyChangeEvent e) 771 { 772 if (e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) 773 { 774 JFileChooser fc = getFileChooser(); 775 FileFilter[] choosableFilters = fc.getChoosableFileFilters(); 776 filters = choosableFilters; 777 fireContentsChanged(this, 0, filters.length); 778 selected = e.getNewValue(); 779 fireContentsChanged(this, -1, -1); 780 } 781 else if (e.getPropertyName().equals( 782 JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY)) 783 { 784 // repopulate list 785 JFileChooser fc = getFileChooser(); 786 FileFilter[] choosableFilters = fc.getChoosableFileFilters(); 787 filters = choosableFilters; 788 fireContentsChanged(this, 0, filters.length); 789 } 790 } 791 792 /** 793 * Sets the selected filter. 794 * 795 * @param filter the filter (<code>null</code> ignored). 796 */ 797 public void setSelectedItem(Object filter) 798 { 799 if (filter != null) 800 { 801 selected = filter; 802 fireContentsChanged(this, -1, -1); 803 } 804 } 805 806 /** 807 * Returns the selected file filter. 808 * 809 * @return The selected file filter. 810 */ 811 public Object getSelectedItem() 812 { 813 return selected; 814 } 815 816 /** 817 * Returns the number of items in the model. 818 * 819 * @return The number of items in the model. 820 */ 821 public int getSize() 822 { 823 return filters.length; 824 } 825 826 /** 827 * Returns the item at the specified index. 828 * 829 * @param index the item index. 830 * 831 * @return The item at the specified index. 832 */ 833 public Object getElementAt(int index) 834 { 835 return filters[index]; 836 } 837 838 } 839 840 /** 841 * A renderer for the items in the file filter combo box. 842 */ 843 public class FilterComboBoxRenderer 844 extends DefaultListCellRenderer 845 { 846 /** 847 * Creates a new renderer. 848 */ 849 public FilterComboBoxRenderer() 850 { 851 // Nothing to do here. 852 } 853 854 /** 855 * Returns a component that can be used to paint the given value within 856 * the list. 857 * 858 * @param list the list. 859 * @param value the value (a {@link FileFilter}). 860 * @param index the item index. 861 * @param isSelected is the item selected? 862 * @param cellHasFocus does the list cell have focus? 863 * 864 * @return This component as the renderer. 865 */ 866 public Component getListCellRendererComponent(JList list, Object value, 867 int index, boolean isSelected, boolean cellHasFocus) 868 { 869 super.getListCellRendererComponent(list, value, index, isSelected, 870 cellHasFocus); 871 FileFilter filter = (FileFilter) value; 872 setText(filter.getDescription()); 873 return this; 874 } 875 } 876 877 /** 878 * A listener for selection events in the file list. 879 * 880 * @see #createListSelectionListener(JFileChooser) 881 */ 882 class MetalFileChooserSelectionListener 883 implements ListSelectionListener 884 { 885 /** 886 * Creates a new <code>SelectionListener</code> object. 887 */ 888 protected MetalFileChooserSelectionListener() 889 { 890 // Do nothing here. 891 } 892 893 /** 894 * Makes changes to different properties when 895 * a value has changed in the filechooser's selection. 896 * 897 * @param e - the list selection event that occured. 898 */ 899 public void valueChanged(ListSelectionEvent e) 900 { 901 File f = (File) fileList.getSelectedValue(); 902 if (f == null) 903 return; 904 JFileChooser filechooser = getFileChooser(); 905 if (! filechooser.isTraversable(f)) 906 filechooser.setSelectedFile(f); 907 else 908 filechooser.setSelectedFile(null); 909 } 910 } 911 912 /** 913 * A mouse listener for the {@link JFileChooser}. 914 * This listener is used for editing filenames. 915 */ 916 protected class SingleClickListener 917 extends MouseAdapter 918 { 919 920 /** Stores instance of the list */ 921 JList list; 922 923 /** 924 * Stores the current file that is being edited. 925 * It is null if nothing is currently being edited. 926 */ 927 File editFile; 928 929 /** The current file chooser. */ 930 JFileChooser fc; 931 932 /** The last file selected. */ 933 Object lastSelected; 934 935 /** The textfield used for editing. */ 936 JTextField editField; 937 938 /** 939 * Creates a new listener. 940 * 941 * @param list the directory/file list. 942 */ 943 public SingleClickListener(JList list) 944 { 945 this.list = list; 946 editFile = null; 947 fc = getFileChooser(); 948 lastSelected = null; 949 startEditing = false; 950 } 951 952 /** 953 * Receives notification of a mouse click event. 954 * 955 * @param e the event. 956 */ 957 public void mouseClicked(MouseEvent e) 958 { 959 if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) 960 { 961 int index = list.locationToIndex(e.getPoint()); 962 File[] sf = fc.getSelectedFiles(); 963 if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1)) 964 && index >= 0 && !startEditing && list.isSelectedIndex(index)) 965 { 966 Object tmp = list.getModel().getElementAt(index); 967 if (lastSelected != null && lastSelected.equals(tmp)) 968 editFile(index); 969 lastSelected = tmp; 970 } 971 else 972 completeEditing(); 973 } 974 else 975 completeEditing(); 976 } 977 978 /** 979 * Sets up the text editor for the current file. 980 * 981 * @param index - 982 * the current index of the item in the list to be edited. 983 */ 984 void editFile(int index) 985 { 986 Rectangle bounds = list.getCellBounds(index, index); 987 list.scrollRectToVisible(bounds); 988 editFile = (File) list.getModel().getElementAt(index); 989 if (editFile.canWrite()) 990 { 991 startEditing = true; 992 editField = new JTextField(editFile.getName()); 993 editField.addActionListener(new EditingActionListener()); 994 995 Icon icon = getFileView(fc).getIcon(editFile); 996 if (icon != null) 997 { 998 int padding = icon.getIconWidth() + 4; 999 bounds.x += padding; 1000 bounds.width -= padding; 1001 } 1002 editField.setBounds(bounds); 1003 1004 list.add(editField); 1005 1006 editField.requestFocus(); 1007 editField.selectAll(); 1008 } 1009 else 1010 completeEditing(); 1011 list.repaint(); 1012 } 1013 1014 /** 1015 * Completes the editing. 1016 */ 1017 void completeEditing() 1018 { 1019 if (editField != null && editFile != null) 1020 { 1021 String text = editField.getText(); 1022 if (text != null && text != "" && !text.equals(fc.getName(editFile))) 1023 { 1024 File f = fc.getFileSystemView(). 1025 createFileObject(fc.getCurrentDirectory(), text); 1026 if ( editFile.renameTo(f) ) 1027 rescanCurrentDirectory(fc); 1028 } 1029 list.remove(editField); 1030 } 1031 startEditing = false; 1032 editFile = null; 1033 lastSelected = null; 1034 editField = null; 1035 list.repaint(); 1036 } 1037 1038 /** 1039 * ActionListener for the editing text field. 1040 */ 1041 class EditingActionListener implements ActionListener 1042 { 1043 1044 /** 1045 * This method is invoked when an action occurs. 1046 * 1047 * @param e - 1048 * the <code>ActionEvent</code> that occurred 1049 */ 1050 public void actionPerformed(ActionEvent e) 1051 { 1052 if (editField != null) 1053 completeEditing(); 1054 } 1055 } 1056 } 1057 1058 /** 1059 * A mouse listener for the {@link JFileChooser}. 1060 * This listener is used for the table 1061 */ 1062 private class TableClickListener extends MouseAdapter 1063 { 1064 1065 /** Stores instance of the table */ 1066 JTable table; 1067 1068 /** Stores instance of the file chooser */ 1069 JFileChooser fc; 1070 1071 /** The last selected file. */ 1072 Object lastSelected; 1073 1074 /** 1075 * Stores the current file that is being edited. 1076 * It is null if nothing is currently being edited. 1077 */ 1078 File editFile; 1079 1080 /** The textfield used for editing. */ 1081 JTextField editField; 1082 1083 /** 1084 * Creates a new listener. 1085 * 1086 * @param table the directory/file table 1087 * @param fc the JFileChooser 1088 */ 1089 public TableClickListener(JTable table, JFileChooser fc) 1090 { 1091 this.table = table; 1092 this.fc = fc; 1093 lastSelected = fileList.getSelectedValue(); 1094 setDirectorySelected(false); 1095 startEditing = false; 1096 editFile = null; 1097 editField = null; 1098 } 1099 1100 /** 1101 * Receives notification of a mouse click event. 1102 * 1103 * @param e the event. 1104 */ 1105 public void mouseClicked(MouseEvent e) 1106 { 1107 int row = table.getSelectedRow(); 1108 Object selVal = fileList.getModel().getElementAt(row); 1109 if (selVal == null) 1110 return; 1111 FileSystemView fsv = fc.getFileSystemView(); 1112 if (e.getClickCount() == 1 && 1113 selVal.equals(lastSelected) && 1114 e.getButton() == MouseEvent.BUTTON1) 1115 { 1116 File[] sf = fc.getSelectedFiles(); 1117 if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1)) 1118 && !startEditing) 1119 { 1120 editFile = (File) selVal; 1121 editFile(row); 1122 } 1123 } 1124 else if (e.getClickCount() >= 2 && 1125 selVal.equals(lastSelected)) 1126 { 1127 if (startEditing) 1128 completeEditing(); 1129 File f = fsv.createFileObject(lastSelected.toString()); 1130 if (fc.isTraversable(f)) 1131 { 1132 fc.setCurrentDirectory(f); 1133 fc.rescanCurrentDirectory(); 1134 } 1135 else 1136 { 1137 fc.setSelectedFile(f); 1138 fc.approveSelection(); 1139 closeDialog(); 1140 } 1141 } 1142 else 1143 { 1144 if (startEditing) 1145 completeEditing(); 1146 String path = selVal.toString(); 1147 File f = fsv.createFileObject(path); 1148 fc.setSelectedFile(f); 1149 if (fc.isTraversable(f)) 1150 { 1151 setDirectorySelected(true); 1152 setDirectory(f); 1153 } 1154 else 1155 { 1156 setDirectorySelected(false); 1157 setDirectory(null); 1158 } 1159 lastSelected = selVal; 1160 if (f.isFile()) 1161 setFileName(path.substring(path.lastIndexOf("/") + 1)); 1162 else if (fc.getFileSelectionMode() != JFileChooser.FILES_ONLY) 1163 setFileName(path); 1164 } 1165 fileTable.repaint(); 1166 } 1167 1168 /** 1169 * Sets up the text editor for the current file. 1170 * 1171 * @param row - 1172 * the current row of the item in the list to be edited. 1173 */ 1174 void editFile(int row) 1175 { 1176 Rectangle bounds = table.getCellRect(row, 0, true); 1177 table.scrollRectToVisible(bounds); 1178 if (editFile.canWrite()) 1179 { 1180 startEditing = true; 1181 editField = new JTextField(editFile.getName()); 1182 editField.addActionListener(new EditingActionListener()); 1183 1184 // Need to adjust y pos 1185 bounds.y = row * table.getRowHeight(); 1186 editField.setBounds(bounds); 1187 1188 table.add(editField); 1189 1190 editField.requestFocus(); 1191 editField.selectAll(); 1192 } 1193 else 1194 completeEditing(); 1195 table.repaint(); 1196 } 1197 1198 /** 1199 * Completes the editing. 1200 */ 1201 void completeEditing() 1202 { 1203 if (editField != null && editFile != null) 1204 { 1205 String text = editField.getText(); 1206 if (text != null && text != "" && !text.equals(fc.getName(editFile))) 1207 if (editFile.renameTo(fc.getFileSystemView().createFileObject( 1208 fc.getCurrentDirectory(), text))) 1209 rescanCurrentDirectory(fc); 1210 table.remove(editField); 1211 } 1212 startEditing = false; 1213 editFile = null; 1214 editField = null; 1215 table.repaint(); 1216 } 1217 1218 /** 1219 * ActionListener for the editing text field. 1220 */ 1221 class EditingActionListener implements ActionListener 1222 { 1223 1224 /** 1225 * This method is invoked when an action occurs. 1226 * 1227 * @param e - 1228 * the <code>ActionEvent</code> that occurred 1229 */ 1230 public void actionPerformed(ActionEvent e) 1231 { 1232 if (editField != null) 1233 completeEditing(); 1234 } 1235 } 1236 1237 /** 1238 * Closes the dialog. 1239 */ 1240 public void closeDialog() 1241 { 1242 Window owner = SwingUtilities.windowForComponent(fc); 1243 if (owner instanceof JDialog) 1244 ((JDialog) owner).dispose(); 1245 } 1246 } 1247 1248 /** The text for a label describing the directory combo box. */ 1249 private String directoryLabel; 1250 1251 private JComboBox directoryComboBox; 1252 1253 /** The model for the directory combo box. */ 1254 DirectoryComboBoxModel directoryModel; 1255 1256 /** The text for a label describing the file text field. */ 1257 private String fileLabel; 1258 1259 /** The file name text field. */ 1260 private JTextField fileTextField; 1261 1262 /** The text for a label describing the filter combo box. */ 1263 private String filterLabel; 1264 1265 /** 1266 * The top panel (contains the directory combo box and the control buttons). 1267 */ 1268 private JPanel topPanel; 1269 1270 /** A panel containing the control buttons ('up', 'home' etc.). */ 1271 private JPanel controls; 1272 1273 /** 1274 * The panel that contains the filename field and the filter combobox. 1275 */ 1276 private JPanel bottomPanel; 1277 1278 /** 1279 * The panel that contains the 'Open' (or 'Save') and 'Cancel' buttons. 1280 */ 1281 private JPanel buttonPanel; 1282 1283 private JButton approveButton; 1284 1285 /** The file list. */ 1286 JList fileList; 1287 1288 /** The file table. */ 1289 JTable fileTable; 1290 1291 /** The panel containing the file list. */ 1292 JPanel fileListPanel; 1293 1294 /** The panel containing the file table. */ 1295 JPanel fileTablePanel; 1296 1297 /** The filter combo box model. */ 1298 private FilterComboBoxModel filterModel; 1299 1300 /** The action map. */ 1301 private ActionMap actionMap; 1302 1303 /** True if currently in list view. */ 1304 boolean listView; 1305 1306 /** True if we can or have started editing a cell. */ 1307 boolean startEditing; 1308 1309 /** The scrollpane used for the table and list. */ 1310 JScrollPane scrollPane; 1311 1312 /** The text for the label when saving. */ 1313 String save; 1314 1315 /** The text for the label when opening a directory. */ 1316 String look; 1317 1318 /** The label for the file combo box. */ 1319 JLabel dirLabel; 1320 1321 /** Listeners. */ 1322 ListSelectionListener listSelList; 1323 MouseListener doubleClickList; 1324 SingleClickListener singleClickList; 1325 TableClickListener tableClickList; 1326 1327 /** 1328 * A factory method that returns a UI delegate for the specified 1329 * component. 1330 * 1331 * @param c the component (which should be a {@link JFileChooser}). 1332 */ 1333 public static ComponentUI createUI(JComponent c) 1334 { 1335 JFileChooser chooser = (JFileChooser) c; 1336 return new MetalFileChooserUI(chooser); 1337 } 1338 1339 /** 1340 * Creates a new instance of this UI delegate. 1341 * 1342 * @param filechooser the file chooser component. 1343 */ 1344 public MetalFileChooserUI(JFileChooser filechooser) 1345 { 1346 super(filechooser); 1347 bottomPanel = new JPanel(new GridLayout(3, 2)); 1348 buttonPanel = new JPanel(); 1349 } 1350 1351 public void installUI(JComponent c) 1352 { 1353 super.installUI(c); 1354 actionMap = createActionMap(); 1355 } 1356 1357 public void uninstallUI(JComponent c) 1358 { 1359 super.uninstallUI(c); 1360 actionMap = null; 1361 } 1362 1363 /** 1364 * Installs the sub-components of the file chooser. 1365 * 1366 * @param fc the file chooser component. 1367 */ 1368 public void installComponents(JFileChooser fc) 1369 { 1370 fc.setLayout(new BorderLayout()); 1371 topPanel = new JPanel(new BorderLayout()); 1372 dirLabel = new JLabel(directoryLabel); 1373 topPanel.add(dirLabel, BorderLayout.WEST); 1374 this.controls = new JPanel(); 1375 addControlButtons(); 1376 1377 JPanel dirPanel = new JPanel(new VerticalMidLayout()); 1378 directoryModel = createDirectoryComboBoxModel(fc); 1379 directoryComboBox = new JComboBox(directoryModel); 1380 directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc)); 1381 dirPanel.add(directoryComboBox); 1382 topPanel.add(dirPanel); 1383 topPanel.add(controls, BorderLayout.EAST); 1384 topPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 0, 8)); 1385 fc.add(topPanel, BorderLayout.NORTH); 1386 1387 JPanel list = createList(fc); 1388 list.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); 1389 fc.add(list, BorderLayout.CENTER); 1390 1391 JPanel bottomPanel = getBottomPanel(); 1392 filterModel = createFilterComboBoxModel(); 1393 JComboBox fileFilterCombo = new JComboBox(filterModel); 1394 fileFilterCombo.setRenderer(createFilterComboBoxRenderer()); 1395 1396 fileTextField = new JTextField(); 1397 JPanel fileNamePanel = new JPanel(new VerticalMidLayout()); 1398 fileNamePanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 5)); 1399 fileNamePanel.add(fileTextField); 1400 JPanel row1 = new JPanel(new BorderLayout()); 1401 row1.add(new JLabel(this.fileLabel), BorderLayout.WEST); 1402 row1.add(fileNamePanel); 1403 bottomPanel.add(row1); 1404 1405 JPanel row2 = new JPanel(new BorderLayout()); 1406 row2.add(new JLabel(this.filterLabel), BorderLayout.WEST); 1407 row2.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); 1408 row2.add(fileFilterCombo); 1409 bottomPanel.add(row2); 1410 JPanel buttonPanel = new JPanel(new ButtonLayout()); 1411 1412 approveButton = new JButton(getApproveSelectionAction()); 1413 approveButton.setText(getApproveButtonText(fc)); 1414 approveButton.setToolTipText(getApproveButtonToolTipText(fc)); 1415 approveButton.setMnemonic(getApproveButtonMnemonic(fc)); 1416 buttonPanel.add(approveButton); 1417 buttonPanel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0)); 1418 1419 JButton cancelButton = new JButton(getCancelSelectionAction()); 1420 cancelButton.setText(cancelButtonText); 1421 cancelButton.setToolTipText(cancelButtonToolTipText); 1422 cancelButton.setMnemonic(cancelButtonMnemonic); 1423 buttonPanel.add(cancelButton); 1424 bottomPanel.add(buttonPanel, BorderLayout.SOUTH); 1425 bottomPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8)); 1426 fc.add(bottomPanel, BorderLayout.SOUTH); 1427 1428 fc.add(getAccessoryPanel(), BorderLayout.EAST); 1429 } 1430 1431 /** 1432 * Uninstalls the components added by 1433 * {@link #installComponents(JFileChooser)}. 1434 * 1435 * @param fc the file chooser. 1436 */ 1437 public void uninstallComponents(JFileChooser fc) 1438 { 1439 fc.remove(bottomPanel); 1440 bottomPanel = null; 1441 fc.remove(fileListPanel); 1442 fc.remove(fileTablePanel); 1443 fileTablePanel = null; 1444 fileListPanel = null; 1445 fc.remove(topPanel); 1446 topPanel = null; 1447 1448 directoryModel = null; 1449 fileTextField = null; 1450 directoryComboBox = null; 1451 } 1452 1453 /** 1454 * Returns the panel that contains the 'Open' (or 'Save') and 'Cancel' 1455 * buttons. 1456 * 1457 * @return The panel. 1458 */ 1459 protected JPanel getButtonPanel() 1460 { 1461 return buttonPanel; 1462 } 1463 1464 /** 1465 * Creates and returns a new panel that will be used for the controls at 1466 * the bottom of the file chooser. 1467 * 1468 * @return A new panel. 1469 */ 1470 protected JPanel getBottomPanel() 1471 { 1472 if (bottomPanel == null) 1473 bottomPanel = new JPanel(new GridLayout(3, 2)); 1474 return bottomPanel; 1475 } 1476 1477 /** 1478 * Fetches localised strings for use by the labels and buttons on the 1479 * file chooser. 1480 * 1481 * @param fc the file chooser. 1482 */ 1483 protected void installStrings(JFileChooser fc) 1484 { 1485 super.installStrings(fc); 1486 look = "Look In: "; 1487 save = "Save In: "; 1488 if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) 1489 directoryLabel = save; 1490 else 1491 directoryLabel = look; 1492 1493 fileLabel = "File Name: "; 1494 filterLabel = "Files of Type: "; 1495 1496 this.cancelButtonMnemonic = 0; 1497 this.cancelButtonText = "Cancel"; 1498 this.cancelButtonToolTipText = "Abort file chooser dialog"; 1499 1500 this.directoryOpenButtonMnemonic = 0; 1501 this.directoryOpenButtonText = "Open"; 1502 this.directoryOpenButtonToolTipText = "Open selected directory"; 1503 1504 this.helpButtonMnemonic = 0; 1505 this.helpButtonText = "Help"; 1506 this.helpButtonToolTipText = "Filechooser help"; 1507 1508 this.openButtonMnemonic = 0; 1509 this.openButtonText = "Open"; 1510 this.openButtonToolTipText = "Open selected file"; 1511 1512 this.saveButtonMnemonic = 0; 1513 this.saveButtonText = "Save"; 1514 this.saveButtonToolTipText = "Save selected file"; 1515 1516 this.updateButtonMnemonic = 0; 1517 this.updateButtonText = "Update"; 1518 this.updateButtonToolTipText = "Update directory listing"; 1519 } 1520 1521 /** 1522 * Installs the listeners required. 1523 * 1524 * @param fc the file chooser. 1525 */ 1526 protected void installListeners(JFileChooser fc) 1527 { 1528 directoryComboBox.setAction(new DirectoryComboBoxAction()); 1529 fc.addPropertyChangeListener(filterModel); 1530 listSelList = createListSelectionListener(fc); 1531 doubleClickList = this.createDoubleClickListener(fc, fileList); 1532 singleClickList = new SingleClickListener(fileList); 1533 fileList.addListSelectionListener(listSelList); 1534 fileList.addMouseListener(doubleClickList); 1535 fileList.addMouseListener(singleClickList); 1536 super.installListeners(fc); 1537 } 1538 1539 protected void uninstallListeners(JFileChooser fc) 1540 { 1541 super.uninstallListeners(fc); 1542 fc.removePropertyChangeListener(filterModel); 1543 directoryComboBox.setAction(null); 1544 fileList.removeListSelectionListener(listSelList); 1545 fileList.removeMouseListener(doubleClickList); 1546 fileList.removeMouseListener(singleClickList); 1547 1548 if (fileTable != null) 1549 fileTable.removeMouseListener(tableClickList); 1550 } 1551 1552 protected ActionMap getActionMap() 1553 { 1554 if (actionMap == null) 1555 actionMap = createActionMap(); 1556 return actionMap; 1557 } 1558 1559 /** 1560 * Creates and returns an action map. 1561 * 1562 * @return The action map. 1563 */ 1564 protected ActionMap createActionMap() 1565 { 1566 ActionMap map = new ActionMap(); 1567 map.put("approveSelection", getApproveSelectionAction()); 1568 map.put("cancelSelection", getCancelSelectionAction()); 1569 map.put("Go Up", getChangeToParentDirectoryAction()); 1570 return map; 1571 } 1572 1573 /** 1574 * Creates a panel containing a list of files. 1575 * 1576 * @param fc the file chooser. 1577 * 1578 * @return A panel. 1579 */ 1580 protected JPanel createList(JFileChooser fc) 1581 { 1582 if (fileList == null) 1583 { 1584 fileListPanel = new JPanel(new BorderLayout()); 1585 fileList = new JList(getModel()); 1586 scrollPane = new JScrollPane(fileList); 1587 fileList.setLayoutOrientation(JList.VERTICAL_WRAP); 1588 fileList.setCellRenderer(new FileRenderer()); 1589 } 1590 else 1591 { 1592 fileList.setModel(getModel()); 1593 fileListPanel.removeAll(); 1594 scrollPane.getViewport().setView(fileList); 1595 } 1596 fileListPanel.add(scrollPane); 1597 // This size was determined using BeanShell and dumping the JFileChooser 1598 // component hierarchy. Sun has an internal FilePane class in there, but 1599 // that probably doesn't matter atm. 1600 fileListPanel.setPreferredSize(new Dimension(405, 135)); 1601 return fileListPanel; 1602 } 1603 1604 /** 1605 * Creates a panel containing a table within a scroll pane. 1606 * 1607 * @param fc the file chooser. 1608 * 1609 * @return The details view. 1610 */ 1611 protected JPanel createDetailsView(JFileChooser fc) 1612 { 1613 fileTablePanel = new JPanel(new BorderLayout()); 1614 1615 Object[] cols = new Object[] {"Name", "Size", "Modified"}; 1616 Object[][] rows = new Object[fileList.getModel().getSize()][3]; 1617 1618 fileTable = new JTable(new DefaultTableModel(rows, cols)); 1619 1620 if (fc.isMultiSelectionEnabled()) 1621 fileTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 1622 else 1623 fileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 1624 1625 fileTable.setShowGrid(false); 1626 fileTable.setColumnSelectionAllowed(false); 1627 fileTable.setDefaultRenderer(Object.class, new TableFileRenderer()); 1628 1629 tableClickList = new TableClickListener(fileTable, fc); 1630 fileTable.addMouseListener(tableClickList); 1631 1632 return updateTable(); 1633 } 1634 1635 /** 1636 * Sets the values in the table, and puts it in the panel. 1637 * 1638 * @return the panel containing the table. 1639 */ 1640 JPanel updateTable() 1641 { 1642 DefaultTableModel mod = (DefaultTableModel) fileTable.getModel(); 1643 ListModel lm = fileList.getModel(); 1644 DateFormat dt = DateFormat.getDateTimeInstance(DateFormat.SHORT, 1645 DateFormat.SHORT); 1646 File curr = null; 1647 int size = lm.getSize(); 1648 int rc = mod.getRowCount(); 1649 1650 // If there are not enough rows 1651 for (int x = rc; x < size; x++) 1652 mod.addRow(new Object[3]); 1653 1654 for (int i = 0; i < size; i++) 1655 { 1656 curr = (File) lm.getElementAt(i); 1657 fileTable.setValueAt(curr.getName(), i, 0); 1658 fileTable.setValueAt(formatSize(curr.length()), i, 1); 1659 fileTable.setValueAt(dt.format(new Date(curr.lastModified())), i, 2); 1660 } 1661 1662 // If there are too many rows 1663 while (rc > size) 1664 mod.removeRow(--rc); 1665 1666 scrollPane.getViewport().setView(fileTable); 1667 scrollPane.setColumnHeaderView(fileTable.getTableHeader()); 1668 1669 fileTablePanel.removeAll(); 1670 fileTablePanel.add(scrollPane); 1671 1672 return fileTablePanel; 1673 } 1674 1675 /** 1676 * Formats bytes into the appropriate size. 1677 * 1678 * @param bytes the number of bytes to convert 1679 * @return a string representation of the size 1680 */ 1681 private String formatSize(long bytes) 1682 { 1683 NumberFormat nf = NumberFormat.getNumberInstance(); 1684 long mb = (long) Math.pow(2, 20); 1685 long kb = (long) Math.pow(2, 10); 1686 long gb = (long) Math.pow(2, 30); 1687 double size = 0; 1688 String id = ""; 1689 1690 if ((bytes / gb) >= 1) 1691 { 1692 size = (double) bytes / (double) gb; 1693 id = "GB"; 1694 } 1695 else if ((bytes / mb) >= 1) 1696 { 1697 size = (double) bytes / (double) mb; 1698 id = "MB"; 1699 } 1700 else if ((bytes / kb) >= 1) 1701 { 1702 size = (double) bytes / (double) kb; 1703 id = "KB"; 1704 } 1705 else 1706 { 1707 size = bytes; 1708 id = "Bytes"; 1709 } 1710 1711 return nf.format(size) + " " + id; 1712 } 1713 /** 1714 * Creates a listener that monitors selections in the directory/file list 1715 * and keeps the {@link JFileChooser} component up to date. 1716 * 1717 * @param fc the file chooser. 1718 * 1719 * @return The listener. 1720 * 1721 * @see #installListeners(JFileChooser) 1722 */ 1723 public ListSelectionListener createListSelectionListener(JFileChooser fc) 1724 { 1725 return new MetalFileChooserSelectionListener(); 1726 } 1727 1728 /** 1729 * Returns the preferred size for the file chooser component. 1730 * 1731 * @return The preferred size. 1732 */ 1733 public Dimension getPreferredSize(JComponent c) 1734 { 1735 Dimension tp = topPanel.getPreferredSize(); 1736 Dimension bp = bottomPanel.getPreferredSize(); 1737 Dimension fl = fileListPanel.getPreferredSize(); 1738 return new Dimension(fl.width, tp.height + bp.height + fl.height); 1739 } 1740 1741 /** 1742 * Returns the minimum size for the file chooser component. 1743 * 1744 * @return The minimum size. 1745 */ 1746 public Dimension getMinimumSize(JComponent c) 1747 { 1748 Dimension tp = topPanel.getMinimumSize(); 1749 Dimension bp = bottomPanel.getMinimumSize(); 1750 Dimension fl = fileListPanel.getMinimumSize(); 1751 return new Dimension(fl.width, tp.height + bp.height + fl.height); 1752 } 1753 1754 /** 1755 * Returns the maximum size for the file chooser component. 1756 * 1757 * @return The maximum size. 1758 */ 1759 public Dimension getMaximumSize(JComponent c) 1760 { 1761 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 1762 } 1763 1764 /** 1765 * Creates a property change listener that monitors the {@link JFileChooser} 1766 * for property change events and updates the component display accordingly. 1767 * 1768 * @param fc the file chooser. 1769 * 1770 * @return The property change listener. 1771 * 1772 * @see #installListeners(JFileChooser) 1773 */ 1774 public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) 1775 { 1776 return new MetalFileChooserPropertyChangeListener(); 1777 } 1778 1779 /** 1780 * Creates and returns a new instance of {@link DirectoryComboBoxModel}. 1781 * 1782 * @return A new instance of {@link DirectoryComboBoxModel}. 1783 */ 1784 protected MetalFileChooserUI.DirectoryComboBoxModel 1785 createDirectoryComboBoxModel(JFileChooser fc) 1786 { 1787 return new DirectoryComboBoxModel(); 1788 } 1789 1790 /** 1791 * Creates a new instance of the renderer used in the directory 1792 * combo box. 1793 * 1794 * @param fc the file chooser. 1795 * 1796 * @return The renderer. 1797 */ 1798 protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer( 1799 JFileChooser fc) 1800 { 1801 return new DirectoryComboBoxRenderer(fc); 1802 } 1803 1804 /** 1805 * Creates and returns a new instance of {@link FilterComboBoxModel}. 1806 * 1807 * @return A new instance of {@link FilterComboBoxModel}. 1808 */ 1809 protected FilterComboBoxModel createFilterComboBoxModel() 1810 { 1811 return new FilterComboBoxModel(); 1812 } 1813 1814 /** 1815 * Creates and returns a new instance of {@link FilterComboBoxRenderer}. 1816 * 1817 * @return A new instance of {@link FilterComboBoxRenderer}. 1818 */ 1819 protected MetalFileChooserUI.FilterComboBoxRenderer 1820 createFilterComboBoxRenderer() 1821 { 1822 return new FilterComboBoxRenderer(); 1823 } 1824 1825 /** 1826 * Adds the control buttons ('up', 'home' etc.) to the panel. 1827 */ 1828 protected void addControlButtons() 1829 { 1830 JButton upButton = new JButton(getChangeToParentDirectoryAction()); 1831 upButton.setText(null); 1832 upButton.setIcon(this.upFolderIcon); 1833 upButton.setMargin(new Insets(0, 0, 0, 0)); 1834 controls.add(upButton); 1835 1836 JButton homeButton = new JButton(getGoHomeAction()); 1837 homeButton.setText(null); 1838 homeButton.setIcon(this.homeFolderIcon); 1839 homeButton.setMargin(new Insets(0, 0, 0, 0)); 1840 controls.add(homeButton); 1841 1842 JButton newFolderButton = new JButton(getNewFolderAction()); 1843 newFolderButton.setText(null); 1844 newFolderButton.setIcon(this.newFolderIcon); 1845 newFolderButton.setMargin(new Insets(0, 0, 0, 0)); 1846 controls.add(newFolderButton); 1847 1848 JToggleButton listButton = new JToggleButton(this.listViewIcon); 1849 listButton.setMargin(new Insets(0, 0, 0, 0)); 1850 listButton.addActionListener(new ListViewActionListener()); 1851 listButton.setSelected(true); 1852 listView = true; 1853 controls.add(listButton); 1854 1855 JToggleButton detailButton = new JToggleButton(this.detailsViewIcon); 1856 detailButton.setMargin(new Insets(0, 0, 0, 0)); 1857 detailButton.addActionListener(new DetailViewActionListener()); 1858 detailButton.setSelected(false); 1859 controls.add(detailButton); 1860 1861 ButtonGroup buttonGroup = new ButtonGroup(); 1862 buttonGroup.add(listButton); 1863 buttonGroup.add(detailButton); 1864 } 1865 1866 /** 1867 * Removes all the buttons from the control panel. 1868 */ 1869 protected void removeControlButtons() 1870 { 1871 controls.removeAll(); 1872 controls.revalidate(); 1873 controls.repaint(); 1874 } 1875 1876 /** 1877 * Updates the current directory. 1878 * 1879 * @param fc the file chooser to update. 1880 */ 1881 public void rescanCurrentDirectory(JFileChooser fc) 1882 { 1883 directoryModel.setSelectedItem(fc.getCurrentDirectory()); 1884 getModel().validateFileCache(); 1885 if (!listView) 1886 updateTable(); 1887 else 1888 createList(fc); 1889 } 1890 1891 /** 1892 * Returns the file name in the text field. 1893 * 1894 * @return The file name. 1895 */ 1896 public String getFileName() 1897 { 1898 String result = null; 1899 if (fileTextField != null) 1900 result = fileTextField.getText(); 1901 return result; 1902 } 1903 1904 /** 1905 * Sets the file name in the text field. 1906 * 1907 * @param filename the file name. 1908 */ 1909 public void setFileName(String filename) 1910 { 1911 fileTextField.setText(filename); 1912 } 1913 1914 /** 1915 * DOCUMENT ME!! 1916 * 1917 * @param e - DOCUMENT ME! 1918 */ 1919 public void valueChanged(ListSelectionEvent e) 1920 { 1921 // FIXME: Not sure what we should be doing here, if anything. 1922 } 1923 1924 /** 1925 * Returns the approve button. 1926 * 1927 * @return The approve button. 1928 */ 1929 protected JButton getApproveButton(JFileChooser fc) 1930 { 1931 return approveButton; 1932 } 1933 1934 /** 1935 * A layout manager that is used to arrange the subcomponents of the 1936 * {@link JFileChooser}. 1937 */ 1938 class VerticalMidLayout implements LayoutManager 1939 { 1940 /** 1941 * Performs the layout. 1942 * 1943 * @param parent the container. 1944 */ 1945 public void layoutContainer(Container parent) 1946 { 1947 int count = parent.getComponentCount(); 1948 if (count > 0) 1949 { 1950 Insets insets = parent.getInsets(); 1951 Component c = parent.getComponent(0); 1952 Dimension prefSize = c.getPreferredSize(); 1953 int h = parent.getHeight() - insets.top - insets.bottom; 1954 int adj = Math.max(0, (h - prefSize.height) / 2); 1955 c.setBounds(insets.left, insets.top + adj, parent.getWidth() 1956 - insets.left - insets.right, 1957 (int) Math.min(prefSize.getHeight(), h)); 1958 } 1959 } 1960 1961 /** 1962 * Returns the minimum layout size. 1963 * 1964 * @param parent the container. 1965 * 1966 * @return The minimum layout size. 1967 */ 1968 public Dimension minimumLayoutSize(Container parent) 1969 { 1970 return preferredLayoutSize(parent); 1971 } 1972 1973 /** 1974 * Returns the preferred layout size. 1975 * 1976 * @param parent the container. 1977 * 1978 * @return The preferred layout size. 1979 */ 1980 public Dimension preferredLayoutSize(Container parent) 1981 { 1982 if (parent.getComponentCount() > 0) 1983 { 1984 return parent.getComponent(0).getPreferredSize(); 1985 } 1986 else return null; 1987 } 1988 1989 /** 1990 * This layout manager does not need to track components, so this 1991 * method does nothing. 1992 * 1993 * @param name the name the component is associated with. 1994 * @param component the component. 1995 */ 1996 public void addLayoutComponent(String name, Component component) 1997 { 1998 // do nothing 1999 } 2000 2001 /** 2002 * This layout manager does not need to track components, so this 2003 * method does nothing. 2004 * 2005 * @param component the component. 2006 */ 2007 public void removeLayoutComponent(Component component) 2008 { 2009 // do nothing 2010 } 2011 } 2012 2013 /** 2014 * A layout manager that is used to arrange buttons for the 2015 * {@link JFileChooser}. 2016 */ 2017 class ButtonLayout implements LayoutManager 2018 { 2019 static final int GAP = 4; 2020 2021 /** 2022 * Performs the layout. 2023 * 2024 * @param parent the container. 2025 */ 2026 public void layoutContainer(Container parent) 2027 { 2028 int count = parent.getComponentCount(); 2029 if (count > 0) 2030 { 2031 // first find the widest button 2032 int maxW = 0; 2033 for (int i = 0; i < count; i++) 2034 { 2035 Component c = parent.getComponent(i); 2036 Dimension prefSize = c.getPreferredSize(); 2037 maxW = Math.max(prefSize.width, maxW); 2038 } 2039 2040 // then position the buttons 2041 Insets insets = parent.getInsets(); 2042 int availableH = parent.getHeight() - insets.top - insets.bottom; 2043 int currentX = parent.getWidth() - insets.right; 2044 for (int i = count - 1; i >= 0; i--) 2045 { 2046 Component c = parent.getComponent(i); 2047 Dimension prefSize = c.getPreferredSize(); 2048 int adj = Math.max(0, (availableH - prefSize.height) / 2); 2049 currentX = currentX - prefSize.width; 2050 c.setBounds(currentX, insets.top + adj, prefSize.width, 2051 (int) Math.min(prefSize.getHeight(), availableH)); 2052 currentX = currentX - GAP; 2053 } 2054 } 2055 } 2056 2057 /** 2058 * Returns the minimum layout size. 2059 * 2060 * @param parent the container. 2061 * 2062 * @return The minimum layout size. 2063 */ 2064 public Dimension minimumLayoutSize(Container parent) 2065 { 2066 return preferredLayoutSize(parent); 2067 } 2068 2069 /** 2070 * Returns the preferred layout size. 2071 * 2072 * @param parent the container. 2073 * 2074 * @return The preferred layout size. 2075 */ 2076 public Dimension preferredLayoutSize(Container parent) 2077 { 2078 Insets insets = parent.getInsets(); 2079 int maxW = 0; 2080 int maxH = 0; 2081 int count = parent.getComponentCount(); 2082 if (count > 0) 2083 { 2084 for (int i = 0; i < count; i++) 2085 { 2086 Component c = parent.getComponent(i); 2087 Dimension d = c.getPreferredSize(); 2088 maxW = Math.max(d.width, maxW); 2089 maxH = Math.max(d.height, maxH); 2090 } 2091 } 2092 return new Dimension(maxW * count + GAP * (count - 1) + insets.left 2093 + insets.right, maxH + insets.top + insets.bottom); 2094 } 2095 2096 /** 2097 * This layout manager does not need to track components, so this 2098 * method does nothing. 2099 * 2100 * @param name the name the component is associated with. 2101 * @param component the component. 2102 */ 2103 public void addLayoutComponent(String name, Component component) 2104 { 2105 // do nothing 2106 } 2107 2108 /** 2109 * This layout manager does not need to track components, so this 2110 * method does nothing. 2111 * 2112 * @param component the component. 2113 */ 2114 public void removeLayoutComponent(Component component) 2115 { 2116 // do nothing 2117 } 2118 } 2119 2120}