/******************************************************************************* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tiny Look and Feel * * (C) Copyright 2003 - 2007 Hans Bickel * * For * licensing information and credits, please refer to the * comment in file * de.muntjak.tinylookandfeel.TinyLookAndFeel * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * This is an almost unchanged version of MetalFileChooserUI. * @(#)MetalFileChooserUI.java 1.45 02/04/11 Copyright 2002 Sun Microsystems, * Inc. All rights reserved. SUN PROPRIETARY/CONFIDENTIAL. Use is subject to * license terms. */ package de.muntjak.tinylookandfeel; import java.awt.BorderLayout; import java.awt.Component; import java.awt.ComponentOrientation; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.text.DateFormat; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.EventObject; import java.util.Iterator; import java.util.Locale; import java.util.Vector; import javax.swing.AbstractAction; import javax.swing.AbstractListModel; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.ComboBoxModel; import javax.swing.DefaultCellEditor; import javax.swing.DefaultListCellRenderer; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JToggleButton; import javax.swing.KeyStroke; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileSystemView; import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.TableHeaderUI; import javax.swing.plaf.basic.BasicDirectoryModel; import javax.swing.plaf.basic.BasicFileChooserUI; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import javax.swing.text.Position; import sun.awt.shell.ShellFolder; import de.muntjak.tinylookandfeel.borders.TinyToolButtonBorder; import de.muntjak.tinylookandfeel.table.SortableTableData; /** * TinyFileChooserUI is a customized MetalFileChooserUI. * * @author Hans Bickel * @version 1.3.7 */ @SuppressWarnings ( { "all" } ) public class TinyFileChooserUI extends BasicFileChooserUI { /** Key for a button's isFileChooserButton client property */ public static final String IS_FILE_CHOOSER_BUTTON_KEY = "JFileChooser.isFileChooserButton"; // This one is evaluated at ensureFileIsVisible() and // will only be false while DetailsTableModel restores // the table selection private boolean doScrolling = true; private JPanel centerPanel; private TinyDirectoryModel directoryModel = null; private JLabel lookInLabel; private JComboBox directoryComboBox; private DirectoryComboBoxModel directoryComboBoxModel; private Action directoryComboBoxAction = new DirectoryComboBoxAction (); private FilterComboBoxModel filterComboBoxModel; private JTextField fileNameTextField; private JToggleButton listViewButton; private JToggleButton detailsViewButton; private JPanel listViewPanel; private JPanel detailsViewPanel; private JPanel currentViewPanel; private FocusListener editorFocusListener = new FocusAdapter () { public void focusLost ( FocusEvent e ) { if ( !e.isTemporary () ) { applyEdit (); } } }; private boolean useShellFolder; private ListSelectionModel listSelectionModel; private JList list; private JTable detailsTable; private DetailsTableModel detailsTableModel; private JButton approveButton; private JButton cancelButton; private JPanel buttonPanel; private JPanel bottomPanel; private JComboBox filterComboBox; private static final Dimension hstrut1 = new Dimension ( 1, 1 ); private static final Dimension hstrut4 = new Dimension ( 4, 1 ); private static final Dimension hstrut11 = new Dimension ( 11, 1 ); private static final Dimension vstrut5 = new Dimension ( 1, 5 ); private static final Insets shrinkwrap = new Insets ( 2, 2, 2, 2 ); // Preferred and Minimum sizes for the dialog box private static int PREF_WIDTH = 500; private static int PREF_HEIGHT = 326; private static Dimension PREF_SIZE = new Dimension ( PREF_WIDTH, PREF_HEIGHT ); private static int MIN_WIDTH = 500; private static int MIN_HEIGHT = 326; private static Dimension MIN_SIZE = new Dimension ( MIN_WIDTH, MIN_HEIGHT ); private static int LIST_PREF_WIDTH = 405; private static int LIST_PREF_HEIGHT = 183; private static Dimension LIST_PREF_SIZE = new Dimension ( LIST_PREF_WIDTH, LIST_PREF_HEIGHT ); private static final int COLUMN_FILENAME = 0; private static final int COLUMN_FILESIZE = 1; private static final int COLUMN_FILETYPE = 2; private static final int COLUMN_FILEDATE = 3; // private static final int COLUMN_FILEATTR = 4; private static final int COLUMN_COLCOUNT = 4; // name, size, type, last modified, attributes private int [] COLUMN_WIDTHS = { 150, 75, 130, 100 }; // Labels, mnemonics, and tooltips (oh my!) private int lookInLabelMnemonic = 0; private String lookInLabelText = null; private String saveInLabelText = null; private int fileNameLabelMnemonic = 0; private String fileNameLabelText = null; private int filesOfTypeLabelMnemonic = 0; private String filesOfTypeLabelText = null; private String upFolderToolTipText = null; private String upFolderAccessibleName = null; private String homeFolderToolTipText = null; private String homeFolderAccessibleName = null; private String newFolderToolTipText = null; private String newFolderAccessibleName = null; private String listViewButtonToolTipText = null; private String listViewButtonAccessibleName = null; private String detailsViewButtonToolTipText = null; private String detailsViewButtonAccessibleName = null; private String fileNameHeaderText = null; private String fileSizeHeaderText = null; private String fileTypeHeaderText = null; private String fileDateHeaderText = null; private String fileAttrHeaderText = null; // // ComponentUI Interface Implementation methods // public static ComponentUI createUI ( JComponent c ) { return new TinyFileChooserUI ( ( JFileChooser ) c ); } public TinyFileChooserUI ( JFileChooser filechooser ) { super ( filechooser ); } public void uninstallComponents ( JFileChooser fc ) { fc.removeAll (); bottomPanel = null; buttonPanel = null; } public void installComponents ( JFileChooser fc ) { FileSystemView fsv = fc.getFileSystemView (); fc.setBorder ( new EmptyBorder ( 12, 12, 11, 11 ) ); fc.setLayout ( new BorderLayout ( 0, 11 ) ); // ********************************* // // **** Construct the top panel **** // // ********************************* // // Directory manipulation buttons JPanel topPanel = new JPanel ( new BorderLayout ( 11, 0 ) ); JPanel topButtonPanel = new JPanel (); topButtonPanel .setLayout ( new BoxLayout ( topButtonPanel, BoxLayout.X_AXIS ) ); topPanel.add ( topButtonPanel, BorderLayout.EAST ); // Add the top panel to the fileChooser fc.add ( topPanel, BorderLayout.NORTH ); // ComboBox Label lookInLabel = new JLabel ( lookInLabelText ); lookInLabel.setDisplayedMnemonic ( lookInLabelMnemonic ); topPanel.add ( lookInLabel, BorderLayout.BEFORE_LINE_BEGINS ); // CurrentDir ComboBox directoryComboBox = new JComboBox (); directoryComboBox.putClientProperty ( "JComboBox.lightweightKeyboardNavigation", "Lightweight" ); lookInLabel.setLabelFor ( directoryComboBox ); directoryComboBoxModel = createDirectoryComboBoxModel ( fc ); directoryComboBox.setModel ( directoryComboBoxModel ); directoryComboBox.addActionListener ( directoryComboBoxAction ); directoryComboBox.setRenderer ( createDirectoryComboBoxRenderer ( fc ) ); directoryComboBox.setAlignmentX ( JComponent.LEFT_ALIGNMENT ); directoryComboBox.setAlignmentY ( JComponent.TOP_ALIGNMENT ); directoryComboBox.setMaximumRowCount ( 8 ); topPanel.add ( directoryComboBox, BorderLayout.CENTER ); Border toolButtonBorder = new TinyToolButtonBorder (); // Up Button Insets margin = new Insets ( 2, 2, 2, 2 ); JButton b = new JButton ( upFolderIcon ); b.putClientProperty ( IS_FILE_CHOOSER_BUTTON_KEY, Boolean.TRUE ); b.setOpaque ( false ); b.setText ( null ); b.setIcon ( upFolderIcon ); b.setToolTipText ( upFolderToolTipText ); b.getAccessibleContext ().setAccessibleName ( upFolderAccessibleName ); b.setAlignmentX ( JComponent.LEFT_ALIGNMENT ); b.setAlignmentY ( JComponent.CENTER_ALIGNMENT ); b.setMargin ( margin ); b.addActionListener ( getChangeToParentDirectoryAction () ); b.setBorder ( toolButtonBorder ); topButtonPanel.add ( b ); // Home Button File homeDir = fsv.getHomeDirectory (); String toolTipText = homeFolderToolTipText; if ( fsv.isRoot ( homeDir ) ) { toolTipText = getFileView ( fc ).getName ( homeDir ); // Probably // "Desktop". } b = new JButton ( homeFolderIcon ); b.putClientProperty ( IS_FILE_CHOOSER_BUTTON_KEY, Boolean.TRUE ); b.setToolTipText ( toolTipText ); b.getAccessibleContext ().setAccessibleName ( homeFolderAccessibleName ); b.setAlignmentX ( JComponent.LEFT_ALIGNMENT ); b.setAlignmentY ( JComponent.CENTER_ALIGNMENT ); b.setMargin ( margin ); b.addActionListener ( getGoHomeAction () ); b.setBorder ( toolButtonBorder ); topButtonPanel.add ( b ); // New Directory Button if ( !UIManager.getBoolean ( "FileChooser.readOnly" ) ) { b = new JButton ( newFolderIcon ); b.putClientProperty ( IS_FILE_CHOOSER_BUTTON_KEY, Boolean.TRUE ); b.setText ( null ); b.setToolTipText ( newFolderToolTipText ); b.getAccessibleContext ().setAccessibleName ( newFolderAccessibleName ); b.setAlignmentX ( JComponent.LEFT_ALIGNMENT ); b.setAlignmentY ( JComponent.CENTER_ALIGNMENT ); b.setMargin ( margin ); b.addActionListener ( getNewFolderAction () ); b.setBorder ( toolButtonBorder ); topButtonPanel.add ( b ); } topButtonPanel.add ( Box.createRigidArea ( hstrut1 ) ); // View button group ButtonGroup viewButtonGroup = new ButtonGroup (); class ViewButtonListener implements ActionListener { JFileChooser fc; ViewButtonListener ( JFileChooser fc ) { this.fc = fc; } public void actionPerformed ( ActionEvent e ) { JToggleButton b = ( JToggleButton ) e.getSource (); JPanel oldViewPanel = currentViewPanel; if ( b == detailsViewButton ) { if ( detailsViewPanel == null ) { detailsViewPanel = createDetailsView ( fc ); detailsViewPanel.setPreferredSize ( LIST_PREF_SIZE ); } currentViewPanel = detailsViewPanel; } else { currentViewPanel = listViewPanel; } if ( currentViewPanel != oldViewPanel ) { centerPanel.remove ( oldViewPanel ); centerPanel.add ( currentViewPanel, BorderLayout.CENTER ); centerPanel.revalidate (); centerPanel.repaint (); } } } ViewButtonListener viewButtonListener = new ViewButtonListener ( fc ); // List Button listViewButton = new JToggleButton ( listViewIcon ); listViewButton .putClientProperty ( IS_FILE_CHOOSER_BUTTON_KEY, Boolean.TRUE ); listViewButton.setToolTipText ( listViewButtonToolTipText ); listViewButton.getAccessibleContext ().setAccessibleName ( listViewButtonAccessibleName ); listViewButton.setSelected ( true ); listViewButton.setAlignmentX ( JComponent.LEFT_ALIGNMENT ); listViewButton.setAlignmentY ( JComponent.CENTER_ALIGNMENT ); listViewButton.setMargin ( /* shrinkwrap */new Insets ( 4, 2, 5, 2 ) ); listViewButton.addActionListener ( viewButtonListener ); listViewButton.setBorder ( toolButtonBorder ); topButtonPanel.add ( listViewButton ); topButtonPanel.add ( Box.createRigidArea ( hstrut1 ) ); viewButtonGroup.add ( listViewButton ); // Details Button detailsViewButton = new JToggleButton ( detailsViewIcon ); detailsViewButton.putClientProperty ( IS_FILE_CHOOSER_BUTTON_KEY, Boolean.TRUE ); detailsViewButton.setToolTipText ( detailsViewButtonToolTipText ); detailsViewButton.getAccessibleContext ().setAccessibleName ( detailsViewButtonAccessibleName ); detailsViewButton.setAlignmentX ( JComponent.LEFT_ALIGNMENT ); detailsViewButton.setAlignmentY ( JComponent.CENTER_ALIGNMENT ); detailsViewButton.setMargin ( /* shrinkwrap */new Insets ( 4, 2, 5, 2 ) ); detailsViewButton.addActionListener ( viewButtonListener ); detailsViewButton.setBorder ( toolButtonBorder ); topButtonPanel.add ( detailsViewButton ); viewButtonGroup.add ( detailsViewButton ); // Use ShellFolder class to populate combobox only if // FileSystemView.getRoots() returns one folder and that is // the same as the first item in the ShellFolder combobox list. { useShellFolder = false; File [] roots = fsv.getRoots (); if ( roots != null && roots.length == 1 ) { File [] cbFolders = ( File [] ) ShellFolder .get ( "fileChooserComboBoxFolders" ); if ( cbFolders != null && cbFolders.length > 0 && roots [ 0 ] == cbFolders [ 0 ] ) { useShellFolder = true; } } } // ************************************** // // ******* Add the directory pane ******* // // ************************************** // centerPanel = new JPanel ( new BorderLayout () ); listViewPanel = createList ( fc ); listSelectionModel = list.getSelectionModel (); listViewPanel.setPreferredSize ( LIST_PREF_SIZE ); centerPanel.add ( listViewPanel, BorderLayout.CENTER ); currentViewPanel = listViewPanel; centerPanel.add ( getAccessoryPanel (), BorderLayout.AFTER_LINE_ENDS ); JComponent accessory = fc.getAccessory (); if ( accessory != null ) { getAccessoryPanel ().add ( accessory ); } fc.add ( centerPanel, BorderLayout.CENTER ); // ********************************** // // **** Construct the bottom panel ** // // ********************************** // JPanel bottomPanel = getBottomPanel (); bottomPanel.setLayout ( new BoxLayout ( bottomPanel, BoxLayout.Y_AXIS ) ); fc.add ( bottomPanel, BorderLayout.SOUTH ); // FileName label and textfield JPanel fileNamePanel = new JPanel (); fileNamePanel.setLayout ( new BoxLayout ( fileNamePanel, BoxLayout.LINE_AXIS ) ); bottomPanel.add ( fileNamePanel ); bottomPanel.add ( Box.createRigidArea ( vstrut5 ) ); AlignedLabel fileNameLabel = new AlignedLabel ( fileNameLabelText ); fileNameLabel.setDisplayedMnemonic ( fileNameLabelMnemonic ); fileNamePanel.add ( fileNameLabel ); fileNameTextField = new JTextField () { public Dimension getMaximumSize () { return new Dimension ( Short.MAX_VALUE, super.getPreferredSize ().height ); } }; fileNamePanel.add ( fileNameTextField ); fileNameLabel.setLabelFor ( fileNameTextField ); fileNameTextField.addFocusListener ( new FocusAdapter () { public void focusGained ( FocusEvent e ) { if ( !getFileChooser ().isMultiSelectionEnabled () ) { listSelectionModel.clearSelection (); } } } ); if ( fc.isMultiSelectionEnabled () ) { setFileName ( fileNameString ( fc.getSelectedFiles () ) ); } else { setFileName ( fileNameString ( fc.getSelectedFile () ) ); } // Filetype label and combobox JPanel filesOfTypePanel = new JPanel (); filesOfTypePanel.setLayout ( new BoxLayout ( filesOfTypePanel, BoxLayout.LINE_AXIS ) ); bottomPanel.add ( filesOfTypePanel ); AlignedLabel filesOfTypeLabel = new AlignedLabel ( filesOfTypeLabelText ); filesOfTypeLabel.setDisplayedMnemonic ( filesOfTypeLabelMnemonic ); filesOfTypePanel.add ( filesOfTypeLabel ); filterComboBoxModel = createFilterComboBoxModel (); fc.addPropertyChangeListener ( filterComboBoxModel ); filterComboBox = new JComboBox ( filterComboBoxModel ); filesOfTypeLabel.setLabelFor ( filterComboBox ); filterComboBox.setRenderer ( createFilterComboBoxRenderer () ); filesOfTypePanel.add ( filterComboBox ); // buttons getButtonPanel ().setLayout ( new ButtonAreaLayout () ); approveButton = new JButton ( getApproveButtonText ( fc ) ); // Note: Metal does not use mnemonics for approve and cancel approveButton.addActionListener ( getApproveSelectionAction () ); approveButton.setToolTipText ( getApproveButtonToolTipText ( fc ) ); getButtonPanel ().add ( approveButton ); cancelButton = new JButton ( cancelButtonText ); cancelButton.setToolTipText ( cancelButtonToolTipText ); cancelButton.addActionListener ( getCancelSelectionAction () ); getButtonPanel ().add ( cancelButton ); if ( fc.getControlButtonsAreShown () ) { addControlButtons (); } groupLabels ( new AlignedLabel [] { fileNameLabel, filesOfTypeLabel } ); } protected JPanel getButtonPanel () { if ( buttonPanel == null ) { buttonPanel = new JPanel (); } return buttonPanel; } protected JPanel getBottomPanel () { if ( bottomPanel == null ) { bottomPanel = new JPanel (); } return bottomPanel; } protected void installStrings ( JFileChooser fc ) { super.installStrings ( fc ); Locale l = fc.getLocale (); lookInLabelMnemonic = UIManager.getInt ( "FileChooser.lookInLabelMnemonic" ); lookInLabelText = UIManager.getString ( "FileChooser.lookInLabelText", l ); saveInLabelText = UIManager.getString ( "FileChooser.saveInLabelText", l ); fileNameLabelMnemonic = UIManager .getInt ( "FileChooser.fileNameLabelMnemonic" ); fileNameLabelText = UIManager.getString ( "FileChooser.fileNameLabelText", l ); filesOfTypeLabelMnemonic = UIManager .getInt ( "FileChooser.filesOfTypeLabelMnemonic" ); filesOfTypeLabelText = UIManager.getString ( "FileChooser.filesOfTypeLabelText", l ); upFolderToolTipText = UIManager.getString ( "FileChooser.upFolderToolTipText", l ); upFolderAccessibleName = UIManager.getString ( "FileChooser.upFolderAccessibleName", l ); homeFolderToolTipText = UIManager.getString ( "FileChooser.homeFolderToolTipText", l ); homeFolderAccessibleName = UIManager.getString ( "FileChooser.homeFolderAccessibleName", l ); newFolderToolTipText = UIManager.getString ( "FileChooser.newFolderToolTipText", l ); newFolderAccessibleName = UIManager.getString ( "FileChooser.newFolderAccessibleName", l ); listViewButtonToolTipText = UIManager.getString ( "FileChooser.listViewButtonToolTipText", l ); listViewButtonAccessibleName = UIManager.getString ( "FileChooser.listViewButtonAccessibleName", l ); detailsViewButtonToolTipText = UIManager.getString ( "FileChooser.detailsViewButtonToolTipText", l ); detailsViewButtonAccessibleName = UIManager.getString ( "FileChooser.detailsViewButtonAccessibleName", l ); fileNameHeaderText = UIManager.getString ( "FileChooser.fileNameHeaderText", l ); fileSizeHeaderText = UIManager.getString ( "FileChooser.fileSizeHeaderText", l ); fileTypeHeaderText = UIManager.getString ( "FileChooser.fileTypeHeaderText", l ); fileDateHeaderText = UIManager.getString ( "FileChooser.fileDateHeaderText", l ); fileAttrHeaderText = UIManager.getString ( "FileChooser.fileAttrHeaderText", l ); } protected void installListeners ( JFileChooser fc ) { super.installListeners ( fc ); ActionMap actionMap = getActionMap (); SwingUtilities.replaceUIActionMap ( fc, actionMap ); } protected ActionMap getActionMap () { return createActionMap (); } protected ActionMap createActionMap () { AbstractAction escAction = new AbstractAction () { public void actionPerformed ( ActionEvent e ) { if ( editFile != null ) { cancelEdit (); } else { getFileChooser ().cancelSelection (); } } public boolean isEnabled () { return getFileChooser ().isEnabled (); } }; ActionMap map = new ActionMapUIResource (); map.put ( "approveSelection", getApproveSelectionAction () ); map.put ( "cancelSelection", escAction ); map.put ( "Go Up", getChangeToParentDirectoryAction () ); return map; } protected JPanel createList ( JFileChooser fc ) { JPanel p = new JPanel ( new BorderLayout () ); final JFileChooser fileChooser = fc; list = new JList () { public int getNextMatch ( String prefix, int startIndex, Position.Bias bias ) { ListModel model = getModel (); int max = model.getSize (); if ( prefix == null || startIndex < 0 || startIndex >= max ) { throw new IllegalArgumentException (); } // start search from the next element before/after the selected element boolean backwards = ( bias == Position.Bias.Backward ); for ( int i = startIndex ; backwards ? i >= 0 : i < max ; i += ( backwards ? -1 : 1 ) ) { String filename = fileChooser.getName ( ( File ) model .getElementAt ( i ) ); if ( filename.regionMatches ( true, 0, prefix, 0, prefix.length () ) ) { return i; } } return -1; } }; list.setCellRenderer ( new FileRenderer () ); list.setLayoutOrientation ( JList.VERTICAL_WRAP ); list.setVisibleRowCount ( -1 ); if ( fc.isMultiSelectionEnabled () ) { list.setSelectionMode ( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION ); } else { list.setSelectionMode ( ListSelectionModel.SINGLE_SELECTION ); } list.setModel ( getModel () ); list.addListSelectionListener ( createListSelectionListener ( fc ) ); list.addMouseListener ( createDoubleClickListener ( fc, list ) ); list.addMouseListener ( createSingleClickListener ( fc, list ) ); getModel ().addListDataListener ( new ListDataListener () { public void contentsChanged ( ListDataEvent e ) { // Update the selection after JList has been updated new DelayedSelectionUpdater (); } public void intervalAdded ( ListDataEvent e ) { new DelayedSelectionUpdater (); } public void intervalRemoved ( ListDataEvent e ) { } } ); JScrollPane scrollpane = new JScrollPane ( list ); p.add ( scrollpane, BorderLayout.CENTER ); return p; } class DetailsTableModel extends AbstractTableModel implements ListDataListener, SortableTableData { String [] columnNames = { " " + fileNameHeaderText, fileSizeHeaderText, " " + fileTypeHeaderText, " " + fileDateHeaderText, " " + fileAttrHeaderText }; JFileChooser chooser; ListModel listModel; // Comparators are lazily created Comparator fileAttributeComparator; Comparator fileDateComparator; Comparator fileNameComparator; Comparator fileSizeComparator; Comparator fileTypeComparator; int sortingDirection; int [] sortColumns; int [] sortDirections; DetailsTableModel ( JFileChooser fc ) { this.chooser = fc; listModel = getModel (); listModel.addListDataListener ( this ); } public int getRowCount () { return listModel.getSize (); } public int getColumnCount () { return COLUMN_COLCOUNT; } public String getColumnName ( int column ) { return columnNames [ column ]; } public Class getColumnClass ( int column ) { switch ( column ) { case COLUMN_FILENAME : return File.class; case COLUMN_FILEDATE : return Date.class; case COLUMN_FILESIZE : return Long.class; case COLUMN_FILETYPE : // case COLUMN_FILEATTR : return String.class; default : return super.getColumnClass ( column ); } } public Object getValueAt ( int row, int col ) { // Note: It is very important to avoid getting info on drives, as // this will trigger "No disk in A:" and similar dialogs. // // Use (f.exists() && !chooser.getFileSystemView().isFileSystemRoot(f)) to // determine if it is safe to call methods directly on f. File f = ( File ) listModel.getElementAt ( row ); switch ( col ) { case COLUMN_FILENAME : return f; case COLUMN_FILESIZE : if ( !f.exists () || f.isDirectory () ) { return null; } return new Long ( f.length () ); case COLUMN_FILETYPE : if ( !f.exists () ) { return null; } return chooser.getFileSystemView ().getSystemTypeDescription ( f ); case COLUMN_FILEDATE : if ( !f.exists () || chooser.getFileSystemView ().isFileSystemRoot ( f ) ) { return null; } long time = f.lastModified (); return ( time == 0L ) ? null : new Date ( time ); // case COLUMN_FILEATTR : // if(!f.exists() || chooser.getFileSystemView().isFileSystemRoot(f)) // { // return null; // } // // String attributes = ""; // // if(!f.canWrite()) { // attributes += "R"; // } // // if(f.isHidden()) { // attributes += "H"; // } // // return attributes; } return null; } public void setValueAt ( Object value, int row, int col ) { if ( col == COLUMN_FILENAME ) { JFileChooser chooser = getFileChooser (); File f = ( File ) getValueAt ( row, col ); if ( f != null ) { String oldDisplayName = chooser.getName ( f ); String oldFileName = f.getName (); String newDisplayName = ( ( String ) value ).trim (); String newFileName; if ( !newDisplayName.equals ( oldDisplayName ) ) { newFileName = newDisplayName; // Check if extension is hidden from user int i1 = oldFileName.length (); int i2 = oldDisplayName.length (); if ( i1 > i2 && oldFileName.charAt ( i2 ) == '.' ) { newFileName = newDisplayName + oldFileName.substring ( i2 ); } // rename FileSystemView fsv = chooser.getFileSystemView (); File f2 = fsv.createFileObject ( f.getParentFile (), newFileName ); if ( !f2.exists () && TinyFileChooserUI.this.getModel ().renameFile ( f, f2 ) ) { if ( fsv.isParent ( chooser.getCurrentDirectory (), f2 ) ) { if ( chooser.isMultiSelectionEnabled () ) { chooser.setSelectedFiles ( new File [] { f2 } ); } else { chooser.setSelectedFile ( f2 ); } } else { // Could be because of delay in updating Desktop folder // chooser.setSelectedFile(null); } } else { // PENDING(jeff) - show a dialog indicating failure } } } } } public boolean isCellEditable ( int row, int column ) { return ( column == COLUMN_FILENAME && !UIManager .getBoolean ( "FileChooser.readOnly" ) ); } public void contentsChanged ( ListDataEvent e ) { DetailsTableModel model = ( DetailsTableModel ) detailsTable.getModel (); model.sortColumns ( detailsTable ); } public void intervalAdded ( ListDataEvent e ) { DetailsTableModel model = ( DetailsTableModel ) detailsTable.getModel (); model.sortColumns ( detailsTable ); } public void intervalRemoved ( ListDataEvent e ) { DetailsTableModel model = ( DetailsTableModel ) detailsTable.getModel (); model.sortColumns ( detailsTable ); } public void invalidateCachedFiles () { } // SortableTableData implementation public boolean isColumnSortable ( int column ) { return Comparable.class.isAssignableFrom ( getColumnClass ( column ) ); } public boolean supportsMultiColumnSort () { return false; } public void sortColumns ( final int [] columns, final int [] sortingDirections, JTable table ) { sortColumns = new int [ columns.length ]; sortDirections = new int [ sortingDirections.length ]; System.arraycopy ( columns, 0, sortColumns, 0, columns.length ); System.arraycopy ( sortingDirections, 0, sortDirections, 0, sortingDirections.length ); sortColumns ( table ); } private void sortColumns ( JTable table ) { // long t = System.currentTimeMillis(); Vector files = ( ( TinyDirectoryModel ) listModel ).getFileCache (); // store column selection int [] sc = table.getSelectedColumns (); int [] sr = new int [ table.getSelectedRowCount () ]; // store file list, each file knows if it is selected or not Vector sortFiles = new Vector ( files.size () ); int end = files.size (); // System.out.println("\nNumber of files: " + end); for ( int i = 0 ; i < end ; i++ ) { sortFiles.add ( new SelectedFile ( ( File ) files.get ( i ), table .isRowSelected ( i ) ) ); } // System.out.println("Create sortFiles: " + (System.currentTimeMillis() - // t)); if ( sortColumns.length == 0 ) { // sort file names ascending sortingDirection = SORT_ASCENDING; if ( fileNameComparator == null ) { fileNameComparator = createFileNameComparator (); } Collections.sort ( sortFiles, fileNameComparator ); } else { // perform single-column sort sortingDirection = ( sortDirections [ 0 ] == SORT_ASCENDING ? 1 : -1 ); switch ( sortColumns [ 0 ] ) { case COLUMN_FILENAME : if ( fileNameComparator == null ) { fileNameComparator = createFileNameComparator (); } Collections.sort ( sortFiles, fileNameComparator ); break; case COLUMN_FILESIZE : if ( fileSizeComparator == null ) { fileSizeComparator = createFileSizeComparator (); } Collections.sort ( sortFiles, fileSizeComparator ); break; case COLUMN_FILETYPE : if ( fileTypeComparator == null ) { fileTypeComparator = createFileTypeComparator (); } Collections.sort ( sortFiles, fileTypeComparator ); break; case COLUMN_FILEDATE : if ( fileDateComparator == null ) { fileDateComparator = createFileDateComparator (); } Collections.sort ( sortFiles, fileDateComparator ); break; // case COLUMN_FILEATTR: // if(fileAttributeComparator == null) { // fileAttributeComparator = createFileAttributeComparator(); // } // // Collections.sort(sortFiles, fileAttributeComparator); // break; } } // System.out.println("sort: " + (System.currentTimeMillis() - t)); // store sorted files and restore selection files.clear (); int row = 0; int index = 0; Iterator ii = sortFiles.iterator (); while ( ii.hasNext () ) { SelectedFile sf = ( SelectedFile ) ii.next (); files.add ( sf.file ); if ( sf.selected ) { sr [ index++ ] = row; } row++ ; } // System.out.println("restore files: " + (System.currentTimeMillis() - // t)); fireTableDataChanged (); // We don't want the table's scroll pane to scroll // as we restore the selection doScrolling = false; for ( int i = 0 ; i < sr.length ; i++ ) { table.addRowSelectionInterval ( sr [ i ], sr [ i ] ); } for ( int i = 0 ; i < sc.length ; i++ ) { table.addColumnSelectionInterval ( sc [ i ], sc [ i ] ); } doScrolling = true; // System.out.println("restore selection: " + (System.currentTimeMillis() // - t)); } // A container for a File and its selected state private class SelectedFile { File file; boolean selected; String name; // String attribute; Long size; Long date; String type; Boolean isDirectory; Boolean exists; Boolean isFileSystemRoot; SelectedFile ( File file, boolean selected ) { this.file = file; this.selected = selected; } // public String getAttribute() { // if(attribute == null) { // attribute = ""; // if(exists() && !isFileSystemRoot()) { // if(!file.canWrite()) { // attribute += "R"; // } // // if(file.isHidden()) { // attribute += "H"; // } // } // } // // return attribute; // } public Long getDate () { if ( date == null ) { if ( exists () && !isFileSystemRoot () ) { date = new Long ( file.lastModified () ); } else { date = new Long ( 0 ); } } return date; } public String getName () { if ( name == null ) { name = chooser.getName ( file ); if ( name == null ) name = ""; else name = name.toLowerCase (); } return name; } public Long getSize () { if ( size == null ) { if ( exists () && !isDirectory () ) { size = new Long ( file.length () ); } else { size = new Long ( 0L ); } } return size; } public String getType () { if ( type == null ) { if ( exists () ) { type = chooser.getFileSystemView () .getSystemTypeDescription ( file ); } else { type = ""; } } return type; } public boolean isDirectory () { if ( isDirectory == null ) { isDirectory = new Boolean ( exists () && file.isDirectory () ); } return isDirectory.booleanValue (); } public boolean exists () { if ( exists == null ) { exists = new Boolean ( file.exists () ); } return exists.booleanValue (); } public boolean isFileSystemRoot () { if ( isFileSystemRoot == null ) { isFileSystemRoot = new Boolean ( chooser.getFileSystemView () .isFileSystemRoot ( file ) ); } return isFileSystemRoot.booleanValue (); } } Comparator createFileNameComparator () { return new Comparator () { public int compare ( Object o1, Object o2 ) { SelectedFile f1 = ( SelectedFile ) o1; SelectedFile f2 = ( SelectedFile ) o2; if ( f1.isDirectory () == f2.isDirectory () ) { return f1.getName ().compareTo ( f2.getName () ) * sortingDirection; } else if ( f1.isDirectory () ) { return -1 * sortingDirection; } else { return 1 * sortingDirection; } } }; } Comparator createFileSizeComparator () { return new Comparator () { public int compare ( Object o1, Object o2 ) { Long s1 = ( ( SelectedFile ) o1 ).getSize (); Long s2 = ( ( SelectedFile ) o2 ).getSize (); return s1.compareTo ( s2 ) * sortingDirection; } }; } Comparator createFileTypeComparator () { return new Comparator () { public int compare ( Object o1, Object o2 ) { SelectedFile f1 = ( SelectedFile ) o1; SelectedFile f2 = ( SelectedFile ) o2; if ( f1.isDirectory () == f2.isDirectory () ) { return f1.getType ().compareTo ( f2.getType () ) * sortingDirection; } else if ( f1.isDirectory () ) { return -1 * sortingDirection; } else { return 1 * sortingDirection; } } }; } Comparator createFileDateComparator () { return new Comparator () { public int compare ( Object o1, Object o2 ) { Long d1 = ( ( SelectedFile ) o1 ).getDate (); Long d2 = ( ( SelectedFile ) o2 ).getDate (); return d1.compareTo ( d2 ) * sortingDirection; } }; } // Comparator createFileAttributeComparator() { // return new Comparator() { // public int compare(Object o1, Object o2) { // String a1 = ((SelectedFile)o1).getAttribute(); // String a2 = ((SelectedFile)o2).getAttribute(); // // return a1.compareTo(a2) * sortingDirection; // } // }; // } } class DetailsTableCellRenderer extends DefaultTableCellRenderer { JFileChooser chooser; DateFormat df; DetailsTableCellRenderer ( JFileChooser chooser ) { this.chooser = chooser; df = DateFormat.getDateTimeInstance ( DateFormat.SHORT, DateFormat.SHORT, chooser.getLocale () ); } public Component getTableCellRendererComponent ( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) { int modelColumn = table.getColumnModel ().getColumn ( column ) .getModelIndex (); if ( modelColumn == COLUMN_FILESIZE /* || modelColumn == COLUMN_FILEATTR */) { super.setHorizontalAlignment ( SwingConstants.TRAILING ); } else { super.setHorizontalAlignment ( SwingConstants.LEADING ); } Component c = super.getTableCellRendererComponent ( table, value, isSelected & ( modelColumn == 0 ), false, row, column ); int w1 = table.getColumnModel ().getColumn ( column ).getWidth (); int w2 = c.getPreferredSize ().width; if ( w2 > w1 ) { super.setToolTipText ( getText () ); } else { super.setToolTipText ( null ); } // paint selection only for file name column and // don't paint focus return c; } public void setValue ( Object value ) { setIcon ( null ); if ( value == null ) { setText ( "" ); } else if ( value instanceof File ) { File file = ( File ) value; String fileName = chooser.getName ( file ); setText ( fileName ); Icon icon = chooser.getIcon ( file ); setIcon ( icon ); } else if ( value instanceof Date ) { setText ( ( value == null ) ? "" : df.format ( ( Date ) value ) ); } else if ( value instanceof Long ) { long len = ( ( Long ) value ).longValue () / 1024L; if ( len < 1024L ) { setText ( ( ( len == 0L ) ? 1L : len ) + " KB" ); } else { len /= 1024L; if ( len < 1024L ) { setText ( len + " MB" ); } else { len /= 1024L; setText ( len + " GB" ); } } } else { super.setValue ( value ); } } } protected JPanel createDetailsView ( JFileChooser fc ) { final JFileChooser chooser = fc; JPanel p = new JPanel ( new BorderLayout () ); detailsTableModel = new DetailsTableModel ( chooser ); detailsTable = new JTable ( detailsTableModel ) { // Handle Escape key events here protected boolean processKeyBinding ( KeyStroke ks, KeyEvent e, int condition, boolean pressed ) { if ( e.getKeyCode () == KeyEvent.VK_ESCAPE && getCellEditor () == null ) { // We are not editing, forward to filechooser. chooser.dispatchEvent ( e ); return true; } return super.processKeyBinding ( ks, e, condition, pressed ); } }; detailsTable.setComponentOrientation ( chooser.getComponentOrientation () ); detailsTable.setAutoResizeMode ( JTable.AUTO_RESIZE_OFF ); detailsTable.setShowGrid ( false ); detailsTable.setSelectionModel ( listSelectionModel ); detailsTable.putClientProperty ( "JTable.autoStartsEdit", Boolean.FALSE ); Font font = detailsTable.getFont (); // Minimum row height will be 18 px. (in MetalFileChooserUI is 22 px.) detailsTable.setRowHeight ( Math.max ( font.getSize (), 15 ) + 3 ); TableColumnModel columnModel = detailsTable.getColumnModel (); TableColumn [] columns = new TableColumn [ COLUMN_COLCOUNT ]; boolean isWin = System.getProperty ( "os.name" ).startsWith ( "Windows" ); for ( int i = 0 ; i < COLUMN_COLCOUNT ; i++ ) { columns [ i ] = columnModel.getColumn ( i ); columns [ i ].setPreferredWidth ( COLUMN_WIDTHS [ i ] ); } if ( !isWin ) { columnModel.removeColumn ( columns [ COLUMN_FILETYPE ] ); // columnModel.removeColumn(columns[COLUMN_FILEATTR]); } TableHeaderUI headerUI = detailsTable.getTableHeader ().getUI (); if ( headerUI instanceof TinyTableHeaderUI ) { // sort first column (= directory/file name) ( ( TinyTableHeaderUI ) headerUI ).sortColumns ( new int [] { 0 }, new int [] { SortableTableData.SORT_ASCENDING }, detailsTable ); // set horizontal alignments of table header renderers if ( isWin ) { ( ( TinyTableHeaderUI ) headerUI ).setHorizontalAlignments ( new int [] { SwingConstants.LEADING, SwingConstants.TRAILING, SwingConstants.LEADING, SwingConstants.LEADING } ); } else { ( ( TinyTableHeaderUI ) headerUI ).setHorizontalAlignments ( new int [] { SwingConstants.LEADING, SwingConstants.TRAILING, SwingConstants.LEADING } ); } } TableCellRenderer cellRenderer = new DetailsTableCellRenderer ( chooser ); detailsTable.setDefaultRenderer ( File.class, cellRenderer ); detailsTable.setDefaultRenderer ( Date.class, cellRenderer ); detailsTable.setDefaultRenderer ( Long.class, cellRenderer ); detailsTable.setDefaultRenderer ( String.class, cellRenderer ); detailsTable.setDefaultRenderer ( Object.class, cellRenderer ); // Install cell editor for editing file name final JTextField tf = new JTextField (); tf.addFocusListener ( editorFocusListener ); columns [ COLUMN_FILENAME ].setCellEditor ( new DefaultCellEditor ( tf ) { public boolean isCellEditable ( EventObject e ) { if ( e instanceof MouseEvent ) { MouseEvent me = ( MouseEvent ) e; int index = detailsTable.rowAtPoint ( me.getPoint () ); return ( me.getClickCount () == 1 && detailsTable .isRowSelected ( index ) ); } return super.isCellEditable ( e ); } public Component getTableCellEditorComponent ( JTable table, Object value, boolean isSelected, int row, int column ) { Component comp = super.getTableCellEditorComponent ( table, value, isSelected, row, column ); if ( value instanceof File ) { tf.setText ( chooser.getName ( ( File ) value ) ); tf.requestFocus (); tf.selectAll (); } return comp; } } ); JList fakeList = new JList ( detailsTableModel.listModel ) { JTable table = detailsTable; public int locationToIndex ( Point location ) { return table.rowAtPoint ( location ); } public Rectangle getCellBounds ( int index0, int index1 ) { Rectangle r0 = table.getCellRect ( index0, COLUMN_FILENAME, false ); Rectangle r1 = table.getCellRect ( index1, COLUMN_FILENAME, false ); return r0.union ( r1 ); } public Object getSelectedValue () { return table.getValueAt ( table.getSelectedRow (), COLUMN_FILENAME ); } public Component add ( Component comp ) { if ( comp instanceof JTextField ) { return table.add ( comp ); } else { return super.add ( comp ); } } public void repaint () { if ( table != null ) table.repaint (); } public TransferHandler getTransferHandler () { if ( table != null ) { return table.getTransferHandler (); } else { return super.getTransferHandler (); } } public void setTransferHandler ( TransferHandler newHandler ) { if ( table != null ) { table.setTransferHandler ( newHandler ); } else { super.setTransferHandler ( newHandler ); } } public boolean getDragEnabled () { if ( table != null ) { return table.getDragEnabled (); } else { return super.getDragEnabled (); } } public void setDragEnabled ( boolean b ) { if ( table != null ) { table.setDragEnabled ( b ); } else { super.setDragEnabled ( b ); } } }; fakeList.setSelectionModel ( listSelectionModel ); detailsTable.addMouseListener ( createDoubleClickListener ( chooser, fakeList ) ); // detailsTable.addMouseListener(createSingleClickListener(chooser, // fakeList)); JScrollPane scrollpane = new JScrollPane ( detailsTable ); scrollpane.setComponentOrientation ( chooser.getComponentOrientation () ); LookAndFeel.installColors ( scrollpane.getViewport (), "Table.background", "Table.foreground" ); // Adjust width of first column so the table fills the viewport when // first displayed (temporary listener). scrollpane.addComponentListener ( new ComponentAdapter () { public void componentResized ( ComponentEvent e ) { JScrollPane sp = ( JScrollPane ) e.getComponent (); fixNameColumnWidth ( sp.getViewport ().getSize ().width ); sp.removeComponentListener ( this ); } } ); p.add ( scrollpane, BorderLayout.CENTER ); return p; } private void fixNameColumnWidth ( int viewWidth ) { TableColumn nameCol = detailsTable.getColumnModel ().getColumn ( COLUMN_FILENAME ); int tableWidth = detailsTable.getPreferredSize ().width; if ( tableWidth < viewWidth ) { nameCol.setPreferredWidth ( nameCol.getPreferredWidth () + viewWidth - tableWidth ); } } private class DelayedSelectionUpdater implements Runnable { DelayedSelectionUpdater () { SwingUtilities.invokeLater ( this ); } public void run () { setFileSelected (); } } /** * Creates a selection listener for the list of files and directories. * * @param fc a <code>JFileChooser</code> * @return a <code>ListSelectionListener</code> */ public ListSelectionListener createListSelectionListener ( JFileChooser fc ) { return new SelectionListener () { public void valueChanged ( ListSelectionEvent e ) { if ( !e.getValueIsAdjusting () ) { JFileChooser chooser = getFileChooser (); FileSystemView fsv = chooser.getFileSystemView (); JList list = ( JList ) e.getSource (); if ( chooser.isMultiSelectionEnabled () ) { File [] files = null; Object [] objects = list.getSelectedValues (); if ( objects != null ) { if ( objects.length == 1 && ( ( File ) objects [ 0 ] ).isDirectory () && chooser.isTraversable ( ( ( File ) objects [ 0 ] ) ) && ( chooser.getFileSelectionMode () == chooser.FILES_ONLY || !fsv .isFileSystem ( ( ( File ) objects [ 0 ] ) ) ) ) { setDirectorySelected ( true ); setDirectory ( ( ( File ) objects [ 0 ] ) ); } else { files = new File [ objects.length ]; int j = 0; for ( int i = 0 ; i < objects.length ; i++ ) { File f = ( File ) objects [ i ]; if ( ( chooser.isFileSelectionEnabled () && f.isFile () ) || ( chooser.isDirectorySelectionEnabled () && fsv.isFileSystem ( f ) && f.isDirectory () ) ) { files [ j++ ] = f; } } if ( j == 0 ) { files = null; } else if ( j < objects.length ) { File [] tmpFiles = new File [ j ]; System.arraycopy ( files, 0, tmpFiles, 0, j ); files = tmpFiles; } setDirectorySelected ( false ); } } chooser.setSelectedFiles ( files ); } else { File file = ( File ) list.getSelectedValue (); if ( file != null && file.isDirectory () && chooser.isTraversable ( file ) && ( chooser.getFileSelectionMode () == chooser.FILES_ONLY || !fsv .isFileSystem ( file ) ) ) { setDirectorySelected ( true ); setDirectory ( file ); chooser.setSelectedFile ( null ); } else { setDirectorySelected ( false ); if ( file != null ) { chooser.setSelectedFile ( file ); } } } } } }; } private MouseListener createSingleClickListener ( JFileChooser fc, JList list ) { return new SingleClickListener ( list ); } int lastIndex = -1; File editFile = null; int editX = 20; private int getEditIndex () { return lastIndex; } private void setEditIndex ( int i ) { lastIndex = i; } private void resetEditIndex () { lastIndex = -1; } private void cancelEdit () { if ( editFile != null ) { editFile = null; list.remove ( editCell ); centerPanel.repaint (); } else if ( detailsTable != null && detailsTable.isEditing () ) { detailsTable.getCellEditor ().cancelCellEditing (); } } JTextField editCell = null; private void editFileName ( int index ) { if ( UIManager.getBoolean ( "FileChooser.readOnly" ) ) return; ensureIndexIsVisible ( index ); if ( listViewPanel.isVisible () ) { editFile = ( File ) getModel ().getElementAt ( index ); Rectangle r = list.getCellBounds ( index, index ); if ( editCell == null ) { editCell = new JTextField (); editCell.addActionListener ( new EditActionListener () ); editCell.addFocusListener ( editorFocusListener ); editCell.setNextFocusableComponent ( list ); } list.add ( editCell ); editCell.setText ( getFileChooser ().getName ( editFile ) ); if ( list.getComponentOrientation ().isLeftToRight () ) { editCell.setBounds ( editX + r.x, r.y, r.width - editX, r.height ); } else { editCell.setBounds ( r.x, r.y, r.width - editX, r.height ); } editCell.requestFocus (); editCell.selectAll (); } else if ( detailsViewPanel.isVisible () ) { detailsTable.editCellAt ( index, COLUMN_FILENAME ); } } protected class SingleClickListener extends MouseAdapter { JList list; public SingleClickListener ( JList list ) { this.list = list; } public void mouseClicked ( MouseEvent e ) { if ( SwingUtilities.isLeftMouseButton ( e ) ) { if ( e.getClickCount () == 1 ) { JFileChooser fc = getFileChooser (); int index = list.locationToIndex ( e.getPoint () ); if ( ( !fc.isMultiSelectionEnabled () || fc.getSelectedFiles ().length <= 1 ) && index >= 0 && list.isSelectedIndex ( index ) && getEditIndex () == index && editFile == null ) { editFileName ( index ); } else { if ( index >= 0 ) { setEditIndex ( index ); } else { resetEditIndex (); } } } else { // on double click (open or drill down one directory) be // sure to clear the edit index resetEditIndex (); } } } } class EditActionListener implements ActionListener { public void actionPerformed ( ActionEvent e ) { applyEdit (); } } private void applyEdit () { if ( editFile != null && editFile.exists () ) { JFileChooser chooser = getFileChooser (); String oldDisplayName = chooser.getName ( editFile ); String oldFileName = editFile.getName (); String newDisplayName = editCell.getText ().trim (); String newFileName; if ( !newDisplayName.equals ( oldDisplayName ) ) { newFileName = newDisplayName; // Check if extension is hidden from user int i1 = oldFileName.length (); int i2 = oldDisplayName.length (); if ( i1 > i2 && oldFileName.charAt ( i2 ) == '.' ) { newFileName = newDisplayName + oldFileName.substring ( i2 ); } // rename FileSystemView fsv = chooser.getFileSystemView (); File f2 = fsv .createFileObject ( editFile.getParentFile (), newFileName ); if ( !f2.exists () && getModel ().renameFile ( editFile, f2 ) ) { if ( fsv.isParent ( chooser.getCurrentDirectory (), f2 ) ) { if ( chooser.isMultiSelectionEnabled () ) { chooser.setSelectedFiles ( new File [] { f2 } ); } else { chooser.setSelectedFile ( f2 ); } } else { // Could be because of delay in updating Desktop folder // chooser.setSelectedFile(null); } } else { // PENDING(jeff) - show a dialog indicating failure } } } if ( detailsTable != null && detailsTable.isEditing () ) { detailsTable.getCellEditor ().stopCellEditing (); } cancelEdit (); } protected class FileRenderer extends DefaultListCellRenderer { public Component getListCellRendererComponent ( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus ) { super.getListCellRendererComponent ( list, value, index, isSelected, cellHasFocus ); File file = ( File ) value; String fileName = getFileChooser ().getName ( file ); setText ( fileName ); Icon icon = getFileChooser ().getIcon ( file ); setIcon ( icon ); if ( isSelected ) { // PENDING(jeff) - grab padding (4) below from defaults table. editX = icon.getIconWidth () + 4; } return this; } } public void uninstallUI ( JComponent c ) { // Remove listeners c.removePropertyChangeListener ( filterComboBoxModel ); cancelButton.removeActionListener ( getCancelSelectionAction () ); approveButton.removeActionListener ( getApproveSelectionAction () ); fileNameTextField.removeActionListener ( getApproveSelectionAction () ); super.uninstallUI ( c ); } /** * Returns the preferred size of the specified <code>JFileChooser</code>. * The preferred size is at least as large, in both height and width, as the * preferred size recommended by the file chooser's layout manager. * * @param mainColor a <code>JFileChooser</code> * @return a <code>Dimension</code> specifying the preferred width and * height of the file chooser */ public Dimension getPreferredSize ( JComponent c ) { int prefWidth = PREF_SIZE.width; Dimension d = c.getLayout ().preferredLayoutSize ( c ); if ( d != null ) { return new Dimension ( d.width < prefWidth ? prefWidth : d.width, d.height < PREF_SIZE.height ? PREF_SIZE.height : d.height ); } else { return new Dimension ( prefWidth, PREF_SIZE.height ); } } /** * Returns the minimum size of the <code>JFileChooser</code>. * * @param mainColor a <code>JFileChooser</code> * @return a <code>Dimension</code> specifying the minimum width and height * of the file chooser */ public Dimension getMinimumSize ( JComponent c ) { return MIN_SIZE; } /** * Returns the maximum size of the <code>JFileChooser</code>. * * @param mainColor a <code>JFileChooser</code> * @return a <code>Dimension</code> specifying the maximum width and height * of the file chooser */ public Dimension getMaximumSize ( JComponent c ) { return new Dimension ( Integer.MAX_VALUE, Integer.MAX_VALUE ); } void setFileSelected () { if ( getFileChooser ().isMultiSelectionEnabled () && !isDirectorySelected () ) { File [] files = getFileChooser ().getSelectedFiles (); // Should be // selected Object [] selectedObjects = list.getSelectedValues (); // Are actually // selected // Remove files that shouldn't be selected for ( int j = 0 ; j < selectedObjects.length ; j++ ) { boolean found = false; for ( int i = 0 ; i < files.length ; i++ ) { if ( files [ i ].equals ( selectedObjects [ j ] ) ) { found = true; break; } } if ( !found ) { int index = getModel ().indexOf ( selectedObjects [ j ] ); if ( index >= 0 ) { listSelectionModel.removeSelectionInterval ( index, index ); } } } // Add files that should be selected for ( int i = 0 ; i < files.length ; i++ ) { boolean found = false; for ( int j = 0 ; j < selectedObjects.length ; j++ ) { if ( files [ i ].equals ( selectedObjects [ j ] ) ) { found = true; break; } } if ( !found ) { int index = getModel ().indexOf ( files [ i ] ); if ( index >= 0 ) { listSelectionModel.addSelectionInterval ( index, index ); } } } } else { JFileChooser chooser = getFileChooser (); File f = null; if ( isDirectorySelected () ) { f = getDirectory (); } else { f = chooser.getSelectedFile (); } int i; if ( f != null && ( i = getModel ().indexOf ( f ) ) >= 0 ) { listSelectionModel.setSelectionInterval ( i, i ); ensureIndexIsVisible ( i ); } else { listSelectionModel.clearSelection (); } } } private String fileNameString ( File file ) { if ( file == null ) { return null; } else { JFileChooser fc = getFileChooser (); if ( fc.isDirectorySelectionEnabled () && !fc.isFileSelectionEnabled () ) { return file.getPath (); } else { return file.getName (); } } } private String fileNameString ( File [] files ) { StringBuffer buf = new StringBuffer (); for ( int i = 0 ; files != null && i < files.length ; i++ ) { if ( i > 0 ) { buf.append ( " " ); } if ( files.length > 1 ) { buf.append ( "\"" ); } buf.append ( fileNameString ( files [ i ] ) ); if ( files.length > 1 ) { buf.append ( "\"" ); } } return buf.toString (); } /* The following methods are used by the PropertyChange Listener */ private void doSelectedFileChanged ( PropertyChangeEvent e ) { applyEdit (); File f = ( File ) e.getNewValue (); JFileChooser fc = getFileChooser (); if ( f != null && ( ( fc.isFileSelectionEnabled () && !f.isDirectory () ) || ( f .isDirectory () && fc.isDirectorySelectionEnabled () ) ) ) { setFileName ( fileNameString ( f ) ); setFileSelected (); } } private void doSelectedFilesChanged ( PropertyChangeEvent e ) { applyEdit (); File [] files = ( File [] ) e.getNewValue (); JFileChooser fc = getFileChooser (); if ( files != null && files.length > 0 && ( files.length > 1 || fc.isDirectorySelectionEnabled () || !files [ 0 ] .isDirectory () ) ) { setFileName ( fileNameString ( files ) ); setFileSelected (); } } private void doDirectoryChanged ( PropertyChangeEvent e ) { JFileChooser fc = getFileChooser (); FileSystemView fsv = fc.getFileSystemView (); applyEdit (); resetEditIndex (); clearIconCache (); listSelectionModel.clearSelection (); ensureIndexIsVisible ( 0 ); File currentDirectory = fc.getCurrentDirectory (); if ( currentDirectory != null ) { directoryComboBoxModel.addItem ( currentDirectory ); getNewFolderAction ().setEnabled ( currentDirectory.canWrite () ); getChangeToParentDirectoryAction ().setEnabled ( !fsv.isRoot ( currentDirectory ) ); if ( fc.isDirectorySelectionEnabled () && !fc.isFileSelectionEnabled () ) { if ( fsv.isFileSystem ( currentDirectory ) ) { setFileName ( currentDirectory.getPath () ); } else { setFileName ( null ); } } } } private void doFilterChanged ( PropertyChangeEvent e ) { applyEdit (); resetEditIndex (); clearIconCache (); listSelectionModel.clearSelection (); } private void doFileSelectionModeChanged ( PropertyChangeEvent e ) { applyEdit (); resetEditIndex (); clearIconCache (); listSelectionModel.clearSelection (); JFileChooser fc = getFileChooser (); File currentDirectory = fc.getCurrentDirectory (); if ( currentDirectory != null && fc.isDirectorySelectionEnabled () && !fc.isFileSelectionEnabled () && fc.getFileSystemView ().isFileSystem ( currentDirectory ) ) { setFileName ( currentDirectory.getPath () ); } else { setFileName ( null ); } } private void doMultiSelectionChanged ( PropertyChangeEvent e ) { if ( getFileChooser ().isMultiSelectionEnabled () ) { listSelectionModel .setSelectionMode ( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION ); } else { listSelectionModel .setSelectionMode ( ListSelectionModel.SINGLE_SELECTION ); listSelectionModel.clearSelection (); getFileChooser ().setSelectedFiles ( null ); } } private void doAccessoryChanged ( PropertyChangeEvent e ) { if ( getAccessoryPanel () != null ) { if ( e.getOldValue () != null ) { getAccessoryPanel ().remove ( ( JComponent ) e.getOldValue () ); } JComponent accessory = ( JComponent ) e.getNewValue (); if ( accessory != null ) { getAccessoryPanel ().add ( accessory, BorderLayout.CENTER ); } } } private void doApproveButtonTextChanged ( PropertyChangeEvent e ) { JFileChooser chooser = getFileChooser (); approveButton.setText ( getApproveButtonText ( chooser ) ); approveButton.setToolTipText ( getApproveButtonToolTipText ( chooser ) ); } private void doDialogTypeChanged ( PropertyChangeEvent e ) { JFileChooser chooser = getFileChooser (); approveButton.setText ( getApproveButtonText ( chooser ) ); approveButton.setToolTipText ( getApproveButtonToolTipText ( chooser ) ); if ( chooser.getDialogType () == JFileChooser.SAVE_DIALOG ) { lookInLabel.setText ( saveInLabelText ); } else { lookInLabel.setText ( lookInLabelText ); } } private void doApproveButtonMnemonicChanged ( PropertyChangeEvent e ) { // Note: Metal does not use mnemonics for approve and cancel } private void doControlButtonsChanged ( PropertyChangeEvent e ) { if ( getFileChooser ().getControlButtonsAreShown () ) { addControlButtons (); } else { removeControlButtons (); } } /* * Listen for filechooser property changes, such as the selected file * changing, or the type of the dialog changing. */ public PropertyChangeListener createPropertyChangeListener ( JFileChooser fc ) { return new PropertyChangeListener () { public void propertyChange ( PropertyChangeEvent e ) { String s = e.getPropertyName (); if ( s.equals ( JFileChooser.SELECTED_FILE_CHANGED_PROPERTY ) ) { doSelectedFileChanged ( e ); } else if ( s.equals ( JFileChooser.SELECTED_FILES_CHANGED_PROPERTY ) ) { doSelectedFilesChanged ( e ); } else if ( s.equals ( JFileChooser.DIRECTORY_CHANGED_PROPERTY ) ) { doDirectoryChanged ( e ); } else if ( s.equals ( JFileChooser.FILE_FILTER_CHANGED_PROPERTY ) ) { doFilterChanged ( e ); } else if ( s.equals ( JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY ) ) { doFileSelectionModeChanged ( e ); } else if ( s .equals ( JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY ) ) { doMultiSelectionChanged ( e ); } else if ( s.equals ( JFileChooser.ACCESSORY_CHANGED_PROPERTY ) ) { doAccessoryChanged ( e ); } else if ( s.equals ( JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY ) || s .equals ( JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY ) ) { doApproveButtonTextChanged ( e ); } else if ( s.equals ( JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY ) ) { doDialogTypeChanged ( e ); } else if ( s .equals ( JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY ) ) { doApproveButtonMnemonicChanged ( e ); } else if ( s .equals ( JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY ) ) { doControlButtonsChanged ( e ); } else if ( s.equals ( JFileChooser.CANCEL_SELECTION ) ) { applyEdit (); } else if ( s.equals ( "componentOrientation" ) ) { ComponentOrientation o = ( ComponentOrientation ) e.getNewValue (); JFileChooser cc = ( JFileChooser ) e.getSource (); if ( o != ( ComponentOrientation ) e.getOldValue () ) { cc.applyComponentOrientation ( o ); } if ( detailsTable != null ) { detailsTable.setComponentOrientation ( o ); detailsTable.getParent ().getParent ().setComponentOrientation ( o ); } } else if ( s.equals ( "ancestor" ) ) { if ( e.getOldValue () == null && e.getNewValue () != null ) { // Ancestor was added, set initial focus fileNameTextField.selectAll (); fileNameTextField.requestFocus (); } } } }; } protected void createModel () { if ( directoryModel != null ) { directoryModel.invalidateFileCache (); } directoryModel = new TinyDirectoryModel ( getFileChooser () ); } public BasicDirectoryModel getModel () { return directoryModel; } protected void removeControlButtons () { getBottomPanel ().remove ( getButtonPanel () ); } protected void addControlButtons () { getBottomPanel ().add ( getButtonPanel () ); } private void ensureIndexIsVisible ( int i ) { if ( i >= 0 ) { list.ensureIndexIsVisible ( i ); if ( detailsTable != null ) { // This is the Metal code ... // detailsTable.scrollRectToVisible(detailsTable.getCellRect(i, // COLUMN_FILENAME, true)); // ... we only want to make sure that the row is visible // (but not scroll to the file name column) Rectangle r1 = detailsTable.getCellRect ( i, COLUMN_FILENAME, true ); Rectangle r2 = detailsTable.getCellRect ( i, detailsTable .getColumnCount () - 1, true ); r1.x = ( r1.x + r2.x + r2.width ) / 2; r1.width = 1; detailsTable.scrollRectToVisible ( r1 ); } } } public void ensureFileIsVisible ( JFileChooser fc, File f ) { if ( !doScrolling ) return; ensureIndexIsVisible ( getModel ().indexOf ( f ) ); } public void rescanCurrentDirectory ( JFileChooser fc ) { getModel ().validateFileCache (); } public String getFileName () { if ( fileNameTextField != null ) { return fileNameTextField.getText (); } else { return null; } } public void setFileName ( String filename ) { if ( fileNameTextField != null ) { fileNameTextField.setText ( filename ); } } /** * Property to remember whether a directory is currently selected in the UI. * This is normally called by the UI on a selection event. * * @param directorySelected if a directory is currently selected. * @since 1.4 */ protected void setDirectorySelected ( boolean directorySelected ) { super.setDirectorySelected ( directorySelected ); JFileChooser chooser = getFileChooser (); if ( directorySelected ) { approveButton.setText ( directoryOpenButtonText ); approveButton.setToolTipText ( directoryOpenButtonToolTipText ); } else { approveButton.setText ( getApproveButtonText ( chooser ) ); approveButton.setToolTipText ( getApproveButtonToolTipText ( chooser ) ); } } public String getDirectoryName () { // PENDING(jeff) - get the name from the directory combobox return null; } public void setDirectoryName ( String dirname ) { // PENDING(jeff) - set the name in the directory combobox } protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer ( JFileChooser fc ) { return new DirectoryComboBoxRenderer (); } // // Renderer for DirectoryComboBox // class DirectoryComboBoxRenderer extends DefaultListCellRenderer { IndentIcon ii = new IndentIcon (); public Component getListCellRendererComponent ( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus ) { super.getListCellRendererComponent ( list, value, index, isSelected, cellHasFocus ); if ( value == null ) { setText ( "" ); return this; } File directory = ( File ) value; setText ( getFileChooser ().getName ( directory ) ); Icon icon = getFileChooser ().getIcon ( directory ); ii.icon = icon; ii.depth = directoryComboBoxModel.getDepth ( index ); setIcon ( ii ); return this; } } final static int space = 10; class IndentIcon implements Icon { Icon icon = null; int depth = 0; public void paintIcon ( Component c, Graphics g, int x, int y ) { if ( c.getComponentOrientation ().isLeftToRight () ) { icon.paintIcon ( c, g, x + depth * space, y ); } else { icon.paintIcon ( c, g, x, y ); } } public int getIconWidth () { return icon.getIconWidth () + depth * space; } public int getIconHeight () { return icon.getIconHeight (); } } // // DataModel for DirectoryComboxbox // protected DirectoryComboBoxModel createDirectoryComboBoxModel ( JFileChooser fc ) { return new DirectoryComboBoxModel (); } /** * Data model for a type-face selection combo-box. */ protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel { Vector directories = new Vector (); int [] depths = null; File selectedDirectory = null; JFileChooser chooser = getFileChooser (); FileSystemView fsv = chooser.getFileSystemView (); public DirectoryComboBoxModel () { // Add the current directory to the model, and make it the // selectedDirectory File dir = getFileChooser ().getCurrentDirectory (); if ( dir != null ) { addItem ( dir ); } } /** * Adds the directory to the model and sets it to be selected, additionally * clears out the previous selected directory and the paths leading up to * it, if any. */ private void addItem ( File directory ) { if ( directory == null ) { return; } directories.clear (); File [] baseFolders; if ( useShellFolder ) { baseFolders = ( File [] ) ShellFolder .get ( "fileChooserComboBoxFolders" ); } else { baseFolders = fsv.getRoots (); } directories.addAll ( Arrays.asList ( baseFolders ) ); // Get the canonical (full) path. This has the side // benefit of removing extraneous chars from the path, // for example /foo/bar/ becomes /foo/bar File canonical = null; try { canonical = directory.getCanonicalFile (); } catch ( IOException e ) { // Maybe drive is not ready. Can't abort here. canonical = directory; } // create File instances of each directory leading up to the top try { File sf = ShellFolder.getShellFolder ( canonical ); File f = sf; Vector path = new Vector ( 10 ); do { path.addElement ( f ); } while ( ( f = f.getParentFile () ) != null ); int pathCount = path.size (); // Insert chain at appropriate place in vector for ( int i = 0 ; i < pathCount ; i++ ) { f = ( File ) path.get ( i ); if ( directories.contains ( f ) ) { int topIndex = directories.indexOf ( f ); for ( int j = i - 1 ; j >= 0 ; j-- ) { directories.insertElementAt ( path.get ( j ), topIndex + i - j ); } break; } } calculateDepths (); setSelectedItem ( sf ); } catch ( FileNotFoundException ex ) { calculateDepths (); } } private void calculateDepths () { depths = new int [ directories.size () ]; for ( int i = 0 ; i < depths.length ; i++ ) { File dir = ( File ) directories.get ( i ); File parent = dir.getParentFile (); depths [ i ] = 0; if ( parent != null ) { for ( int j = i - 1 ; j >= 0 ; j-- ) { if ( parent.equals ( ( File ) directories.get ( j ) ) ) { depths [ i ] = depths [ j ] + 1; break; } } } } } public int getDepth ( int i ) { return ( depths != null && i >= 0 && i < depths.length ) ? depths [ i ] : 0; } public void setSelectedItem ( Object selectedDirectory ) { this.selectedDirectory = ( File ) selectedDirectory; fireContentsChanged ( this, -1, -1 ); } public Object getSelectedItem () { return selectedDirectory; } public int getSize () { return directories.size (); } public Object getElementAt ( int index ) { return directories.elementAt ( index ); } } // // Renderer for Types ComboBox // protected FilterComboBoxRenderer createFilterComboBoxRenderer () { return new FilterComboBoxRenderer (); } /** * Render different type sizes and styles. */ public class FilterComboBoxRenderer extends DefaultListCellRenderer { public Component getListCellRendererComponent ( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus ) { super.getListCellRendererComponent ( list, value, index, isSelected, cellHasFocus ); if ( value != null && value instanceof FileFilter ) { setText ( ( ( FileFilter ) value ).getDescription () ); } return this; } } // // DataModel for Types Comboxbox // protected FilterComboBoxModel createFilterComboBoxModel () { return new FilterComboBoxModel (); } /** * Data model for a type-face selection combo-box. */ protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener { protected FileFilter [] filters; protected FilterComboBoxModel () { super (); filters = getFileChooser ().getChoosableFileFilters (); } public void propertyChange ( PropertyChangeEvent e ) { String prop = e.getPropertyName (); if ( prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY ) { filters = ( FileFilter [] ) e.getNewValue (); fireContentsChanged ( this, -1, -1 ); } else if ( prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY ) { fireContentsChanged ( this, -1, -1 ); } } public void setSelectedItem ( Object filter ) { if ( filter != null ) { getFileChooser ().setFileFilter ( ( FileFilter ) filter ); setFileName ( null ); fireContentsChanged ( this, -1, -1 ); } } public Object getSelectedItem () { // Ensure that the current filter is in the list. // NOTE: we shouldnt' have to do this, since JFileChooser adds // the filter to the choosable filters list when the filter // is set. Lets be paranoid just in case someone overrides // setFileFilter in JFileChooser. FileFilter currentFilter = getFileChooser ().getFileFilter (); boolean found = false; if ( currentFilter != null ) { for ( int i = 0 ; i < filters.length ; i++ ) { if ( filters [ i ] == currentFilter ) { found = true; } } if ( found == false ) { getFileChooser ().addChoosableFileFilter ( currentFilter ); } } return getFileChooser ().getFileFilter (); } public int getSize () { if ( filters != null ) { return filters.length; } else { return 0; } } public Object getElementAt ( int index ) { if ( index > getSize () - 1 ) { // This shouldn't happen. Try to recover gracefully. return getFileChooser ().getFileFilter (); } if ( filters != null ) { return filters [ index ]; } else { return null; } } } public void valueChanged ( ListSelectionEvent e ) { JFileChooser fc = getFileChooser (); File f = fc.getSelectedFile (); if ( !e.getValueIsAdjusting () && f != null && !getFileChooser ().isTraversable ( f ) ) { setFileName ( fileNameString ( f ) ); } } /** * Acts when DirectoryComboBox has changed the selected item. */ protected class DirectoryComboBoxAction extends AbstractAction { protected DirectoryComboBoxAction () { super ( "DirectoryComboBoxAction" ); } public void actionPerformed ( ActionEvent e ) { File f = ( File ) directoryComboBox.getSelectedItem (); getFileChooser ().setCurrentDirectory ( f ); } } protected JButton getApproveButton ( JFileChooser fc ) { return approveButton; } /** * <code>ButtonAreaLayout</code> behaves in a similar manner to * <code>FlowLayout</code>. It lays out all components from left to right, * flushed right. The widths of all components will be set to the largest * preferred size width. */ private static class ButtonAreaLayout implements LayoutManager { private int hGap = 5; private int topMargin = 17; public void addLayoutComponent ( String string, Component comp ) { } public void layoutContainer ( Container container ) { Component [] children = container.getComponents (); if ( children != null && children.length > 0 ) { int numChildren = children.length; Dimension [] sizes = new Dimension [ numChildren ]; Insets insets = container.getInsets (); int yLocation = insets.top + topMargin; int maxWidth = 0; for ( int counter = 0 ; counter < numChildren ; counter++ ) { sizes [ counter ] = children [ counter ].getPreferredSize (); maxWidth = Math.max ( maxWidth, sizes [ counter ].width ); } int xLocation, xOffset; if ( container.getComponentOrientation ().isLeftToRight () ) { xLocation = container.getSize ().width - insets.left - maxWidth; xOffset = hGap + maxWidth; } else { xLocation = insets.left; xOffset = - ( hGap + maxWidth ); } for ( int counter = numChildren - 1 ; counter >= 0 ; counter-- ) { children [ counter ].setBounds ( xLocation, yLocation, maxWidth, sizes [ counter ].height ); xLocation -= xOffset; } } } public Dimension minimumLayoutSize ( Container c ) { if ( c != null ) { Component [] children = c.getComponents (); if ( children != null && children.length > 0 ) { int numChildren = children.length; int height = 0; Insets cInsets = c.getInsets (); int extraHeight = topMargin + cInsets.top + cInsets.bottom; int extraWidth = cInsets.left + cInsets.right; int maxWidth = 0; for ( int counter = 0 ; counter < numChildren ; counter++ ) { Dimension aSize = children [ counter ].getPreferredSize (); height = Math.max ( height, aSize.height ); maxWidth = Math.max ( maxWidth, aSize.width ); } return new Dimension ( extraWidth + numChildren * maxWidth + ( numChildren - 1 ) * hGap, extraHeight + height ); } } return new Dimension ( 0, 0 ); } public Dimension preferredLayoutSize ( Container c ) { return minimumLayoutSize ( c ); } public void removeLayoutComponent ( Component c ) { } } private static void groupLabels ( AlignedLabel [] group ) { for ( int i = 0 ; i < group.length ; i++ ) { group [ i ].group = group; } } private class AlignedLabel extends JLabel { private AlignedLabel [] group; private int maxWidth = 0; AlignedLabel ( String text ) { super ( text ); setAlignmentX ( JComponent.LEFT_ALIGNMENT ); } public Dimension getPreferredSize () { Dimension d = super.getPreferredSize (); // Align the width with all other labels in group. return new Dimension ( getMaxWidth () + 11, d.height ); } private int getMaxWidth () { if ( maxWidth == 0 && group != null ) { int max = 0; for ( int i = 0 ; i < group.length ; i++ ) { max = Math.max ( group [ i ].getSuperPreferredWidth (), max ); } for ( int i = 0 ; i < group.length ; i++ ) { group [ i ].maxWidth = max; } } return maxWidth; } private int getSuperPreferredWidth () { return super.getPreferredSize ().width; } } }