Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/swing/FilePane.java
38829 views
/*1* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package sun.swing;2526import java.awt.*;27import java.awt.event.*;28import java.beans.PropertyChangeEvent;29import java.beans.PropertyChangeListener;30import java.io.*;31import java.text.DateFormat;32import java.text.MessageFormat;33import java.util.*;34import java.util.List;35import java.util.concurrent.Callable;3637import javax.accessibility.AccessibleContext;38import javax.swing.*;39import javax.swing.border.*;40import javax.swing.event.*;41import javax.swing.filechooser.*;42import javax.swing.plaf.basic.*;43import javax.swing.table.*;44import javax.swing.text.*;4546import sun.awt.AWTAccessor;47import sun.awt.AWTAccessor.MouseEventAccessor;48import sun.awt.shell.*;4950/**51* <b>WARNING:</b> This class is an implementation detail and is only52* public so that it can be used by two packages. You should NOT consider53* this public API.54* <p>55* This component is intended to be used in a subclass of56* javax.swing.plaf.basic.BasicFileChooserUI. It realies heavily on the57* implementation of BasicFileChooserUI, and is intended to be API compatible58* with earlier implementations of MetalFileChooserUI and WindowsFileChooserUI.59*60* @author Leif Samuelsson61*/62public class FilePane extends JPanel implements PropertyChangeListener {63// Constants for actions. These are used for the actions' ACTION_COMMAND_KEY64// and as keys in the action maps for FilePane and the corresponding UI classes6566public final static String ACTION_APPROVE_SELECTION = "approveSelection";67public final static String ACTION_CANCEL = "cancelSelection";68public final static String ACTION_EDIT_FILE_NAME = "editFileName";69public final static String ACTION_REFRESH = "refresh";70public final static String ACTION_CHANGE_TO_PARENT_DIRECTORY = "Go Up";71public final static String ACTION_NEW_FOLDER = "New Folder";72public final static String ACTION_VIEW_LIST = "viewTypeList";73public final static String ACTION_VIEW_DETAILS = "viewTypeDetails";7475private Action[] actions;7677// "enums" for setViewType()78public static final int VIEWTYPE_LIST = 0;79public static final int VIEWTYPE_DETAILS = 1;80private static final int VIEWTYPE_COUNT = 2;8182private int viewType = -1;83private JPanel[] viewPanels = new JPanel[VIEWTYPE_COUNT];84private JPanel currentViewPanel;85private String[] viewTypeActionNames;8687private String filesListAccessibleName = null;88private String filesDetailsAccessibleName = null;8990private JPopupMenu contextMenu;91private JMenu viewMenu;9293private String viewMenuLabelText;94private String refreshActionLabelText;95private String newFolderActionLabelText;9697private String kiloByteString;98private String megaByteString;99private String gigaByteString;100101private String renameErrorTitleText;102private String renameErrorText;103private String renameErrorFileExistsText;104105private static final Cursor waitCursor =106Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);107108private final KeyListener detailsKeyListener = new KeyAdapter() {109private final long timeFactor;110111private final StringBuilder typedString = new StringBuilder();112113private long lastTime = 1000L;114115{116Long l = (Long) UIManager.get("Table.timeFactor");117timeFactor = (l != null) ? l : 1000L;118}119120/**121* Moves the keyboard focus to the first element whose prefix matches122* the sequence of alphanumeric keys pressed by the user with delay123* less than value of <code>timeFactor</code>. Subsequent same key124* presses move the keyboard focus to the next object that starts with125* the same letter until another key is pressed, then it is treated126* as the prefix with appropriate number of the same letters followed127* by first typed another letter.128*/129public void keyTyped(KeyEvent e) {130BasicDirectoryModel model = getModel();131int rowCount = model.getSize();132133if (detailsTable == null || rowCount == 0 ||134e.isAltDown() || e.isControlDown() || e.isMetaDown()) {135return;136}137138InputMap inputMap = detailsTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);139KeyStroke key = KeyStroke.getKeyStrokeForEvent(e);140141if (inputMap != null && inputMap.get(key) != null) {142return;143}144145int startIndex = detailsTable.getSelectionModel().getLeadSelectionIndex();146147if (startIndex < 0) {148startIndex = 0;149}150151if (startIndex >= rowCount) {152startIndex = rowCount - 1;153}154155char c = e.getKeyChar();156157long time = e.getWhen();158159if (time - lastTime < timeFactor) {160if (typedString.length() == 1 && typedString.charAt(0) == c) {161// Subsequent same key presses move the keyboard focus to the next162// object that starts with the same letter.163startIndex++;164} else {165typedString.append(c);166}167} else {168startIndex++;169170typedString.setLength(0);171typedString.append(c);172}173174lastTime = time;175176if (startIndex >= rowCount) {177startIndex = 0;178}179180// Find next file181int index = getNextMatch(startIndex, rowCount - 1);182183if (index < 0 && startIndex > 0) { // wrap184index = getNextMatch(0, startIndex - 1);185}186187if (index >= 0) {188detailsTable.getSelectionModel().setSelectionInterval(index, index);189190Rectangle cellRect = detailsTable.getCellRect(index,191detailsTable.convertColumnIndexToView(COLUMN_FILENAME), false);192detailsTable.scrollRectToVisible(cellRect);193}194}195196private int getNextMatch(int startIndex, int finishIndex) {197BasicDirectoryModel model = getModel();198JFileChooser fileChooser = getFileChooser();199DetailsTableRowSorter rowSorter = getRowSorter();200201String prefix = typedString.toString().toLowerCase();202203// Search element204for (int index = startIndex; index <= finishIndex; index++) {205File file = (File) model.getElementAt(rowSorter.convertRowIndexToModel(index));206207String fileName = fileChooser.getName(file).toLowerCase();208209if (fileName.startsWith(prefix)) {210return index;211}212}213214return -1;215}216};217218private FocusListener editorFocusListener = new FocusAdapter() {219public void focusLost(FocusEvent e) {220if (! e.isTemporary()) {221applyEdit();222}223}224};225226private static FocusListener repaintListener = new FocusListener() {227public void focusGained(FocusEvent fe) {228repaintSelection(fe.getSource());229}230231public void focusLost(FocusEvent fe) {232repaintSelection(fe.getSource());233}234235private void repaintSelection(Object source) {236if (source instanceof JList) {237repaintListSelection((JList)source);238} else if (source instanceof JTable) {239repaintTableSelection((JTable)source);240}241}242243private void repaintListSelection(JList list) {244int[] indices = list.getSelectedIndices();245for (int i : indices) {246Rectangle bounds = list.getCellBounds(i, i);247list.repaint(bounds);248}249}250251private void repaintTableSelection(JTable table) {252int minRow = table.getSelectionModel().getMinSelectionIndex();253int maxRow = table.getSelectionModel().getMaxSelectionIndex();254if (minRow == -1 || maxRow == -1) {255return;256}257258int col0 = table.convertColumnIndexToView(COLUMN_FILENAME);259260Rectangle first = table.getCellRect(minRow, col0, false);261Rectangle last = table.getCellRect(maxRow, col0, false);262Rectangle dirty = first.union(last);263table.repaint(dirty);264}265};266267private boolean smallIconsView = false;268private Border listViewBorder;269private Color listViewBackground;270private boolean listViewWindowsStyle;271private boolean readOnly;272private boolean fullRowSelection = false;273274private ListSelectionModel listSelectionModel;275private JList list;276private JTable detailsTable;277278private static final int COLUMN_FILENAME = 0;279280// Provides a way to recognize a newly created folder, so it can281// be selected when it appears in the model.282private File newFolderFile;283284// Used for accessing methods in the corresponding UI class285private FileChooserUIAccessor fileChooserUIAccessor;286private DetailsTableModel detailsTableModel;287private DetailsTableRowSorter rowSorter;288289public FilePane(FileChooserUIAccessor fileChooserUIAccessor) {290super(new BorderLayout());291292this.fileChooserUIAccessor = fileChooserUIAccessor;293294installDefaults();295createActionMap();296}297298public void uninstallUI() {299if (getModel() != null) {300getModel().removePropertyChangeListener(this);301}302}303304protected JFileChooser getFileChooser() {305return fileChooserUIAccessor.getFileChooser();306}307308protected BasicDirectoryModel getModel() {309return fileChooserUIAccessor.getModel();310}311312public int getViewType() {313return viewType;314}315316public void setViewType(int viewType) {317if (viewType == this.viewType) {318return;319}320321int oldValue = this.viewType;322this.viewType = viewType;323324JPanel createdViewPanel = null;325Component newFocusOwner = null;326327switch (viewType) {328case VIEWTYPE_LIST:329if (viewPanels[viewType] == null) {330createdViewPanel = fileChooserUIAccessor.createList();331if (createdViewPanel == null) {332createdViewPanel = createList();333}334335list = (JList) findChildComponent(createdViewPanel, JList.class);336if (listSelectionModel == null) {337listSelectionModel = list.getSelectionModel();338if (detailsTable != null) {339detailsTable.setSelectionModel(listSelectionModel);340}341} else {342list.setSelectionModel(listSelectionModel);343}344}345list.setLayoutOrientation(JList.VERTICAL_WRAP);346newFocusOwner = list;347break;348349case VIEWTYPE_DETAILS:350if (viewPanels[viewType] == null) {351createdViewPanel = fileChooserUIAccessor.createDetailsView();352if (createdViewPanel == null) {353createdViewPanel = createDetailsView();354}355356detailsTable = (JTable) findChildComponent(createdViewPanel, JTable.class);357detailsTable.setRowHeight(Math.max(detailsTable.getFont().getSize() + 4, 16 + 1));358if (listSelectionModel != null) {359detailsTable.setSelectionModel(listSelectionModel);360}361}362newFocusOwner = detailsTable;363break;364}365366if (createdViewPanel != null) {367viewPanels[viewType] = createdViewPanel;368recursivelySetInheritsPopupMenu(createdViewPanel, true);369}370371boolean isFocusOwner = false;372373if (currentViewPanel != null) {374Component owner = DefaultKeyboardFocusManager.375getCurrentKeyboardFocusManager().getPermanentFocusOwner();376377isFocusOwner = owner == detailsTable || owner == list;378379remove(currentViewPanel);380}381382currentViewPanel = viewPanels[viewType];383add(currentViewPanel, BorderLayout.CENTER);384385if (isFocusOwner && newFocusOwner != null) {386newFocusOwner.requestFocusInWindow();387}388389revalidate();390repaint();391updateViewMenu();392firePropertyChange("viewType", oldValue, viewType);393}394395class ViewTypeAction extends AbstractAction {396private int viewType;397398ViewTypeAction(int viewType) {399super(viewTypeActionNames[viewType]);400this.viewType = viewType;401402String cmd;403switch (viewType) {404case VIEWTYPE_LIST: cmd = ACTION_VIEW_LIST; break;405case VIEWTYPE_DETAILS: cmd = ACTION_VIEW_DETAILS; break;406default: cmd = (String)getValue(Action.NAME);407}408putValue(Action.ACTION_COMMAND_KEY, cmd);409}410411public void actionPerformed(ActionEvent e) {412setViewType(viewType);413}414}415416public Action getViewTypeAction(int viewType) {417return new ViewTypeAction(viewType);418}419420private static void recursivelySetInheritsPopupMenu(Container container, boolean b) {421if (container instanceof JComponent) {422((JComponent)container).setInheritsPopupMenu(b);423}424int n = container.getComponentCount();425for (int i = 0; i < n; i++) {426recursivelySetInheritsPopupMenu((Container)container.getComponent(i), b);427}428}429430protected void installDefaults() {431Locale l = getFileChooser().getLocale();432433listViewBorder = UIManager.getBorder("FileChooser.listViewBorder");434listViewBackground = UIManager.getColor("FileChooser.listViewBackground");435listViewWindowsStyle = UIManager.getBoolean("FileChooser.listViewWindowsStyle");436readOnly = UIManager.getBoolean("FileChooser.readOnly");437438// TODO: On windows, get the following localized strings from the OS439440viewMenuLabelText =441UIManager.getString("FileChooser.viewMenuLabelText", l);442refreshActionLabelText =443UIManager.getString("FileChooser.refreshActionLabelText", l);444newFolderActionLabelText =445UIManager.getString("FileChooser.newFolderActionLabelText", l);446447viewTypeActionNames = new String[VIEWTYPE_COUNT];448viewTypeActionNames[VIEWTYPE_LIST] =449UIManager.getString("FileChooser.listViewActionLabelText", l);450viewTypeActionNames[VIEWTYPE_DETAILS] =451UIManager.getString("FileChooser.detailsViewActionLabelText", l);452453kiloByteString = UIManager.getString("FileChooser.fileSizeKiloBytes", l);454megaByteString = UIManager.getString("FileChooser.fileSizeMegaBytes", l);455gigaByteString = UIManager.getString("FileChooser.fileSizeGigaBytes", l);456fullRowSelection = UIManager.getBoolean("FileView.fullRowSelection");457458filesListAccessibleName = UIManager.getString("FileChooser.filesListAccessibleName", l);459filesDetailsAccessibleName = UIManager.getString("FileChooser.filesDetailsAccessibleName", l);460461renameErrorTitleText = UIManager.getString("FileChooser.renameErrorTitleText", l);462renameErrorText = UIManager.getString("FileChooser.renameErrorText", l);463renameErrorFileExistsText = UIManager.getString("FileChooser.renameErrorFileExistsText", l);464}465466/**467* Fetches the command list for the FilePane. These commands468* are useful for binding to events, such as in a keymap.469*470* @return the command list471*/472public Action[] getActions() {473if (actions == null) {474class FilePaneAction extends AbstractAction {475FilePaneAction(String name) {476this(name, name);477}478479FilePaneAction(String name, String cmd) {480super(name);481putValue(Action.ACTION_COMMAND_KEY, cmd);482}483484public void actionPerformed(ActionEvent e) {485String cmd = (String)getValue(Action.ACTION_COMMAND_KEY);486487if (cmd == ACTION_CANCEL) {488if (editFile != null) {489cancelEdit();490} else {491getFileChooser().cancelSelection();492}493} else if (cmd == ACTION_EDIT_FILE_NAME) {494JFileChooser fc = getFileChooser();495int index = listSelectionModel.getMinSelectionIndex();496if (index >= 0 && editFile == null &&497(!fc.isMultiSelectionEnabled() ||498fc.getSelectedFiles().length <= 1)) {499500editFileName(index);501}502} else if (cmd == ACTION_REFRESH) {503getFileChooser().rescanCurrentDirectory();504}505}506507public boolean isEnabled() {508String cmd = (String)getValue(Action.ACTION_COMMAND_KEY);509if (cmd == ACTION_CANCEL) {510return getFileChooser().isEnabled();511} else if (cmd == ACTION_EDIT_FILE_NAME) {512return !readOnly && getFileChooser().isEnabled();513} else {514return true;515}516}517}518519ArrayList<Action> actionList = new ArrayList<Action>(8);520Action action;521522actionList.add(new FilePaneAction(ACTION_CANCEL));523actionList.add(new FilePaneAction(ACTION_EDIT_FILE_NAME));524actionList.add(new FilePaneAction(refreshActionLabelText, ACTION_REFRESH));525526action = fileChooserUIAccessor.getApproveSelectionAction();527if (action != null) {528actionList.add(action);529}530action = fileChooserUIAccessor.getChangeToParentDirectoryAction();531if (action != null) {532actionList.add(action);533}534action = getNewFolderAction();535if (action != null) {536actionList.add(action);537}538action = getViewTypeAction(VIEWTYPE_LIST);539if (action != null) {540actionList.add(action);541}542action = getViewTypeAction(VIEWTYPE_DETAILS);543if (action != null) {544actionList.add(action);545}546actions = actionList.toArray(new Action[actionList.size()]);547}548549return actions;550}551552protected void createActionMap() {553addActionsToMap(super.getActionMap(), getActions());554}555556557public static void addActionsToMap(ActionMap map, Action[] actions) {558if (map != null && actions != null) {559for (Action a : actions) {560String cmd = (String)a.getValue(Action.ACTION_COMMAND_KEY);561if (cmd == null) {562cmd = (String)a.getValue(Action.NAME);563}564map.put(cmd, a);565}566}567}568569570private void updateListRowCount(JList list) {571if (smallIconsView) {572list.setVisibleRowCount(getModel().getSize() / 3);573} else {574list.setVisibleRowCount(-1);575}576}577578public JPanel createList() {579JPanel p = new JPanel(new BorderLayout());580final JFileChooser fileChooser = getFileChooser();581final JList<Object> list = new JList<Object>() {582public int getNextMatch(String prefix, int startIndex, Position.Bias bias) {583ListModel model = getModel();584int max = model.getSize();585if (prefix == null || startIndex < 0 || startIndex >= max) {586throw new IllegalArgumentException();587}588// start search from the next element before/after the selected element589boolean backwards = (bias == Position.Bias.Backward);590for (int i = startIndex; backwards ? i >= 0 : i < max; i += (backwards ? -1 : 1)) {591String filename = fileChooser.getName((File)model.getElementAt(i));592if (filename.regionMatches(true, 0, prefix, 0, prefix.length())) {593return i;594}595}596return -1;597}598};599list.setCellRenderer(new FileRenderer());600list.setLayoutOrientation(JList.VERTICAL_WRAP);601602// 4835633 : tell BasicListUI that this is a file list603list.putClientProperty("List.isFileList", Boolean.TRUE);604605if (listViewWindowsStyle) {606list.addFocusListener(repaintListener);607}608609updateListRowCount(list);610611getModel().addListDataListener(new ListDataListener() {612public void intervalAdded(ListDataEvent e) {613updateListRowCount(list);614}615public void intervalRemoved(ListDataEvent e) {616updateListRowCount(list);617}618public void contentsChanged(ListDataEvent e) {619if (isShowing()) {620clearSelection();621}622updateListRowCount(list);623}624});625626getModel().addPropertyChangeListener(this);627628if (fileChooser.isMultiSelectionEnabled()) {629list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);630} else {631list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);632}633list.setModel(new SortableListModel());634635list.addListSelectionListener(createListSelectionListener());636list.addMouseListener(getMouseHandler());637638JScrollPane scrollpane = new JScrollPane(list);639if (listViewBackground != null) {640list.setBackground(listViewBackground);641}642if (listViewBorder != null) {643scrollpane.setBorder(listViewBorder);644}645646list.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesListAccessibleName);647648p.add(scrollpane, BorderLayout.CENTER);649return p;650}651652/**653* This model allows for sorting JList654*/655private class SortableListModel extends AbstractListModel<Object>656implements TableModelListener, RowSorterListener {657658public SortableListModel() {659getDetailsTableModel().addTableModelListener(this);660getRowSorter().addRowSorterListener(this);661}662663public int getSize() {664return getModel().getSize();665}666667public Object getElementAt(int index) {668// JList doesn't support RowSorter so far, so we put it into the list model669return getModel().getElementAt(getRowSorter().convertRowIndexToModel(index));670}671672public void tableChanged(TableModelEvent e) {673fireContentsChanged(this, 0, getSize());674}675676public void sorterChanged(RowSorterEvent e) {677fireContentsChanged(this, 0, getSize());678}679}680681private DetailsTableModel getDetailsTableModel() {682if(detailsTableModel == null) {683detailsTableModel = new DetailsTableModel(getFileChooser());684}685return detailsTableModel;686}687688class DetailsTableModel extends AbstractTableModel implements ListDataListener {689JFileChooser chooser;690BasicDirectoryModel directoryModel;691692ShellFolderColumnInfo[] columns;693int[] columnMap;694695DetailsTableModel(JFileChooser fc) {696this.chooser = fc;697directoryModel = getModel();698directoryModel.addListDataListener(this);699700updateColumnInfo();701}702703void updateColumnInfo() {704File dir = chooser.getCurrentDirectory();705if (dir != null && usesShellFolder(chooser)) {706try {707dir = ShellFolder.getShellFolder(dir);708} catch (FileNotFoundException e) {709// Leave dir without changing710}711}712713ShellFolderColumnInfo[] allColumns = ShellFolder.getFolderColumns(dir);714715ArrayList<ShellFolderColumnInfo> visibleColumns =716new ArrayList<ShellFolderColumnInfo>();717columnMap = new int[allColumns.length];718for (int i = 0; i < allColumns.length; i++) {719ShellFolderColumnInfo column = allColumns[i];720if (column.isVisible()) {721columnMap[visibleColumns.size()] = i;722visibleColumns.add(column);723}724}725726columns = new ShellFolderColumnInfo[visibleColumns.size()];727visibleColumns.toArray(columns);728columnMap = Arrays.copyOf(columnMap, columns.length);729730List<? extends RowSorter.SortKey> sortKeys =731(rowSorter == null) ? null : rowSorter.getSortKeys();732fireTableStructureChanged();733restoreSortKeys(sortKeys);734}735736private void restoreSortKeys(List<? extends RowSorter.SortKey> sortKeys) {737if (sortKeys != null) {738// check if preserved sortKeys are valid for this folder739for (int i = 0; i < sortKeys.size(); i++) {740RowSorter.SortKey sortKey = sortKeys.get(i);741if (sortKey.getColumn() >= columns.length) {742sortKeys = null;743break;744}745}746if (sortKeys != null) {747rowSorter.setSortKeys(sortKeys);748}749}750}751752public int getRowCount() {753return directoryModel.getSize();754}755756public int getColumnCount() {757return columns.length;758}759760public Object getValueAt(int row, int col) {761// Note: It is very important to avoid getting info on drives, as762// this will trigger "No disk in A:" and similar dialogs.763//764// Use (f.exists() && !chooser.getFileSystemView().isFileSystemRoot(f)) to765// determine if it is safe to call methods directly on f.766return getFileColumnValue((File)directoryModel.getElementAt(row), col);767}768769private Object getFileColumnValue(File f, int col) {770return (col == COLUMN_FILENAME)771? f // always return the file itself for the 1st column772: ShellFolder.getFolderColumnValue(f, columnMap[col]);773}774775public void setValueAt(Object value, int row, int col) {776if (col == COLUMN_FILENAME) {777final JFileChooser chooser = getFileChooser();778File f = (File)getValueAt(row, col);779if (f != null) {780String oldDisplayName = chooser.getName(f);781String oldFileName = f.getName();782String newDisplayName = ((String)value).trim();783String newFileName;784785if (!newDisplayName.equals(oldDisplayName)) {786newFileName = newDisplayName;787//Check if extension is hidden from user788int i1 = oldFileName.length();789int i2 = oldDisplayName.length();790if (i1 > i2 && oldFileName.charAt(i2) == '.') {791newFileName = newDisplayName + oldFileName.substring(i2);792}793794// rename795FileSystemView fsv = chooser.getFileSystemView();796final File f2 = fsv.createFileObject(f.getParentFile(), newFileName);797if (f2.exists()) {798JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorFileExistsText,799oldFileName), renameErrorTitleText, JOptionPane.ERROR_MESSAGE);800} else {801if (FilePane.this.getModel().renameFile(f, f2)) {802if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {803// The setSelectedFile method produces a new setValueAt invocation while the JTable804// is editing. Postpone file selection to be sure that edit mode of the JTable805// is completed806SwingUtilities.invokeLater(new Runnable() {807public void run() {808if (chooser.isMultiSelectionEnabled()) {809chooser.setSelectedFiles(new File[]{f2});810} else {811chooser.setSelectedFile(f2);812}813}814});815} else {816// Could be because of delay in updating Desktop folder817// chooser.setSelectedFile(null);818}819} else {820JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorText, oldFileName),821renameErrorTitleText, JOptionPane.ERROR_MESSAGE);822}823}824}825}826}827}828829public boolean isCellEditable(int row, int column) {830File currentDirectory = getFileChooser().getCurrentDirectory();831return (!readOnly && column == COLUMN_FILENAME && canWrite(currentDirectory));832}833834public void contentsChanged(ListDataEvent e) {835// Update the selection after the model has been updated836new DelayedSelectionUpdater();837fireTableDataChanged();838}839840public void intervalAdded(ListDataEvent e) {841int i0 = e.getIndex0();842int i1 = e.getIndex1();843if (i0 == i1) {844File file = (File)getModel().getElementAt(i0);845if (file.equals(newFolderFile)) {846new DelayedSelectionUpdater(file);847newFolderFile = null;848}849}850851fireTableRowsInserted(e.getIndex0(), e.getIndex1());852}853public void intervalRemoved(ListDataEvent e) {854fireTableRowsDeleted(e.getIndex0(), e.getIndex1());855}856857public ShellFolderColumnInfo[] getColumns() {858return columns;859}860}861862863private void updateDetailsColumnModel(JTable table) {864if (table != null) {865ShellFolderColumnInfo[] columns = detailsTableModel.getColumns();866867TableColumnModel columnModel = new DefaultTableColumnModel();868for (int i = 0; i < columns.length; i++) {869ShellFolderColumnInfo dataItem = columns[i];870TableColumn column = new TableColumn(i);871872String title = dataItem.getTitle();873if (title != null && title.startsWith("FileChooser.") && title.endsWith("HeaderText")) {874// the column must have a string resource that we try to get875String uiTitle = UIManager.getString(title, table.getLocale());876if (uiTitle != null) {877title = uiTitle;878}879}880column.setHeaderValue(title);881882Integer width = dataItem.getWidth();883if (width != null) {884column.setPreferredWidth(width);885// otherwise we let JTable to decide the actual width886}887888columnModel.addColumn(column);889}890891// Install cell editor for editing file name892if (!readOnly && columnModel.getColumnCount() > COLUMN_FILENAME) {893columnModel.getColumn(COLUMN_FILENAME).894setCellEditor(getDetailsTableCellEditor());895}896897table.setColumnModel(columnModel);898}899}900901private DetailsTableRowSorter getRowSorter() {902if (rowSorter == null) {903rowSorter = new DetailsTableRowSorter();904}905return rowSorter;906}907908private class DetailsTableRowSorter extends TableRowSorter<TableModel> {909public DetailsTableRowSorter() {910setModelWrapper(new SorterModelWrapper());911}912913public void updateComparators(ShellFolderColumnInfo [] columns) {914for (int i = 0; i < columns.length; i++) {915Comparator c = columns[i].getComparator();916if (c != null) {917c = new DirectoriesFirstComparatorWrapper(i, c);918}919setComparator(i, c);920}921}922923@Override924public void sort() {925ShellFolder.invoke(new Callable<Void>() {926public Void call() {927DetailsTableRowSorter.super.sort();928return null;929}930});931}932933public void modelStructureChanged() {934super.modelStructureChanged();935updateComparators(detailsTableModel.getColumns());936}937938private class SorterModelWrapper extends ModelWrapper<TableModel, Integer> {939public TableModel getModel() {940return getDetailsTableModel();941}942943public int getColumnCount() {944return getDetailsTableModel().getColumnCount();945}946947public int getRowCount() {948return getDetailsTableModel().getRowCount();949}950951public Object getValueAt(int row, int column) {952return FilePane.this.getModel().getElementAt(row);953}954955public Integer getIdentifier(int row) {956return row;957}958}959}960961/**962* This class sorts directories before files, comparing directory to963* directory and file to file using the wrapped comparator.964*/965private class DirectoriesFirstComparatorWrapper implements Comparator<File> {966private Comparator comparator;967private int column;968969public DirectoriesFirstComparatorWrapper(int column, Comparator comparator) {970this.column = column;971this.comparator = comparator;972}973974public int compare(File f1, File f2) {975if (f1 != null && f2 != null) {976boolean traversable1 = getFileChooser().isTraversable(f1);977boolean traversable2 = getFileChooser().isTraversable(f2);978// directories go first979if (traversable1 && !traversable2) {980return -1;981}982if (!traversable1 && traversable2) {983return 1;984}985}986if (detailsTableModel.getColumns()[column].isCompareByColumn()) {987return comparator.compare(988getDetailsTableModel().getFileColumnValue(f1, column),989getDetailsTableModel().getFileColumnValue(f2, column)990);991}992// For this column we need to pass the file itself (not a993// column value) to the comparator994return comparator.compare(f1, f2);995}996}997998private DetailsTableCellEditor tableCellEditor;9991000private DetailsTableCellEditor getDetailsTableCellEditor() {1001if (tableCellEditor == null) {1002tableCellEditor = new DetailsTableCellEditor(new JTextField());1003}1004return tableCellEditor;1005}10061007private class DetailsTableCellEditor extends DefaultCellEditor {1008private final JTextField tf;10091010public DetailsTableCellEditor(JTextField tf) {1011super(tf);1012this.tf = tf;1013tf.setName("Table.editor");1014tf.addFocusListener(editorFocusListener);1015}10161017public Component getTableCellEditorComponent(JTable table, Object value,1018boolean isSelected, int row, int column) {1019Component comp = super.getTableCellEditorComponent(table, value,1020isSelected, row, column);1021if (value instanceof File) {1022tf.setText(getFileChooser().getName((File) value));1023tf.selectAll();1024}1025return comp;1026}1027}102810291030class DetailsTableCellRenderer extends DefaultTableCellRenderer {1031JFileChooser chooser;1032DateFormat df;10331034DetailsTableCellRenderer(JFileChooser chooser) {1035this.chooser = chooser;1036df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT,1037chooser.getLocale());1038}10391040public void setBounds(int x, int y, int width, int height) {1041if (getHorizontalAlignment() == SwingConstants.LEADING &&1042!fullRowSelection) {1043// Restrict width to actual text1044width = Math.min(width, this.getPreferredSize().width+4);1045} else {1046x -= 4;1047}1048super.setBounds(x, y, width, height);1049}105010511052public Insets getInsets(Insets i) {1053// Provide some space between columns1054i = super.getInsets(i);1055i.left += 4;1056i.right += 4;1057return i;1058}10591060public Component getTableCellRendererComponent(JTable table, Object value,1061boolean isSelected, boolean hasFocus, int row, int column) {10621063if ((table.convertColumnIndexToModel(column) != COLUMN_FILENAME ||1064(listViewWindowsStyle && !table.isFocusOwner())) &&1065!fullRowSelection) {1066isSelected = false;1067}10681069super.getTableCellRendererComponent(table, value, isSelected,1070hasFocus, row, column);10711072setIcon(null);10731074int modelColumn = table.convertColumnIndexToModel(column);1075ShellFolderColumnInfo columnInfo = detailsTableModel.getColumns()[modelColumn];10761077Integer alignment = columnInfo.getAlignment();1078if (alignment == null) {1079alignment = (value instanceof Number)1080? SwingConstants.RIGHT1081: SwingConstants.LEADING;1082}10831084setHorizontalAlignment(alignment);10851086// formatting cell text1087// TODO: it's rather a temporary trick, to be revised1088String text;10891090if (value == null) {1091text = "";10921093} else if (value instanceof File) {1094File file = (File)value;1095text = chooser.getName(file);1096Icon icon = chooser.getIcon(file);1097setIcon(icon);10981099} else if (value instanceof Long) {1100long len = ((Long) value) / 1024L;1101if (listViewWindowsStyle) {1102text = MessageFormat.format(kiloByteString, len + 1);1103} else if (len < 1024L) {1104text = MessageFormat.format(kiloByteString, (len == 0L) ? 1L : len);1105} else {1106len /= 1024L;1107if (len < 1024L) {1108text = MessageFormat.format(megaByteString, len);1109} else {1110len /= 1024L;1111text = MessageFormat.format(gigaByteString, len);1112}1113}11141115} else if (value instanceof Date) {1116text = df.format((Date)value);11171118} else {1119text = value.toString();1120}11211122setText(text);11231124return this;1125}1126}11271128public JPanel createDetailsView() {1129final JFileChooser chooser = getFileChooser();11301131JPanel p = new JPanel(new BorderLayout());11321133final JTable detailsTable = new JTable(getDetailsTableModel()) {1134// Handle Escape key events here1135protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {1136if (e.getKeyCode() == KeyEvent.VK_ESCAPE && getCellEditor() == null) {1137// We are not editing, forward to filechooser.1138chooser.dispatchEvent(e);1139return true;1140}1141return super.processKeyBinding(ks, e, condition, pressed);1142}11431144public void tableChanged(TableModelEvent e) {1145super.tableChanged(e);11461147if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {1148// update header with possibly changed column set1149updateDetailsColumnModel(this);1150}1151}1152};11531154detailsTable.setRowSorter(getRowSorter());1155detailsTable.setAutoCreateColumnsFromModel(false);1156detailsTable.setComponentOrientation(chooser.getComponentOrientation());1157detailsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);1158detailsTable.setShowGrid(false);1159detailsTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);1160detailsTable.addKeyListener(detailsKeyListener);11611162Font font = list.getFont();1163detailsTable.setFont(font);1164detailsTable.setIntercellSpacing(new Dimension(0, 0));11651166TableCellRenderer headerRenderer =1167new AlignableTableHeaderRenderer(detailsTable.getTableHeader().getDefaultRenderer());1168detailsTable.getTableHeader().setDefaultRenderer(headerRenderer);1169TableCellRenderer cellRenderer = new DetailsTableCellRenderer(chooser);1170detailsTable.setDefaultRenderer(Object.class, cellRenderer);11711172// So that drag can be started on a mouse press1173detailsTable.getColumnModel().getSelectionModel().1174setSelectionMode(ListSelectionModel.SINGLE_SELECTION);11751176detailsTable.addMouseListener(getMouseHandler());1177// No need to addListSelectionListener because selections are forwarded1178// to our JList.11791180// 4835633 : tell BasicTableUI that this is a file list1181detailsTable.putClientProperty("Table.isFileList", Boolean.TRUE);11821183if (listViewWindowsStyle) {1184detailsTable.addFocusListener(repaintListener);1185}11861187// TAB/SHIFT-TAB should transfer focus and ENTER should select an item.1188// We don't want them to navigate within the table1189ActionMap am = SwingUtilities.getUIActionMap(detailsTable);1190am.remove("selectNextRowCell");1191am.remove("selectPreviousRowCell");1192am.remove("selectNextColumnCell");1193am.remove("selectPreviousColumnCell");1194detailsTable.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,1195null);1196detailsTable.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,1197null);11981199JScrollPane scrollpane = new JScrollPane(detailsTable);1200scrollpane.setComponentOrientation(chooser.getComponentOrientation());1201LookAndFeel.installColors(scrollpane.getViewport(), "Table.background", "Table.foreground");12021203// Adjust width of first column so the table fills the viewport when1204// first displayed (temporary listener).1205scrollpane.addComponentListener(new ComponentAdapter() {1206public void componentResized(ComponentEvent e) {1207JScrollPane sp = (JScrollPane)e.getComponent();1208fixNameColumnWidth(sp.getViewport().getSize().width);1209sp.removeComponentListener(this);1210}1211});12121213// 4835633.1214// If the mouse is pressed in the area below the Details view table, the1215// event is not dispatched to the Table MouseListener but to the1216// scrollpane. Listen for that here so we can clear the selection.1217scrollpane.addMouseListener(new MouseAdapter() {1218public void mousePressed(MouseEvent e) {1219JScrollPane jsp = ((JScrollPane)e.getComponent());1220JTable table = (JTable)jsp.getViewport().getView();12211222if (!e.isShiftDown() || table.getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) {1223clearSelection();1224TableCellEditor tce = table.getCellEditor();1225if (tce != null) {1226tce.stopCellEditing();1227}1228}1229}1230});12311232detailsTable.setForeground(list.getForeground());1233detailsTable.setBackground(list.getBackground());12341235if (listViewBorder != null) {1236scrollpane.setBorder(listViewBorder);1237}1238p.add(scrollpane, BorderLayout.CENTER);12391240detailsTableModel.fireTableStructureChanged();12411242detailsTable.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesDetailsAccessibleName);12431244return p;1245} // createDetailsView12461247private class AlignableTableHeaderRenderer implements TableCellRenderer {1248TableCellRenderer wrappedRenderer;12491250public AlignableTableHeaderRenderer(TableCellRenderer wrappedRenderer) {1251this.wrappedRenderer = wrappedRenderer;1252}12531254public Component getTableCellRendererComponent(1255JTable table, Object value, boolean isSelected,1256boolean hasFocus, int row, int column) {12571258Component c = wrappedRenderer.getTableCellRendererComponent(1259table, value, isSelected, hasFocus, row, column);12601261int modelColumn = table.convertColumnIndexToModel(column);1262ShellFolderColumnInfo columnInfo = detailsTableModel.getColumns()[modelColumn];12631264Integer alignment = columnInfo.getAlignment();1265if (alignment == null) {1266alignment = SwingConstants.CENTER;1267}1268if (c instanceof JLabel) {1269((JLabel) c).setHorizontalAlignment(alignment);1270}12711272return c;1273}1274}12751276private void fixNameColumnWidth(int viewWidth) {1277TableColumn nameCol = detailsTable.getColumnModel().getColumn(COLUMN_FILENAME);1278int tableWidth = detailsTable.getPreferredSize().width;12791280if (tableWidth < viewWidth) {1281nameCol.setPreferredWidth(nameCol.getPreferredWidth() + viewWidth - tableWidth);1282}1283}12841285private class DelayedSelectionUpdater implements Runnable {1286File editFile;12871288DelayedSelectionUpdater() {1289this(null);1290}12911292DelayedSelectionUpdater(File editFile) {1293this.editFile = editFile;1294if (isShowing()) {1295SwingUtilities.invokeLater(this);1296}1297}12981299public void run() {1300setFileSelected();1301if (editFile != null) {1302editFileName(getRowSorter().convertRowIndexToView(1303getModel().indexOf(editFile)));1304editFile = null;1305}1306}1307}130813091310/**1311* Creates a selection listener for the list of files and directories.1312*1313* @return a <code>ListSelectionListener</code>1314*/1315public ListSelectionListener createListSelectionListener() {1316return fileChooserUIAccessor.createListSelectionListener();1317}13181319int lastIndex = -1;1320File editFile = null;13211322private int getEditIndex() {1323return lastIndex;1324}13251326private void setEditIndex(int i) {1327lastIndex = i;1328}13291330private void resetEditIndex() {1331lastIndex = -1;1332}13331334private void cancelEdit() {1335if (editFile != null) {1336editFile = null;1337list.remove(editCell);1338repaint();1339} else if (detailsTable != null && detailsTable.isEditing()) {1340detailsTable.getCellEditor().cancelCellEditing();1341}1342}13431344JTextField editCell = null;13451346/**1347* @param index visual index of the file to be edited1348*/1349private void editFileName(int index) {1350JFileChooser chooser = getFileChooser();1351File currentDirectory = chooser.getCurrentDirectory();13521353if (readOnly || !canWrite(currentDirectory)) {1354return;1355}13561357ensureIndexIsVisible(index);1358switch (viewType) {1359case VIEWTYPE_LIST:1360editFile = (File)getModel().getElementAt(getRowSorter().convertRowIndexToModel(index));1361Rectangle r = list.getCellBounds(index, index);1362if (editCell == null) {1363editCell = new JTextField();1364editCell.setName("Tree.cellEditor");1365editCell.addActionListener(new EditActionListener());1366editCell.addFocusListener(editorFocusListener);1367editCell.setNextFocusableComponent(list);1368}1369list.add(editCell);1370editCell.setText(chooser.getName(editFile));1371ComponentOrientation orientation = list.getComponentOrientation();1372editCell.setComponentOrientation(orientation);13731374Icon icon = chooser.getIcon(editFile);13751376// PENDING - grab padding (4) below from defaults table.1377int editX = icon == null ? 20 : icon.getIconWidth() + 4;13781379if (orientation.isLeftToRight()) {1380editCell.setBounds(editX + r.x, r.y, r.width - editX, r.height);1381} else {1382editCell.setBounds(r.x, r.y, r.width - editX, r.height);1383}1384editCell.requestFocus();1385editCell.selectAll();1386break;13871388case VIEWTYPE_DETAILS:1389detailsTable.editCellAt(index, COLUMN_FILENAME);1390break;1391}1392}139313941395class EditActionListener implements ActionListener {1396public void actionPerformed(ActionEvent e) {1397applyEdit();1398}1399}14001401private void applyEdit() {1402if (editFile != null && editFile.exists()) {1403JFileChooser chooser = getFileChooser();1404String oldDisplayName = chooser.getName(editFile);1405String oldFileName = editFile.getName();1406String newDisplayName = editCell.getText().trim();1407String newFileName;14081409if (!newDisplayName.equals(oldDisplayName)) {1410newFileName = newDisplayName;1411//Check if extension is hidden from user1412int i1 = oldFileName.length();1413int i2 = oldDisplayName.length();1414if (i1 > i2 && oldFileName.charAt(i2) == '.') {1415newFileName = newDisplayName + oldFileName.substring(i2);1416}14171418// rename1419FileSystemView fsv = chooser.getFileSystemView();1420File f2 = fsv.createFileObject(editFile.getParentFile(), newFileName);1421if (f2.exists()) {1422JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorFileExistsText, oldFileName),1423renameErrorTitleText, JOptionPane.ERROR_MESSAGE);1424} else {1425if (getModel().renameFile(editFile, f2)) {1426if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {1427if (chooser.isMultiSelectionEnabled()) {1428chooser.setSelectedFiles(new File[]{f2});1429} else {1430chooser.setSelectedFile(f2);1431}1432} else {1433//Could be because of delay in updating Desktop folder1434//chooser.setSelectedFile(null);1435}1436} else {1437JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorText, oldFileName),1438renameErrorTitleText, JOptionPane.ERROR_MESSAGE);1439}1440}1441}1442}1443if (detailsTable != null && detailsTable.isEditing()) {1444detailsTable.getCellEditor().stopCellEditing();1445}1446cancelEdit();1447}14481449protected Action newFolderAction;14501451public Action getNewFolderAction() {1452if (!readOnly && newFolderAction == null) {1453newFolderAction = new AbstractAction(newFolderActionLabelText) {1454private Action basicNewFolderAction;14551456// Initializer1457{1458putValue(Action.ACTION_COMMAND_KEY, FilePane.ACTION_NEW_FOLDER);14591460File currentDirectory = getFileChooser().getCurrentDirectory();1461if (currentDirectory != null) {1462setEnabled(canWrite(currentDirectory));1463}1464}14651466public void actionPerformed(ActionEvent ev) {1467if (basicNewFolderAction == null) {1468basicNewFolderAction = fileChooserUIAccessor.getNewFolderAction();1469}1470JFileChooser fc = getFileChooser();1471File oldFile = fc.getSelectedFile();1472basicNewFolderAction.actionPerformed(ev);1473File newFile = fc.getSelectedFile();1474if (newFile != null && !newFile.equals(oldFile) && newFile.isDirectory()) {1475newFolderFile = newFile;1476}1477}1478};1479}1480return newFolderAction;1481}14821483protected class FileRenderer extends DefaultListCellRenderer {14841485public Component getListCellRendererComponent(JList list, Object value,1486int index, boolean isSelected,1487boolean cellHasFocus) {14881489if (listViewWindowsStyle && !list.isFocusOwner()) {1490isSelected = false;1491}14921493super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);1494File file = (File) value;1495String fileName = getFileChooser().getName(file);1496setText(fileName);1497setFont(list.getFont());14981499Icon icon = getFileChooser().getIcon(file);1500if (icon != null) {1501setIcon(icon);1502} else {1503if (getFileChooser().getFileSystemView().isTraversable(file)) {1504setText(fileName+File.separator);1505}1506}15071508return this;1509}1510}151115121513void setFileSelected() {1514if (getFileChooser().isMultiSelectionEnabled() && !isDirectorySelected()) {1515File[] files = getFileChooser().getSelectedFiles(); // Should be selected1516Object[] selectedObjects = list.getSelectedValues(); // Are actually selected15171518listSelectionModel.setValueIsAdjusting(true);1519try {1520int lead = listSelectionModel.getLeadSelectionIndex();1521int anchor = listSelectionModel.getAnchorSelectionIndex();15221523Arrays.sort(files);1524Arrays.sort(selectedObjects);15251526int shouldIndex = 0;1527int actuallyIndex = 0;15281529// Remove files that shouldn't be selected and add files which should be selected1530// Note: Assume files are already sorted in compareTo order.1531while (shouldIndex < files.length &&1532actuallyIndex < selectedObjects.length) {1533int comparison = files[shouldIndex].compareTo((File)selectedObjects[actuallyIndex]);1534if (comparison < 0) {1535doSelectFile(files[shouldIndex++]);1536} else if (comparison > 0) {1537doDeselectFile(selectedObjects[actuallyIndex++]);1538} else {1539// Do nothing1540shouldIndex++;1541actuallyIndex++;1542}15431544}15451546while (shouldIndex < files.length) {1547doSelectFile(files[shouldIndex++]);1548}15491550while (actuallyIndex < selectedObjects.length) {1551doDeselectFile(selectedObjects[actuallyIndex++]);1552}15531554// restore the anchor and lead1555if (listSelectionModel instanceof DefaultListSelectionModel) {1556((DefaultListSelectionModel)listSelectionModel).1557moveLeadSelectionIndex(lead);1558listSelectionModel.setAnchorSelectionIndex(anchor);1559}1560} finally {1561listSelectionModel.setValueIsAdjusting(false);1562}1563} else {1564JFileChooser chooser = getFileChooser();1565File f;1566if (isDirectorySelected()) {1567f = getDirectory();1568} else {1569f = chooser.getSelectedFile();1570}1571int i;1572if (f != null && (i = getModel().indexOf(f)) >= 0) {1573int viewIndex = getRowSorter().convertRowIndexToView(i);1574listSelectionModel.setSelectionInterval(viewIndex, viewIndex);1575ensureIndexIsVisible(viewIndex);1576} else {1577clearSelection();1578}1579}1580}15811582private void doSelectFile(File fileToSelect) {1583int index = getModel().indexOf(fileToSelect);1584// could be missed in the current directory if it changed1585if (index >= 0) {1586index = getRowSorter().convertRowIndexToView(index);1587listSelectionModel.addSelectionInterval(index, index);1588}1589}15901591private void doDeselectFile(Object fileToDeselect) {1592int index = getRowSorter().convertRowIndexToView(1593getModel().indexOf(fileToDeselect));1594listSelectionModel.removeSelectionInterval(index, index);1595}15961597/* The following methods are used by the PropertyChange Listener */15981599private void doSelectedFileChanged(PropertyChangeEvent e) {1600applyEdit();1601File f = (File) e.getNewValue();1602JFileChooser fc = getFileChooser();1603if (f != null1604&& ((fc.isFileSelectionEnabled() && !f.isDirectory())1605|| (f.isDirectory() && fc.isDirectorySelectionEnabled()))) {16061607setFileSelected();1608}1609}16101611private void doSelectedFilesChanged(PropertyChangeEvent e) {1612applyEdit();1613File[] files = (File[]) e.getNewValue();1614JFileChooser fc = getFileChooser();1615if (files != null1616&& files.length > 01617&& (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) {1618setFileSelected();1619}1620}16211622private void doDirectoryChanged(PropertyChangeEvent e) {1623getDetailsTableModel().updateColumnInfo();16241625JFileChooser fc = getFileChooser();1626FileSystemView fsv = fc.getFileSystemView();16271628applyEdit();1629resetEditIndex();1630ensureIndexIsVisible(0);1631File currentDirectory = fc.getCurrentDirectory();1632if (currentDirectory != null) {1633if (!readOnly) {1634getNewFolderAction().setEnabled(canWrite(currentDirectory));1635}1636fileChooserUIAccessor.getChangeToParentDirectoryAction().setEnabled(!fsv.isRoot(currentDirectory));1637}1638if (list != null) {1639list.clearSelection();1640}1641}16421643private void doFilterChanged(PropertyChangeEvent e) {1644applyEdit();1645resetEditIndex();1646clearSelection();1647}16481649private void doFileSelectionModeChanged(PropertyChangeEvent e) {1650applyEdit();1651resetEditIndex();1652clearSelection();1653}16541655private void doMultiSelectionChanged(PropertyChangeEvent e) {1656if (getFileChooser().isMultiSelectionEnabled()) {1657listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);1658} else {1659listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);1660clearSelection();1661getFileChooser().setSelectedFiles(null);1662}1663}16641665/*1666* Listen for filechooser property changes, such as1667* the selected file changing, or the type of the dialog changing.1668*/1669public void propertyChange(PropertyChangeEvent e) {1670if (viewType == -1) {1671setViewType(VIEWTYPE_LIST);1672}16731674String s = e.getPropertyName();1675if (s.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {1676doSelectedFileChanged(e);1677} else if (s.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) {1678doSelectedFilesChanged(e);1679} else if (s.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {1680doDirectoryChanged(e);1681} else if (s.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) {1682doFilterChanged(e);1683} else if (s.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {1684doFileSelectionModeChanged(e);1685} else if (s.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {1686doMultiSelectionChanged(e);1687} else if (s.equals(JFileChooser.CANCEL_SELECTION)) {1688applyEdit();1689} else if (s.equals("busy")) {1690setCursor((Boolean)e.getNewValue() ? waitCursor : null);1691} else if (s.equals("componentOrientation")) {1692ComponentOrientation o = (ComponentOrientation)e.getNewValue();1693JFileChooser cc = (JFileChooser)e.getSource();1694if (o != e.getOldValue()) {1695cc.applyComponentOrientation(o);1696}1697if (detailsTable != null) {1698detailsTable.setComponentOrientation(o);1699detailsTable.getParent().getParent().setComponentOrientation(o);1700}1701}1702}17031704private void ensureIndexIsVisible(int i) {1705if (i >= 0) {1706if (list != null) {1707list.ensureIndexIsVisible(i);1708}1709if (detailsTable != null) {1710detailsTable.scrollRectToVisible(detailsTable.getCellRect(i, COLUMN_FILENAME, true));1711}1712}1713}17141715public void ensureFileIsVisible(JFileChooser fc, File f) {1716int modelIndex = getModel().indexOf(f);1717if (modelIndex >= 0) {1718ensureIndexIsVisible(getRowSorter().convertRowIndexToView(modelIndex));1719}1720}17211722public void rescanCurrentDirectory() {1723getModel().validateFileCache();1724}17251726public void clearSelection() {1727if (listSelectionModel != null) {1728listSelectionModel.clearSelection();1729if (listSelectionModel instanceof DefaultListSelectionModel) {1730((DefaultListSelectionModel)listSelectionModel).moveLeadSelectionIndex(0);1731listSelectionModel.setAnchorSelectionIndex(0);1732}1733}1734}17351736public JMenu getViewMenu() {1737if (viewMenu == null) {1738viewMenu = new JMenu(viewMenuLabelText);1739ButtonGroup viewButtonGroup = new ButtonGroup();17401741for (int i = 0; i < VIEWTYPE_COUNT; i++) {1742JRadioButtonMenuItem mi =1743new JRadioButtonMenuItem(new ViewTypeAction(i));1744viewButtonGroup.add(mi);1745viewMenu.add(mi);1746}1747updateViewMenu();1748}1749return viewMenu;1750}17511752private void updateViewMenu() {1753if (viewMenu != null) {1754Component[] comps = viewMenu.getMenuComponents();1755for (Component comp : comps) {1756if (comp instanceof JRadioButtonMenuItem) {1757JRadioButtonMenuItem mi = (JRadioButtonMenuItem) comp;1758if (((ViewTypeAction)mi.getAction()).viewType == viewType) {1759mi.setSelected(true);1760}1761}1762}1763}1764}17651766public JPopupMenu getComponentPopupMenu() {1767JPopupMenu popupMenu = getFileChooser().getComponentPopupMenu();1768if (popupMenu != null) {1769return popupMenu;1770}17711772JMenu viewMenu = getViewMenu();1773if (contextMenu == null) {1774contextMenu = new JPopupMenu();1775if (viewMenu != null) {1776contextMenu.add(viewMenu);1777if (listViewWindowsStyle) {1778contextMenu.addSeparator();1779}1780}1781ActionMap actionMap = getActionMap();1782Action refreshAction = actionMap.get(ACTION_REFRESH);1783Action newFolderAction = actionMap.get(ACTION_NEW_FOLDER);1784if (refreshAction != null) {1785contextMenu.add(refreshAction);1786if (listViewWindowsStyle && newFolderAction != null) {1787contextMenu.addSeparator();1788}1789}1790if (newFolderAction != null) {1791contextMenu.add(newFolderAction);1792}1793}1794if (viewMenu != null) {1795viewMenu.getPopupMenu().setInvoker(viewMenu);1796}1797return contextMenu;1798}179918001801private Handler handler;18021803protected Handler getMouseHandler() {1804if (handler == null) {1805handler = new Handler();1806}1807return handler;1808}18091810private class Handler implements MouseListener {1811private MouseListener doubleClickListener;18121813public void mouseClicked(MouseEvent evt) {1814JComponent source = (JComponent)evt.getSource();18151816int index;1817if (source instanceof JList) {1818index = SwingUtilities2.loc2IndexFileList(list, evt.getPoint());1819} else if (source instanceof JTable) {1820JTable table = (JTable)source;1821Point p = evt.getPoint();1822index = table.rowAtPoint(p);18231824boolean pointOutsidePrefSize =1825SwingUtilities2.pointOutsidePrefSize(1826table, index, table.columnAtPoint(p), p);18271828if (pointOutsidePrefSize && !fullRowSelection) {1829return;1830}18311832// Translate point from table to list1833if (index >= 0 && list != null &&1834listSelectionModel.isSelectedIndex(index)) {18351836// Make a new event with the list as source, placing the1837// click in the corresponding list cell.1838Rectangle r = list.getCellBounds(index, index);1839MouseEvent newEvent = new MouseEvent(list, evt.getID(),1840evt.getWhen(), evt.getModifiers(),1841r.x + 1, r.y + r.height/2,1842evt.getXOnScreen(),1843evt.getYOnScreen(),1844evt.getClickCount(), evt.isPopupTrigger(),1845evt.getButton());1846MouseEventAccessor meAccessor = AWTAccessor.getMouseEventAccessor();1847meAccessor.setCausedByTouchEvent(newEvent,1848meAccessor.isCausedByTouchEvent(evt));1849evt = newEvent;1850}1851} else {1852return;1853}18541855if (index >= 0 && SwingUtilities.isLeftMouseButton(evt)) {1856JFileChooser fc = getFileChooser();18571858// For single click, we handle editing file name1859if (evt.getClickCount() == 1 && source instanceof JList) {1860if ((!fc.isMultiSelectionEnabled() || fc.getSelectedFiles().length <= 1)1861&& index >= 0 && listSelectionModel.isSelectedIndex(index)1862&& getEditIndex() == index && editFile == null) {18631864editFileName(index);1865} else {1866if (index >= 0) {1867setEditIndex(index);1868} else {1869resetEditIndex();1870}1871}1872} else if (evt.getClickCount() == 2) {1873// on double click (open or drill down one directory) be1874// sure to clear the edit index1875resetEditIndex();1876}1877}18781879// Forward event to Basic1880if (getDoubleClickListener() != null) {1881getDoubleClickListener().mouseClicked(evt);1882}1883}18841885public void mouseEntered(MouseEvent evt) {1886JComponent source = (JComponent)evt.getSource();1887if (source instanceof JTable) {1888JTable table = (JTable)evt.getSource();18891890TransferHandler th1 = getFileChooser().getTransferHandler();1891TransferHandler th2 = table.getTransferHandler();1892if (th1 != th2) {1893table.setTransferHandler(th1);1894}18951896boolean dragEnabled = getFileChooser().getDragEnabled();1897if (dragEnabled != table.getDragEnabled()) {1898table.setDragEnabled(dragEnabled);1899}1900} else if (source instanceof JList) {1901// Forward event to Basic1902if (getDoubleClickListener() != null) {1903getDoubleClickListener().mouseEntered(evt);1904}1905}1906}19071908public void mouseExited(MouseEvent evt) {1909if (evt.getSource() instanceof JList) {1910// Forward event to Basic1911if (getDoubleClickListener() != null) {1912getDoubleClickListener().mouseExited(evt);1913}1914}1915}19161917public void mousePressed(MouseEvent evt) {1918if (evt.getSource() instanceof JList) {1919// Forward event to Basic1920if (getDoubleClickListener() != null) {1921getDoubleClickListener().mousePressed(evt);1922}1923}1924}19251926public void mouseReleased(MouseEvent evt) {1927if (evt.getSource() instanceof JList) {1928// Forward event to Basic1929if (getDoubleClickListener() != null) {1930getDoubleClickListener().mouseReleased(evt);1931}1932}1933}19341935private MouseListener getDoubleClickListener() {1936// Lazy creation of Basic's listener1937if (doubleClickListener == null && list != null) {1938doubleClickListener =1939fileChooserUIAccessor.createDoubleClickListener(list);1940}1941return doubleClickListener;1942}1943}19441945/**1946* Property to remember whether a directory is currently selected in the UI.1947*1948* @return <code>true</code> iff a directory is currently selected.1949*/1950protected boolean isDirectorySelected() {1951return fileChooserUIAccessor.isDirectorySelected();1952}195319541955/**1956* Property to remember the directory that is currently selected in the UI.1957*1958* @return the value of the <code>directory</code> property1959* @see javax.swing.plaf.basic.BasicFileChooserUI#setDirectory1960*/1961protected File getDirectory() {1962return fileChooserUIAccessor.getDirectory();1963}19641965private Component findChildComponent(Container container, Class cls) {1966int n = container.getComponentCount();1967for (int i = 0; i < n; i++) {1968Component comp = container.getComponent(i);1969if (cls.isInstance(comp)) {1970return comp;1971} else if (comp instanceof Container) {1972Component c = findChildComponent((Container)comp, cls);1973if (c != null) {1974return c;1975}1976}1977}1978return null;1979}19801981public boolean canWrite(File f) {1982// Return false for non FileSystem files or if file doesn't exist.1983if (!f.exists()) {1984return false;1985}19861987try {1988if (f instanceof ShellFolder) {1989return f.canWrite();1990} else {1991if (usesShellFolder(getFileChooser())) {1992try {1993return ShellFolder.getShellFolder(f).canWrite();1994} catch (FileNotFoundException ex) {1995// File doesn't exist1996return false;1997}1998} else {1999// Ordinary file2000return f.canWrite();2001}2002}2003} catch (SecurityException e) {2004return false;2005}2006}20072008/**2009* Returns true if specified FileChooser should use ShellFolder2010*/2011public static boolean usesShellFolder(JFileChooser chooser) {2012Boolean prop = (Boolean) chooser.getClientProperty("FileChooser.useShellFolder");20132014return prop == null ? chooser.getFileSystemView().equals(FileSystemView.getFileSystemView())2015: prop.booleanValue();2016}20172018// This interface is used to access methods in the FileChooserUI2019// that are not public.2020public interface FileChooserUIAccessor {2021public JFileChooser getFileChooser();2022public BasicDirectoryModel getModel();2023public JPanel createList();2024public JPanel createDetailsView();2025public boolean isDirectorySelected();2026public File getDirectory();2027public Action getApproveSelectionAction();2028public Action getChangeToParentDirectoryAction();2029public Action getNewFolderAction();2030public MouseListener createDoubleClickListener(JList list);2031public ListSelectionListener createListSelectionListener();2032}2033}203420352036