Path: blob/master/src/java.desktop/share/classes/sun/swing/FilePane.java
41153 views
/*1* Copyright (c) 2003, 2020, 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*/2425package sun.swing;2627import java.awt.BorderLayout;28import java.awt.Color;29import java.awt.Component;30import java.awt.ComponentOrientation;31import java.awt.Container;32import java.awt.Cursor;33import java.awt.DefaultKeyboardFocusManager;34import java.awt.Dimension;35import java.awt.Font;36import java.awt.Insets;37import java.awt.KeyboardFocusManager;38import java.awt.Point;39import java.awt.Rectangle;40import java.awt.event.ActionEvent;41import java.awt.event.ActionListener;42import java.awt.event.ComponentAdapter;43import java.awt.event.ComponentEvent;44import java.awt.event.FocusAdapter;45import java.awt.event.FocusEvent;46import java.awt.event.FocusListener;47import java.awt.event.KeyAdapter;48import java.awt.event.KeyEvent;49import java.awt.event.KeyListener;50import java.awt.event.MouseAdapter;51import java.awt.event.MouseEvent;52import java.awt.event.MouseListener;53import java.beans.PropertyChangeEvent;54import java.beans.PropertyChangeListener;55import java.io.File;56import java.io.FileNotFoundException;57import java.text.DateFormat;58import java.text.MessageFormat;59import java.util.ArrayList;60import java.util.Arrays;61import java.util.Comparator;62import java.util.Date;63import java.util.List;64import java.util.Locale;65import java.util.concurrent.Callable;6667import javax.accessibility.AccessibleContext;68import javax.swing.AbstractAction;69import javax.swing.AbstractListModel;70import javax.swing.Action;71import javax.swing.ActionMap;72import javax.swing.ButtonGroup;73import javax.swing.DefaultCellEditor;74import javax.swing.DefaultListCellRenderer;75import javax.swing.DefaultListSelectionModel;76import javax.swing.Icon;77import javax.swing.InputMap;78import javax.swing.JComponent;79import javax.swing.JFileChooser;80import javax.swing.JLabel;81import javax.swing.JList;82import javax.swing.JMenu;83import javax.swing.JOptionPane;84import javax.swing.JPanel;85import javax.swing.JPopupMenu;86import javax.swing.JRadioButtonMenuItem;87import javax.swing.JScrollPane;88import javax.swing.JTable;89import javax.swing.JTextField;90import javax.swing.KeyStroke;91import javax.swing.ListModel;92import javax.swing.ListSelectionModel;93import javax.swing.LookAndFeel;94import javax.swing.RowSorter;95import javax.swing.SwingConstants;96import javax.swing.SwingUtilities;97import javax.swing.TransferHandler;98import javax.swing.UIManager;99import javax.swing.border.Border;100import javax.swing.event.ListDataEvent;101import javax.swing.event.ListDataListener;102import javax.swing.event.ListSelectionListener;103import javax.swing.event.RowSorterEvent;104import javax.swing.event.RowSorterListener;105import javax.swing.event.TableModelEvent;106import javax.swing.event.TableModelListener;107import javax.swing.filechooser.FileSystemView;108import javax.swing.plaf.basic.BasicDirectoryModel;109import javax.swing.table.AbstractTableModel;110import javax.swing.table.DefaultTableCellRenderer;111import javax.swing.table.DefaultTableColumnModel;112import javax.swing.table.TableCellEditor;113import javax.swing.table.TableCellRenderer;114import javax.swing.table.TableColumn;115import javax.swing.table.TableColumnModel;116import javax.swing.table.TableModel;117import javax.swing.table.TableRowSorter;118import javax.swing.text.Position;119120import sun.awt.AWTAccessor;121import sun.awt.AWTAccessor.MouseEventAccessor;122import sun.awt.shell.ShellFolder;123import sun.awt.shell.ShellFolderColumnInfo;124125/**126* <b>WARNING:</b> This class is an implementation detail and is only127* public so that it can be used by two packages. You should NOT consider128* this public API.129* <p>130* This component is intended to be used in a subclass of131* javax.swing.plaf.basic.BasicFileChooserUI. It realies heavily on the132* implementation of BasicFileChooserUI, and is intended to be API compatible133* with earlier implementations of MetalFileChooserUI and WindowsFileChooserUI.134*135* @author Leif Samuelsson136*/137@SuppressWarnings("serial") // JDK-implementation class138public class FilePane extends JPanel implements PropertyChangeListener {139// Constants for actions. These are used for the actions' ACTION_COMMAND_KEY140// and as keys in the action maps for FilePane and the corresponding UI classes141142public static final String ACTION_APPROVE_SELECTION = "approveSelection";143public static final String ACTION_CANCEL = "cancelSelection";144public static final String ACTION_EDIT_FILE_NAME = "editFileName";145public static final String ACTION_REFRESH = "refresh";146public static final String ACTION_CHANGE_TO_PARENT_DIRECTORY = "Go Up";147public static final String ACTION_NEW_FOLDER = "New Folder";148public static final String ACTION_VIEW_LIST = "viewTypeList";149public static final String ACTION_VIEW_DETAILS = "viewTypeDetails";150151private Action[] actions;152153// "enums" for setViewType()154public static final int VIEWTYPE_LIST = 0;155public static final int VIEWTYPE_DETAILS = 1;156private static final int VIEWTYPE_COUNT = 2;157158private int viewType = -1;159private JPanel[] viewPanels = new JPanel[VIEWTYPE_COUNT];160private JPanel currentViewPanel;161private String[] viewTypeActionNames;162163private String filesListAccessibleName = null;164private String filesDetailsAccessibleName = null;165166private JPopupMenu contextMenu;167private JMenu viewMenu;168169private String viewMenuLabelText;170private String refreshActionLabelText;171private String newFolderActionLabelText;172173private String kiloByteString;174private String megaByteString;175private String gigaByteString;176177private String renameErrorTitleText;178private String renameErrorText;179private String renameErrorFileExistsText;180181private static final Cursor waitCursor =182Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);183184private final KeyListener detailsKeyListener = new KeyAdapter() {185private final long timeFactor;186187private final StringBuilder typedString = new StringBuilder();188189private long lastTime = 1000L;190191{192Long l = (Long) UIManager.get("Table.timeFactor");193timeFactor = (l != null) ? l : 1000L;194}195196/**197* Moves the keyboard focus to the first element whose prefix matches198* the sequence of alphanumeric keys pressed by the user with delay199* less than value of <code>timeFactor</code>. Subsequent same key200* presses move the keyboard focus to the next object that starts with201* the same letter until another key is pressed, then it is treated202* as the prefix with appropriate number of the same letters followed203* by first typed another letter.204*/205public void keyTyped(KeyEvent e) {206BasicDirectoryModel model = getModel();207int rowCount = model.getSize();208209if (detailsTable == null || rowCount == 0 ||210e.isAltDown() || e.isControlDown() || e.isMetaDown()) {211return;212}213214InputMap inputMap = detailsTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);215KeyStroke key = KeyStroke.getKeyStrokeForEvent(e);216217if (inputMap != null && inputMap.get(key) != null) {218return;219}220221int startIndex = detailsTable.getSelectionModel().getLeadSelectionIndex();222223if (startIndex < 0) {224startIndex = 0;225}226227if (startIndex >= rowCount) {228startIndex = rowCount - 1;229}230231char c = e.getKeyChar();232233long time = e.getWhen();234235if (time - lastTime < timeFactor) {236if (typedString.length() == 1 && typedString.charAt(0) == c) {237// Subsequent same key presses move the keyboard focus to the next238// object that starts with the same letter.239startIndex++;240} else {241typedString.append(c);242}243} else {244startIndex++;245246typedString.setLength(0);247typedString.append(c);248}249250lastTime = time;251252if (startIndex >= rowCount) {253startIndex = 0;254}255256// Find next file257int index = getNextMatch(startIndex, rowCount - 1);258259if (index < 0 && startIndex > 0) { // wrap260index = getNextMatch(0, startIndex - 1);261}262263if (index >= 0) {264detailsTable.getSelectionModel().setSelectionInterval(index, index);265266Rectangle cellRect = detailsTable.getCellRect(index,267detailsTable.convertColumnIndexToView(COLUMN_FILENAME), false);268detailsTable.scrollRectToVisible(cellRect);269}270}271272private int getNextMatch(int startIndex, int finishIndex) {273BasicDirectoryModel model = getModel();274JFileChooser fileChooser = getFileChooser();275DetailsTableRowSorter rowSorter = getRowSorter();276277String prefix = typedString.toString().toLowerCase();278279// Search element280for (int index = startIndex; index <= finishIndex; index++) {281File file = (File) model.getElementAt(rowSorter.convertRowIndexToModel(index));282283String fileName = fileChooser.getName(file).toLowerCase();284285if (fileName.startsWith(prefix)) {286return index;287}288}289290return -1;291}292};293294private FocusListener editorFocusListener = new FocusAdapter() {295public void focusLost(FocusEvent e) {296if (! e.isTemporary()) {297applyEdit();298}299}300};301302private static FocusListener repaintListener = new FocusListener() {303public void focusGained(FocusEvent fe) {304repaintSelection(fe.getSource());305}306307public void focusLost(FocusEvent fe) {308repaintSelection(fe.getSource());309}310311private void repaintSelection(Object source) {312if (source instanceof JList) {313repaintListSelection((JList)source);314} else if (source instanceof JTable) {315repaintTableSelection((JTable)source);316}317}318319private void repaintListSelection(JList<?> list) {320int[] indices = list.getSelectedIndices();321for (int i : indices) {322Rectangle bounds = list.getCellBounds(i, i);323list.repaint(bounds);324}325}326327private void repaintTableSelection(JTable table) {328int minRow = table.getSelectionModel().getMinSelectionIndex();329int maxRow = table.getSelectionModel().getMaxSelectionIndex();330if (minRow == -1 || maxRow == -1) {331return;332}333334int col0 = table.convertColumnIndexToView(COLUMN_FILENAME);335336Rectangle first = table.getCellRect(minRow, col0, false);337Rectangle last = table.getCellRect(maxRow, col0, false);338Rectangle dirty = first.union(last);339table.repaint(dirty);340}341};342343private boolean smallIconsView = false;344private Border listViewBorder;345private Color listViewBackground;346private boolean listViewWindowsStyle;347private boolean readOnly;348private boolean fullRowSelection = false;349350private ListSelectionModel listSelectionModel;351private JList<?> list;352private JTable detailsTable;353354private static final int COLUMN_FILENAME = 0;355356// Provides a way to recognize a newly created folder, so it can357// be selected when it appears in the model.358private File newFolderFile;359360// Used for accessing methods in the corresponding UI class361private FileChooserUIAccessor fileChooserUIAccessor;362private DetailsTableModel detailsTableModel;363private DetailsTableRowSorter rowSorter;364365public FilePane(FileChooserUIAccessor fileChooserUIAccessor) {366super(new BorderLayout());367368this.fileChooserUIAccessor = fileChooserUIAccessor;369370installDefaults();371createActionMap();372}373374public void uninstallUI() {375if (getModel() != null) {376getModel().removePropertyChangeListener(this);377}378}379380protected JFileChooser getFileChooser() {381return fileChooserUIAccessor.getFileChooser();382}383384protected BasicDirectoryModel getModel() {385return fileChooserUIAccessor.getModel();386}387388public int getViewType() {389return viewType;390}391392public void setViewType(int viewType) {393if (viewType == this.viewType) {394return;395}396397int oldValue = this.viewType;398this.viewType = viewType;399400JPanel createdViewPanel = null;401Component newFocusOwner = null;402403switch (viewType) {404case VIEWTYPE_LIST:405if (viewPanels[viewType] == null) {406createdViewPanel = fileChooserUIAccessor.createList();407if (createdViewPanel == null) {408createdViewPanel = createList();409}410411list = findChildComponent(createdViewPanel, JList.class);412if (listSelectionModel == null) {413listSelectionModel = list.getSelectionModel();414if (detailsTable != null) {415detailsTable.setSelectionModel(listSelectionModel);416}417} else {418list.setSelectionModel(listSelectionModel);419}420}421list.setLayoutOrientation(JList.VERTICAL_WRAP);422newFocusOwner = list;423break;424425case VIEWTYPE_DETAILS:426if (viewPanels[viewType] == null) {427createdViewPanel = fileChooserUIAccessor.createDetailsView();428if (createdViewPanel == null) {429createdViewPanel = createDetailsView();430}431432detailsTable = findChildComponent(createdViewPanel, JTable.class);433detailsTable.setRowHeight(Math.max(detailsTable.getFont().getSize() + 4, 16 + 1));434if (listSelectionModel != null) {435detailsTable.setSelectionModel(listSelectionModel);436}437}438newFocusOwner = detailsTable;439break;440}441442if (createdViewPanel != null) {443viewPanels[viewType] = createdViewPanel;444recursivelySetInheritsPopupMenu(createdViewPanel, true);445}446447boolean isFocusOwner = false;448449if (currentViewPanel != null) {450Component owner = DefaultKeyboardFocusManager.451getCurrentKeyboardFocusManager().getPermanentFocusOwner();452453isFocusOwner = owner == detailsTable || owner == list;454455remove(currentViewPanel);456}457458currentViewPanel = viewPanels[viewType];459add(currentViewPanel, BorderLayout.CENTER);460461if (isFocusOwner && newFocusOwner != null) {462newFocusOwner.requestFocusInWindow();463}464465revalidate();466repaint();467updateViewMenu();468firePropertyChange("viewType", oldValue, viewType);469}470471@SuppressWarnings("serial") // JDK-implementation class472class ViewTypeAction extends AbstractAction {473private int viewType;474475ViewTypeAction(int viewType) {476super(viewTypeActionNames[viewType]);477this.viewType = viewType;478479String cmd;480switch (viewType) {481case VIEWTYPE_LIST: cmd = ACTION_VIEW_LIST; break;482case VIEWTYPE_DETAILS: cmd = ACTION_VIEW_DETAILS; break;483default: cmd = (String)getValue(Action.NAME);484}485putValue(Action.ACTION_COMMAND_KEY, cmd);486}487488public void actionPerformed(ActionEvent e) {489setViewType(viewType);490}491}492493public Action getViewTypeAction(int viewType) {494return new ViewTypeAction(viewType);495}496497private static void recursivelySetInheritsPopupMenu(Container container, boolean b) {498if (container instanceof JComponent) {499((JComponent)container).setInheritsPopupMenu(b);500}501int n = container.getComponentCount();502for (int i = 0; i < n; i++) {503recursivelySetInheritsPopupMenu((Container)container.getComponent(i), b);504}505}506507protected void installDefaults() {508Locale l = getFileChooser().getLocale();509510listViewBorder = UIManager.getBorder("FileChooser.listViewBorder");511listViewBackground = UIManager.getColor("FileChooser.listViewBackground");512listViewWindowsStyle = UIManager.getBoolean("FileChooser.listViewWindowsStyle");513readOnly = UIManager.getBoolean("FileChooser.readOnly");514515// TODO: On windows, get the following localized strings from the OS516517viewMenuLabelText =518UIManager.getString("FileChooser.viewMenuLabelText", l);519refreshActionLabelText =520UIManager.getString("FileChooser.refreshActionLabelText", l);521newFolderActionLabelText =522UIManager.getString("FileChooser.newFolderActionLabelText", l);523524viewTypeActionNames = new String[VIEWTYPE_COUNT];525viewTypeActionNames[VIEWTYPE_LIST] =526UIManager.getString("FileChooser.listViewActionLabelText", l);527viewTypeActionNames[VIEWTYPE_DETAILS] =528UIManager.getString("FileChooser.detailsViewActionLabelText", l);529530kiloByteString = UIManager.getString("FileChooser.fileSizeKiloBytes", l);531megaByteString = UIManager.getString("FileChooser.fileSizeMegaBytes", l);532gigaByteString = UIManager.getString("FileChooser.fileSizeGigaBytes", l);533fullRowSelection = UIManager.getBoolean("FileView.fullRowSelection");534535filesListAccessibleName = UIManager.getString("FileChooser.filesListAccessibleName", l);536filesDetailsAccessibleName = UIManager.getString("FileChooser.filesDetailsAccessibleName", l);537538renameErrorTitleText = UIManager.getString("FileChooser.renameErrorTitleText", l);539renameErrorText = UIManager.getString("FileChooser.renameErrorText", l);540renameErrorFileExistsText = UIManager.getString("FileChooser.renameErrorFileExistsText", l);541}542543/**544* Fetches the command list for the FilePane. These commands545* are useful for binding to events, such as in a keymap.546*547* @return the command list548*/549public Action[] getActions() {550if (actions == null) {551@SuppressWarnings("serial") // JDK-implementation class552class FilePaneAction extends AbstractAction {553FilePaneAction(String name) {554this(name, name);555}556557FilePaneAction(String name, String cmd) {558super(name);559putValue(Action.ACTION_COMMAND_KEY, cmd);560}561562public void actionPerformed(ActionEvent e) {563String cmd = (String)getValue(Action.ACTION_COMMAND_KEY);564565if (cmd == ACTION_CANCEL) {566if (editFile != null) {567cancelEdit();568} else {569getFileChooser().cancelSelection();570}571} else if (cmd == ACTION_EDIT_FILE_NAME) {572JFileChooser fc = getFileChooser();573int index = listSelectionModel.getMinSelectionIndex();574if (index >= 0 && editFile == null &&575(!fc.isMultiSelectionEnabled() ||576fc.getSelectedFiles().length <= 1)) {577578editFileName(index);579}580} else if (cmd == ACTION_REFRESH) {581getFileChooser().rescanCurrentDirectory();582}583}584585public boolean isEnabled() {586String cmd = (String)getValue(Action.ACTION_COMMAND_KEY);587if (cmd == ACTION_CANCEL) {588return getFileChooser().isEnabled();589} else if (cmd == ACTION_EDIT_FILE_NAME) {590return !readOnly && getFileChooser().isEnabled();591} else {592return true;593}594}595}596597ArrayList<Action> actionList = new ArrayList<Action>(8);598Action action;599600actionList.add(new FilePaneAction(ACTION_CANCEL));601actionList.add(new FilePaneAction(ACTION_EDIT_FILE_NAME));602actionList.add(new FilePaneAction(refreshActionLabelText, ACTION_REFRESH));603604action = fileChooserUIAccessor.getApproveSelectionAction();605if (action != null) {606actionList.add(action);607}608action = fileChooserUIAccessor.getChangeToParentDirectoryAction();609if (action != null) {610actionList.add(action);611}612action = getNewFolderAction();613if (action != null) {614actionList.add(action);615}616action = getViewTypeAction(VIEWTYPE_LIST);617if (action != null) {618actionList.add(action);619}620action = getViewTypeAction(VIEWTYPE_DETAILS);621if (action != null) {622actionList.add(action);623}624actions = actionList.toArray(new Action[actionList.size()]);625}626627return Arrays.copyOf(actions, actions.length);628}629630protected void createActionMap() {631addActionsToMap(super.getActionMap(), getActions());632}633634635public static void addActionsToMap(ActionMap map, Action[] actions) {636if (map != null && actions != null) {637for (Action a : actions) {638String cmd = (String)a.getValue(Action.ACTION_COMMAND_KEY);639if (cmd == null) {640cmd = (String)a.getValue(Action.NAME);641}642map.put(cmd, a);643}644}645}646647648private void updateListRowCount(JList<?> list) {649if (smallIconsView) {650list.setVisibleRowCount(getModel().getSize() / 3);651} else {652list.setVisibleRowCount(-1);653}654}655656public JPanel createList() {657JPanel p = new JPanel(new BorderLayout());658final JFileChooser fileChooser = getFileChooser();659660@SuppressWarnings("serial") // anonymous class661final JList<Object> list = new JList<Object>() {662public int getNextMatch(String prefix, int startIndex, Position.Bias bias) {663ListModel<?> model = getModel();664int max = model.getSize();665if (prefix == null || startIndex < 0 || startIndex >= max) {666throw new IllegalArgumentException();667}668// start search from the next element before/after the selected element669boolean backwards = (bias == Position.Bias.Backward);670for (int i = startIndex; backwards ? i >= 0 : i < max; i += (backwards ? -1 : 1)) {671String filename = fileChooser.getName((File)model.getElementAt(i));672if (filename.regionMatches(true, 0, prefix, 0, prefix.length())) {673return i;674}675}676return -1;677}678};679list.setCellRenderer(new FileRenderer());680list.setLayoutOrientation(JList.VERTICAL_WRAP);681682// 4835633 : tell BasicListUI that this is a file list683list.putClientProperty("List.isFileList", Boolean.TRUE);684685if (listViewWindowsStyle) {686list.addFocusListener(repaintListener);687}688689updateListRowCount(list);690691getModel().addListDataListener(new ListDataListener() {692public void intervalAdded(ListDataEvent e) {693updateListRowCount(list);694}695public void intervalRemoved(ListDataEvent e) {696updateListRowCount(list);697}698public void contentsChanged(ListDataEvent e) {699if (isShowing()) {700clearSelection();701}702updateListRowCount(list);703}704});705706getModel().addPropertyChangeListener(this);707708if (fileChooser.isMultiSelectionEnabled()) {709list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);710} else {711list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);712}713list.setModel(new SortableListModel());714715list.addListSelectionListener(createListSelectionListener());716list.addMouseListener(getMouseHandler());717718JScrollPane scrollpane = new JScrollPane(list);719if (listViewBackground != null) {720list.setBackground(listViewBackground);721}722if (listViewBorder != null) {723scrollpane.setBorder(listViewBorder);724}725726list.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesListAccessibleName);727728p.add(scrollpane, BorderLayout.CENTER);729return p;730}731732/**733* This model allows for sorting JList734*/735@SuppressWarnings("serial") // JDK-implementation class736private class SortableListModel extends AbstractListModel<Object>737implements TableModelListener, RowSorterListener {738739public SortableListModel() {740getDetailsTableModel().addTableModelListener(this);741getRowSorter().addRowSorterListener(this);742}743744public int getSize() {745return getModel().getSize();746}747748public Object getElementAt(int index) {749// JList doesn't support RowSorter so far, so we put it into the list model750return getModel().getElementAt(getRowSorter().convertRowIndexToModel(index));751}752753public void tableChanged(TableModelEvent e) {754fireContentsChanged(this, 0, getSize());755}756757public void sorterChanged(RowSorterEvent e) {758fireContentsChanged(this, 0, getSize());759}760}761762private DetailsTableModel getDetailsTableModel() {763if(detailsTableModel == null) {764detailsTableModel = new DetailsTableModel(getFileChooser());765}766return detailsTableModel;767}768769@SuppressWarnings("serial") // JDK-implementation class770class DetailsTableModel extends AbstractTableModel implements ListDataListener {771private final JFileChooser chooser;772private final BasicDirectoryModel directoryModel;773774ShellFolderColumnInfo[] columns;775int[] columnMap;776777DetailsTableModel(JFileChooser fc) {778this.chooser = fc;779directoryModel = getModel();780directoryModel.addListDataListener(this);781782updateColumnInfo();783}784785void updateColumnInfo() {786File dir = chooser.getCurrentDirectory();787if (dir != null && usesShellFolder(chooser)) {788try {789dir = ShellFolder.getShellFolder(dir);790} catch (FileNotFoundException e) {791// Leave dir without changing792}793}794795ShellFolderColumnInfo[] allColumns = ShellFolder.getFolderColumns(dir);796797ArrayList<ShellFolderColumnInfo> visibleColumns =798new ArrayList<ShellFolderColumnInfo>();799columnMap = new int[allColumns.length];800for (int i = 0; i < allColumns.length; i++) {801ShellFolderColumnInfo column = allColumns[i];802if (column.isVisible()) {803columnMap[visibleColumns.size()] = i;804visibleColumns.add(column);805}806}807808columns = new ShellFolderColumnInfo[visibleColumns.size()];809visibleColumns.toArray(columns);810columnMap = Arrays.copyOf(columnMap, columns.length);811812List<? extends RowSorter.SortKey> sortKeys =813(rowSorter == null) ? null : rowSorter.getSortKeys();814fireTableStructureChanged();815restoreSortKeys(sortKeys);816}817818private void restoreSortKeys(List<? extends RowSorter.SortKey> sortKeys) {819if (sortKeys != null) {820// check if preserved sortKeys are valid for this folder821for (int i = 0; i < sortKeys.size(); i++) {822RowSorter.SortKey sortKey = sortKeys.get(i);823if (sortKey.getColumn() >= columns.length) {824sortKeys = null;825break;826}827}828if (sortKeys != null) {829rowSorter.setSortKeys(sortKeys);830}831}832}833834public int getRowCount() {835return directoryModel.getSize();836}837838public int getColumnCount() {839return columns.length;840}841842public Object getValueAt(int row, int col) {843// Note: It is very important to avoid getting info on drives, as844// this will trigger "No disk in A:" and similar dialogs.845//846// Use (f.exists() && !chooser.getFileSystemView().isFileSystemRoot(f)) to847// determine if it is safe to call methods directly on f.848return getFileColumnValue((File)directoryModel.getElementAt(row), col);849}850851private Object getFileColumnValue(File f, int col) {852return (col == COLUMN_FILENAME)853? f // always return the file itself for the 1st column854: ShellFolder.getFolderColumnValue(f, columnMap[col]);855}856857public void setValueAt(Object value, int row, int col) {858if (col == COLUMN_FILENAME) {859final JFileChooser chooser = getFileChooser();860File f = (File)getValueAt(row, col);861if (f != null) {862String oldDisplayName = chooser.getName(f);863String oldFileName = f.getName();864String newDisplayName = ((String)value).trim();865String newFileName;866867if (!newDisplayName.equals(oldDisplayName)) {868newFileName = newDisplayName;869//Check if extension is hidden from user870int i1 = oldFileName.length();871int i2 = oldDisplayName.length();872if (i1 > i2 && oldFileName.charAt(i2) == '.') {873newFileName = newDisplayName + oldFileName.substring(i2);874}875876// rename877FileSystemView fsv = chooser.getFileSystemView();878final File f2 = fsv.createFileObject(f.getParentFile(), newFileName);879if (f2.exists()) {880JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorFileExistsText,881oldFileName), renameErrorTitleText, JOptionPane.ERROR_MESSAGE);882} else {883if (directoryModel.renameFile(f, f2)) {884if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {885// The setSelectedFile method produces a new setValueAt invocation while the JTable886// is editing. Postpone file selection to be sure that edit mode of the JTable887// is completed888SwingUtilities.invokeLater(new Runnable() {889public void run() {890if (chooser.isMultiSelectionEnabled()) {891chooser.setSelectedFiles(new File[]{f2});892} else {893chooser.setSelectedFile(f2);894}895}896});897} else {898// Could be because of delay in updating Desktop folder899// chooser.setSelectedFile(null);900}901} else {902JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorText, oldFileName),903renameErrorTitleText, JOptionPane.ERROR_MESSAGE);904}905}906}907}908}909}910911public boolean isCellEditable(int row, int column) {912File currentDirectory = getFileChooser().getCurrentDirectory();913return (!readOnly && column == COLUMN_FILENAME && canWrite(currentDirectory));914}915916public void contentsChanged(ListDataEvent e) {917// Update the selection after the model has been updated918new DelayedSelectionUpdater();919fireTableDataChanged();920}921922public void intervalAdded(ListDataEvent e) {923int i0 = e.getIndex0();924int i1 = e.getIndex1();925if (i0 == i1) {926File file = (File)directoryModel.getElementAt(i0);927if (file.equals(newFolderFile)) {928new DelayedSelectionUpdater(file);929newFolderFile = null;930}931}932933fireTableRowsInserted(e.getIndex0(), e.getIndex1());934}935public void intervalRemoved(ListDataEvent e) {936fireTableRowsDeleted(e.getIndex0(), e.getIndex1());937}938939public ShellFolderColumnInfo[] getColumns() {940return columns;941}942}943944945private void updateDetailsColumnModel(JTable table) {946if (table != null) {947ShellFolderColumnInfo[] columns = detailsTableModel.getColumns();948949TableColumnModel columnModel = new DefaultTableColumnModel();950for (int i = 0; i < columns.length; i++) {951ShellFolderColumnInfo dataItem = columns[i];952TableColumn column = new TableColumn(i);953954String title = dataItem.getTitle();955if (title != null && title.startsWith("FileChooser.") && title.endsWith("HeaderText")) {956// the column must have a string resource that we try to get957String uiTitle = UIManager.getString(title, table.getLocale());958if (uiTitle != null) {959title = uiTitle;960}961}962column.setHeaderValue(title);963964Integer width = dataItem.getWidth();965if (width != null) {966column.setPreferredWidth(width);967// otherwise we let JTable to decide the actual width968}969970columnModel.addColumn(column);971}972973// Install cell editor for editing file name974if (!readOnly && columnModel.getColumnCount() > COLUMN_FILENAME) {975columnModel.getColumn(COLUMN_FILENAME).976setCellEditor(getDetailsTableCellEditor());977}978979table.setColumnModel(columnModel);980}981}982983private DetailsTableRowSorter getRowSorter() {984if (rowSorter == null) {985rowSorter = new DetailsTableRowSorter();986}987return rowSorter;988}989990private class DetailsTableRowSorter extends TableRowSorter<TableModel> {991public DetailsTableRowSorter() {992SorterModelWrapper modelWrapper = new SorterModelWrapper();993setModelWrapper(modelWrapper);994modelWrapper.getModel().addTableModelListener(995new TableModelListener() {996@Override997public void tableChanged(TableModelEvent e) {998modelStructureChanged();999}1000});1001}10021003public void updateComparators(ShellFolderColumnInfo [] columns) {1004for (int i = 0; i < columns.length; i++) {1005Comparator<?> c = columns[i].getComparator();1006if (c != null) {1007c = new DirectoriesFirstComparatorWrapper(i, c);1008}1009setComparator(i, c);1010}1011}10121013@Override1014public void sort() {1015ShellFolder.invoke(new Callable<Void>() {1016public Void call() {1017DetailsTableRowSorter.super.sort();1018return null;1019}1020});1021}10221023public void modelStructureChanged() {1024super.modelStructureChanged();1025updateComparators(detailsTableModel.getColumns());1026}10271028private class SorterModelWrapper extends ModelWrapper<TableModel, Integer> {1029public TableModel getModel() {1030return getDetailsTableModel();1031}10321033public int getColumnCount() {1034return getDetailsTableModel().getColumnCount();1035}10361037public int getRowCount() {1038return getDetailsTableModel().getRowCount();1039}10401041public Object getValueAt(int row, int column) {1042return FilePane.this.getModel().getElementAt(row);1043}10441045public Integer getIdentifier(int row) {1046return row;1047}1048}1049}10501051/**1052* This class sorts directories before files, comparing directory to1053* directory and file to file using the wrapped comparator.1054*/1055private class DirectoriesFirstComparatorWrapper implements Comparator<File> {1056private Comparator<Object> comparator;1057private int column;10581059@SuppressWarnings("unchecked")1060public DirectoriesFirstComparatorWrapper(int column, Comparator<?> comparator) {1061this.column = column;1062this.comparator = (Comparator<Object>)comparator;1063}10641065public int compare(File f1, File f2) {1066if (f1 != null && f2 != null) {1067boolean traversable1 = getFileChooser().isTraversable(f1);1068boolean traversable2 = getFileChooser().isTraversable(f2);1069// directories go first1070if (traversable1 && !traversable2) {1071return -1;1072}1073if (!traversable1 && traversable2) {1074return 1;1075}1076}1077if (detailsTableModel.getColumns()[column].isCompareByColumn()) {1078return comparator.compare(1079getDetailsTableModel().getFileColumnValue(f1, column),1080getDetailsTableModel().getFileColumnValue(f2, column)1081);1082}1083// For this column we need to pass the file itself (not a1084// column value) to the comparator1085return comparator.compare(f1, f2);1086}1087}10881089private DetailsTableCellEditor tableCellEditor;10901091private DetailsTableCellEditor getDetailsTableCellEditor() {1092if (tableCellEditor == null) {1093tableCellEditor = new DetailsTableCellEditor(new JTextField());1094}1095return tableCellEditor;1096}10971098@SuppressWarnings("serial") // JDK-implementation class1099private class DetailsTableCellEditor extends DefaultCellEditor {1100private final JTextField tf;11011102public DetailsTableCellEditor(JTextField tf) {1103super(tf);1104this.tf = tf;1105tf.setName("Table.editor");1106tf.addFocusListener(editorFocusListener);1107}11081109public Component getTableCellEditorComponent(JTable table, Object value,1110boolean isSelected, int row, int column) {1111Component comp = super.getTableCellEditorComponent(table, value,1112isSelected, row, column);1113if (value instanceof File) {1114tf.setText(getFileChooser().getName((File) value));1115tf.selectAll();1116}1117return comp;1118}1119}11201121@SuppressWarnings("serial") // JDK-implementation class1122class DetailsTableCellRenderer extends DefaultTableCellRenderer {1123JFileChooser chooser;1124DateFormat df;11251126DetailsTableCellRenderer(JFileChooser chooser) {1127this.chooser = chooser;1128df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT,1129chooser.getLocale());1130}11311132public void setBounds(int x, int y, int width, int height) {1133if (getHorizontalAlignment() == SwingConstants.LEADING &&1134!fullRowSelection) {1135// Restrict width to actual text1136width = Math.min(width, this.getPreferredSize().width+4);1137} else {1138x -= 4;1139}1140super.setBounds(x, y, width, height);1141}114211431144public Insets getInsets(Insets i) {1145// Provide some space between columns1146i = super.getInsets(i);1147i.left += 4;1148i.right += 4;1149return i;1150}11511152public Component getTableCellRendererComponent(JTable table, Object value,1153boolean isSelected, boolean hasFocus, int row, int column) {11541155if ((table.convertColumnIndexToModel(column) != COLUMN_FILENAME ||1156(listViewWindowsStyle && !table.isFocusOwner())) &&1157!fullRowSelection) {1158isSelected = false;1159}11601161super.getTableCellRendererComponent(table, value, isSelected,1162hasFocus, row, column);11631164setIcon(null);11651166int modelColumn = table.convertColumnIndexToModel(column);1167ShellFolderColumnInfo columnInfo = detailsTableModel.getColumns()[modelColumn];11681169Integer alignment = columnInfo.getAlignment();1170if (alignment == null) {1171alignment = (value instanceof Number)1172? SwingConstants.RIGHT1173: SwingConstants.LEADING;1174}11751176setHorizontalAlignment(alignment);11771178// formatting cell text1179// TODO: it's rather a temporary trick, to be revised1180String text;11811182if (value == null) {1183text = "";11841185} else if (value instanceof File) {1186File file = (File)value;1187text = chooser.getName(file);1188Icon icon = chooser.getIcon(file);1189setIcon(icon);11901191} else if (value instanceof Long) {1192long len = ((Long) value) / 1024L;1193if (listViewWindowsStyle) {1194text = MessageFormat.format(kiloByteString, len + 1);1195} else if (len < 1024L) {1196text = MessageFormat.format(kiloByteString, (len == 0L) ? 1L : len);1197} else {1198len /= 1024L;1199if (len < 1024L) {1200text = MessageFormat.format(megaByteString, len);1201} else {1202len /= 1024L;1203text = MessageFormat.format(gigaByteString, len);1204}1205}12061207} else if (value instanceof Date) {1208text = df.format((Date)value);12091210} else {1211text = value.toString();1212}12131214setText(text);12151216return this;1217}1218}12191220public JPanel createDetailsView() {1221final JFileChooser chooser = getFileChooser();12221223JPanel p = new JPanel(new BorderLayout());12241225@SuppressWarnings("serial") // anonymous class1226final JTable detailsTable = new JTable(getDetailsTableModel()) {1227// Handle Escape key events here1228protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {1229if (e.getKeyCode() == KeyEvent.VK_ESCAPE && getCellEditor() == null) {1230// We are not editing, forward to filechooser.1231chooser.dispatchEvent(e);1232return true;1233}1234return super.processKeyBinding(ks, e, condition, pressed);1235}12361237public void tableChanged(TableModelEvent e) {1238super.tableChanged(e);12391240if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {1241// update header with possibly changed column set1242updateDetailsColumnModel(this);1243}1244}1245};12461247detailsTable.setRowSorter(getRowSorter());1248detailsTable.setAutoCreateColumnsFromModel(false);1249detailsTable.setComponentOrientation(chooser.getComponentOrientation());1250detailsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);1251detailsTable.setShowGrid(false);1252detailsTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);1253detailsTable.addKeyListener(detailsKeyListener);12541255Font font = list.getFont();1256detailsTable.setFont(font);1257detailsTable.setIntercellSpacing(new Dimension(0, 0));12581259TableCellRenderer headerRenderer =1260new AlignableTableHeaderRenderer(detailsTable.getTableHeader().getDefaultRenderer());1261detailsTable.getTableHeader().setDefaultRenderer(headerRenderer);1262TableCellRenderer cellRenderer = new DetailsTableCellRenderer(chooser);1263detailsTable.setDefaultRenderer(Object.class, cellRenderer);12641265// So that drag can be started on a mouse press1266detailsTable.getColumnModel().getSelectionModel().1267setSelectionMode(ListSelectionModel.SINGLE_SELECTION);12681269detailsTable.addMouseListener(getMouseHandler());1270// No need to addListSelectionListener because selections are forwarded1271// to our JList.12721273// 4835633 : tell BasicTableUI that this is a file list1274detailsTable.putClientProperty("Table.isFileList", Boolean.TRUE);12751276if (listViewWindowsStyle) {1277detailsTable.addFocusListener(repaintListener);1278}12791280// TAB/SHIFT-TAB should transfer focus and ENTER should select an item.1281// We don't want them to navigate within the table1282ActionMap am = SwingUtilities.getUIActionMap(detailsTable);1283am.remove("selectNextRowCell");1284am.remove("selectPreviousRowCell");1285am.remove("selectNextColumnCell");1286am.remove("selectPreviousColumnCell");1287detailsTable.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,1288null);1289detailsTable.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,1290null);12911292JScrollPane scrollpane = new JScrollPane(detailsTable);1293scrollpane.setComponentOrientation(chooser.getComponentOrientation());1294LookAndFeel.installColors(scrollpane.getViewport(), "Table.background", "Table.foreground");12951296// Adjust width of first column so the table fills the viewport when1297// first displayed (temporary listener).1298scrollpane.addComponentListener(new ComponentAdapter() {1299public void componentResized(ComponentEvent e) {1300JScrollPane sp = (JScrollPane)e.getComponent();1301fixNameColumnWidth(sp.getViewport().getSize().width);1302sp.removeComponentListener(this);1303}1304});13051306// 4835633.1307// If the mouse is pressed in the area below the Details view table, the1308// event is not dispatched to the Table MouseListener but to the1309// scrollpane. Listen for that here so we can clear the selection.1310scrollpane.addMouseListener(new MouseAdapter() {1311public void mousePressed(MouseEvent e) {1312JScrollPane jsp = ((JScrollPane)e.getComponent());1313JTable table = (JTable)jsp.getViewport().getView();13141315if (!e.isShiftDown() || table.getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) {1316clearSelection();1317TableCellEditor tce = table.getCellEditor();1318if (tce != null) {1319tce.stopCellEditing();1320}1321}1322}1323});13241325detailsTable.setForeground(list.getForeground());1326detailsTable.setBackground(list.getBackground());13271328if (listViewBorder != null) {1329scrollpane.setBorder(listViewBorder);1330}1331p.add(scrollpane, BorderLayout.CENTER);13321333detailsTableModel.fireTableStructureChanged();13341335detailsTable.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesDetailsAccessibleName);13361337return p;1338} // createDetailsView13391340private class AlignableTableHeaderRenderer implements TableCellRenderer {1341TableCellRenderer wrappedRenderer;13421343public AlignableTableHeaderRenderer(TableCellRenderer wrappedRenderer) {1344this.wrappedRenderer = wrappedRenderer;1345}13461347public Component getTableCellRendererComponent(1348JTable table, Object value, boolean isSelected,1349boolean hasFocus, int row, int column) {13501351Component c = wrappedRenderer.getTableCellRendererComponent(1352table, value, isSelected, hasFocus, row, column);13531354int modelColumn = table.convertColumnIndexToModel(column);1355ShellFolderColumnInfo columnInfo = detailsTableModel.getColumns()[modelColumn];13561357Integer alignment = columnInfo.getAlignment();1358if (alignment == null) {1359alignment = SwingConstants.CENTER;1360}1361if (c instanceof JLabel) {1362((JLabel) c).setHorizontalAlignment(alignment);1363}13641365return c;1366}1367}13681369private void fixNameColumnWidth(int viewWidth) {1370TableColumn nameCol = detailsTable.getColumnModel().getColumn(COLUMN_FILENAME);1371int tableWidth = detailsTable.getPreferredSize().width;13721373if (tableWidth < viewWidth) {1374nameCol.setPreferredWidth(nameCol.getPreferredWidth() + viewWidth - tableWidth);1375}1376}13771378private class DelayedSelectionUpdater implements Runnable {1379File editFile;13801381DelayedSelectionUpdater() {1382this(null);1383}13841385DelayedSelectionUpdater(File editFile) {1386this.editFile = editFile;1387if (isShowing()) {1388SwingUtilities.invokeLater(this);1389}1390}13911392public void run() {1393setFileSelected();1394if (editFile != null) {1395editFileName(getRowSorter().convertRowIndexToView(1396getModel().indexOf(editFile)));1397editFile = null;1398}1399}1400}140114021403/**1404* Creates a selection listener for the list of files and directories.1405*1406* @return a <code>ListSelectionListener</code>1407*/1408public ListSelectionListener createListSelectionListener() {1409return fileChooserUIAccessor.createListSelectionListener();1410}14111412int lastIndex = -1;1413File editFile = null;14141415private int getEditIndex() {1416return lastIndex;1417}14181419private void setEditIndex(int i) {1420lastIndex = i;1421}14221423private void resetEditIndex() {1424lastIndex = -1;1425}14261427private void cancelEdit() {1428if (editFile != null) {1429editFile = null;1430list.remove(editCell);1431repaint();1432} else if (detailsTable != null && detailsTable.isEditing()) {1433detailsTable.getCellEditor().cancelCellEditing();1434}1435}14361437JTextField editCell = null;14381439/**1440* @param index visual index of the file to be edited1441*/1442@SuppressWarnings("deprecation")1443private void editFileName(int index) {1444JFileChooser chooser = getFileChooser();1445File currentDirectory = chooser.getCurrentDirectory();14461447if (readOnly || !canWrite(currentDirectory)) {1448return;1449}14501451ensureIndexIsVisible(index);1452switch (viewType) {1453case VIEWTYPE_LIST:1454editFile = (File)getModel().getElementAt(getRowSorter().convertRowIndexToModel(index));1455Rectangle r = list.getCellBounds(index, index);1456if (editCell == null) {1457editCell = new JTextField();1458editCell.setName("Tree.cellEditor");1459editCell.addActionListener(new EditActionListener());1460editCell.addFocusListener(editorFocusListener);1461editCell.setNextFocusableComponent(list);1462}1463list.add(editCell);1464editCell.setText(chooser.getName(editFile));1465ComponentOrientation orientation = list.getComponentOrientation();1466editCell.setComponentOrientation(orientation);14671468Icon icon = chooser.getIcon(editFile);14691470// PENDING - grab padding (4) below from defaults table.1471int editX = icon == null ? 20 : icon.getIconWidth() + 4;14721473if (orientation.isLeftToRight()) {1474editCell.setBounds(editX + r.x, r.y, r.width - editX, r.height);1475} else {1476editCell.setBounds(r.x, r.y, r.width - editX, r.height);1477}1478editCell.requestFocus();1479editCell.selectAll();1480break;14811482case VIEWTYPE_DETAILS:1483detailsTable.editCellAt(index, COLUMN_FILENAME);1484break;1485}1486}148714881489class EditActionListener implements ActionListener {1490public void actionPerformed(ActionEvent e) {1491applyEdit();1492}1493}14941495private void applyEdit() {1496if (editFile != null && editFile.exists()) {1497JFileChooser chooser = getFileChooser();1498String oldDisplayName = chooser.getName(editFile);1499String oldFileName = editFile.getName();1500String newDisplayName = editCell.getText().trim();1501String newFileName;15021503if (!newDisplayName.equals(oldDisplayName)) {1504newFileName = newDisplayName;1505//Check if extension is hidden from user1506int i1 = oldFileName.length();1507int i2 = oldDisplayName.length();1508if (i1 > i2 && oldFileName.charAt(i2) == '.') {1509newFileName = newDisplayName + oldFileName.substring(i2);1510}15111512// rename1513FileSystemView fsv = chooser.getFileSystemView();1514File f2 = fsv.createFileObject(editFile.getParentFile(), newFileName);1515if (f2.exists()) {1516JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorFileExistsText, oldFileName),1517renameErrorTitleText, JOptionPane.ERROR_MESSAGE);1518} else {1519if (getModel().renameFile(editFile, f2)) {1520if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {1521if (chooser.isMultiSelectionEnabled()) {1522chooser.setSelectedFiles(new File[]{f2});1523} else {1524chooser.setSelectedFile(f2);1525}1526} else {1527//Could be because of delay in updating Desktop folder1528//chooser.setSelectedFile(null);1529}1530} else {1531JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorText, oldFileName),1532renameErrorTitleText, JOptionPane.ERROR_MESSAGE);1533}1534}1535}1536}1537if (detailsTable != null && detailsTable.isEditing()) {1538detailsTable.getCellEditor().stopCellEditing();1539}1540cancelEdit();1541}15421543protected Action newFolderAction;15441545@SuppressWarnings("serial") // anonymous class inside1546public Action getNewFolderAction() {1547if (!readOnly && newFolderAction == null) {1548newFolderAction = new AbstractAction(newFolderActionLabelText) {1549private Action basicNewFolderAction;15501551// Initializer1552{1553putValue(Action.ACTION_COMMAND_KEY, FilePane.ACTION_NEW_FOLDER);15541555File currentDirectory = getFileChooser().getCurrentDirectory();1556if (currentDirectory != null) {1557setEnabled(canWrite(currentDirectory));1558}1559}15601561public void actionPerformed(ActionEvent ev) {1562if (basicNewFolderAction == null) {1563basicNewFolderAction = fileChooserUIAccessor.getNewFolderAction();1564}1565JFileChooser fc = getFileChooser();1566File oldFile = fc.getSelectedFile();1567basicNewFolderAction.actionPerformed(ev);1568File newFile = fc.getSelectedFile();1569if (newFile != null && !newFile.equals(oldFile) && newFile.isDirectory()) {1570newFolderFile = newFile;1571}1572}1573};1574}1575return newFolderAction;1576}15771578@SuppressWarnings("serial") // JDK-implementation class1579protected class FileRenderer extends DefaultListCellRenderer {15801581public Component getListCellRendererComponent(JList<?> list, Object value,1582int index, boolean isSelected,1583boolean cellHasFocus) {15841585if (listViewWindowsStyle && !list.isFocusOwner()) {1586isSelected = false;1587}15881589super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);1590File file = (File) value;1591String fileName = getFileChooser().getName(file);1592setText(fileName);1593setFont(list.getFont());15941595Icon icon = getFileChooser().getIcon(file);1596if (icon != null) {1597setIcon(icon);1598} else {1599if (getFileChooser().getFileSystemView().isTraversable(file)) {1600setText(fileName+File.separator);1601}1602}16031604return this;1605}1606}160716081609@SuppressWarnings("deprecation")1610void setFileSelected() {1611if (getFileChooser().isMultiSelectionEnabled() && !isDirectorySelected()) {1612File[] files = getFileChooser().getSelectedFiles(); // Should be selected1613Object[] selectedObjects = list.getSelectedValues(); // Are actually selected16141615listSelectionModel.setValueIsAdjusting(true);1616try {1617int lead = listSelectionModel.getLeadSelectionIndex();1618int anchor = listSelectionModel.getAnchorSelectionIndex();16191620Arrays.sort(files);1621Arrays.sort(selectedObjects);16221623int shouldIndex = 0;1624int actuallyIndex = 0;16251626// Remove files that shouldn't be selected and add files which should be selected1627// Note: Assume files are already sorted in compareTo order.1628while (shouldIndex < files.length &&1629actuallyIndex < selectedObjects.length) {1630int comparison = files[shouldIndex].compareTo((File)selectedObjects[actuallyIndex]);1631if (comparison < 0) {1632doSelectFile(files[shouldIndex++]);1633} else if (comparison > 0) {1634doDeselectFile(selectedObjects[actuallyIndex++]);1635} else {1636// Do nothing1637shouldIndex++;1638actuallyIndex++;1639}16401641}16421643while (shouldIndex < files.length) {1644doSelectFile(files[shouldIndex++]);1645}16461647while (actuallyIndex < selectedObjects.length) {1648doDeselectFile(selectedObjects[actuallyIndex++]);1649}16501651// restore the anchor and lead1652if (listSelectionModel instanceof DefaultListSelectionModel) {1653((DefaultListSelectionModel)listSelectionModel).1654moveLeadSelectionIndex(lead);1655listSelectionModel.setAnchorSelectionIndex(anchor);1656}1657} finally {1658listSelectionModel.setValueIsAdjusting(false);1659}1660} else {1661JFileChooser chooser = getFileChooser();1662File f;1663if (isDirectorySelected()) {1664f = getDirectory();1665} else {1666f = chooser.getSelectedFile();1667}1668int i;1669if (f != null && (i = getModel().indexOf(f)) >= 0) {1670int viewIndex = getRowSorter().convertRowIndexToView(i);1671listSelectionModel.setSelectionInterval(viewIndex, viewIndex);1672ensureIndexIsVisible(viewIndex);1673} else {1674clearSelection();1675}1676}1677}16781679private void doSelectFile(File fileToSelect) {1680int index = getModel().indexOf(fileToSelect);1681// could be missed in the current directory if it changed1682if (index >= 0) {1683index = getRowSorter().convertRowIndexToView(index);1684listSelectionModel.addSelectionInterval(index, index);1685}1686}16871688private void doDeselectFile(Object fileToDeselect) {1689int index = getRowSorter().convertRowIndexToView(1690getModel().indexOf(fileToDeselect));1691listSelectionModel.removeSelectionInterval(index, index);1692}16931694/* The following methods are used by the PropertyChange Listener */16951696private void doSelectedFileChanged(PropertyChangeEvent e) {1697applyEdit();1698File f = (File) e.getNewValue();1699JFileChooser fc = getFileChooser();1700if (f != null1701&& ((fc.isFileSelectionEnabled() && !f.isDirectory())1702|| (f.isDirectory() && fc.isDirectorySelectionEnabled()))) {17031704setFileSelected();1705}1706}17071708private void doSelectedFilesChanged(PropertyChangeEvent e) {1709applyEdit();1710File[] files = (File[]) e.getNewValue();1711JFileChooser fc = getFileChooser();1712if (files != null1713&& files.length > 01714&& (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) {1715setFileSelected();1716}1717}17181719private void doDirectoryChanged(PropertyChangeEvent e) {1720getDetailsTableModel().updateColumnInfo();17211722JFileChooser fc = getFileChooser();1723FileSystemView fsv = fc.getFileSystemView();17241725applyEdit();1726resetEditIndex();1727ensureIndexIsVisible(0);1728File currentDirectory = fc.getCurrentDirectory();1729if (currentDirectory != null) {1730if (!readOnly) {1731getNewFolderAction().setEnabled(canWrite(currentDirectory));1732}1733fileChooserUIAccessor.getChangeToParentDirectoryAction().setEnabled(!fsv.isRoot(currentDirectory));1734}1735if (list != null) {1736list.clearSelection();1737}1738}17391740private void doFilterChanged(PropertyChangeEvent e) {1741applyEdit();1742resetEditIndex();1743clearSelection();1744}17451746private void doFileSelectionModeChanged(PropertyChangeEvent e) {1747applyEdit();1748resetEditIndex();1749clearSelection();1750}17511752private void doMultiSelectionChanged(PropertyChangeEvent e) {1753if (getFileChooser().isMultiSelectionEnabled()) {1754listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);1755} else {1756listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);1757clearSelection();1758getFileChooser().setSelectedFiles(null);1759}1760}17611762/*1763* Listen for filechooser property changes, such as1764* the selected file changing, or the type of the dialog changing.1765*/1766public void propertyChange(PropertyChangeEvent e) {1767if (viewType == -1) {1768setViewType(VIEWTYPE_LIST);1769}17701771String s = e.getPropertyName();1772if (s.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {1773doSelectedFileChanged(e);1774} else if (s.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) {1775doSelectedFilesChanged(e);1776} else if (s.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {1777doDirectoryChanged(e);1778} else if (s.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) {1779doFilterChanged(e);1780} else if (s.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {1781doFileSelectionModeChanged(e);1782} else if (s.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {1783doMultiSelectionChanged(e);1784} else if (s.equals(JFileChooser.CANCEL_SELECTION)) {1785applyEdit();1786} else if (s.equals("busy")) {1787setCursor((Boolean)e.getNewValue() ? waitCursor : null);1788} else if (s.equals("componentOrientation")) {1789ComponentOrientation o = (ComponentOrientation)e.getNewValue();1790JFileChooser cc = (JFileChooser)e.getSource();1791if (o != e.getOldValue()) {1792cc.applyComponentOrientation(o);1793}1794if (detailsTable != null) {1795detailsTable.setComponentOrientation(o);1796detailsTable.getParent().getParent().setComponentOrientation(o);1797}1798}1799}18001801private void ensureIndexIsVisible(int i) {1802if (i >= 0) {1803if (list != null) {1804list.ensureIndexIsVisible(i);1805}1806if (detailsTable != null) {1807detailsTable.scrollRectToVisible(detailsTable.getCellRect(i, COLUMN_FILENAME, true));1808}1809}1810}18111812public void ensureFileIsVisible(JFileChooser fc, File f) {1813int modelIndex = getModel().indexOf(f);1814if (modelIndex >= 0) {1815ensureIndexIsVisible(getRowSorter().convertRowIndexToView(modelIndex));1816}1817}18181819public void rescanCurrentDirectory() {1820getModel().validateFileCache();1821}18221823public void clearSelection() {1824if (listSelectionModel != null) {1825listSelectionModel.clearSelection();1826if (listSelectionModel instanceof DefaultListSelectionModel) {1827((DefaultListSelectionModel)listSelectionModel).moveLeadSelectionIndex(-1);1828listSelectionModel.setAnchorSelectionIndex(-1);1829}1830}1831}18321833public JMenu getViewMenu() {1834if (viewMenu == null) {1835viewMenu = new JMenu(viewMenuLabelText);1836ButtonGroup viewButtonGroup = new ButtonGroup();18371838for (int i = 0; i < VIEWTYPE_COUNT; i++) {1839JRadioButtonMenuItem mi =1840new JRadioButtonMenuItem(new ViewTypeAction(i));1841viewButtonGroup.add(mi);1842viewMenu.add(mi);1843}1844updateViewMenu();1845}1846return viewMenu;1847}18481849private void updateViewMenu() {1850if (viewMenu != null) {1851Component[] comps = viewMenu.getMenuComponents();1852for (Component comp : comps) {1853if (comp instanceof JRadioButtonMenuItem) {1854JRadioButtonMenuItem mi = (JRadioButtonMenuItem) comp;1855if (((ViewTypeAction)mi.getAction()).viewType == viewType) {1856mi.setSelected(true);1857}1858}1859}1860}1861}18621863public JPopupMenu getComponentPopupMenu() {1864JPopupMenu popupMenu = getFileChooser().getComponentPopupMenu();1865if (popupMenu != null) {1866return popupMenu;1867}18681869JMenu viewMenu = getViewMenu();1870if (contextMenu == null) {1871contextMenu = new JPopupMenu();1872if (viewMenu != null) {1873contextMenu.add(viewMenu);1874if (listViewWindowsStyle) {1875contextMenu.addSeparator();1876}1877}1878ActionMap actionMap = getActionMap();1879Action refreshAction = actionMap.get(ACTION_REFRESH);1880Action newFolderAction = actionMap.get(ACTION_NEW_FOLDER);1881if (refreshAction != null) {1882contextMenu.add(refreshAction);1883if (listViewWindowsStyle && newFolderAction != null) {1884contextMenu.addSeparator();1885}1886}1887if (newFolderAction != null) {1888contextMenu.add(newFolderAction);1889}1890}1891if (viewMenu != null) {1892viewMenu.getPopupMenu().setInvoker(viewMenu);1893}1894return contextMenu;1895}189618971898private Handler handler;18991900protected Handler getMouseHandler() {1901if (handler == null) {1902handler = new Handler();1903}1904return handler;1905}19061907private class Handler implements MouseListener {1908private MouseListener doubleClickListener;19091910@SuppressWarnings("deprecation")1911public void mouseClicked(MouseEvent evt) {1912JComponent source = (JComponent)evt.getSource();19131914int index;1915if (source instanceof JList) {1916index = SwingUtilities2.loc2IndexFileList(list, evt.getPoint());1917} else if (source instanceof JTable) {1918JTable table = (JTable)source;1919Point p = evt.getPoint();1920index = table.rowAtPoint(p);19211922boolean pointOutsidePrefSize =1923SwingUtilities2.pointOutsidePrefSize(1924table, index, table.columnAtPoint(p), p);19251926if (pointOutsidePrefSize && !fullRowSelection) {1927return;1928}19291930// Translate point from table to list1931if (index >= 0 && list != null &&1932listSelectionModel.isSelectedIndex(index)) {19331934// Make a new event with the list as source, placing the1935// click in the corresponding list cell.1936Rectangle r = list.getCellBounds(index, index);1937MouseEvent newEvent = new MouseEvent(list, evt.getID(),1938evt.getWhen(), evt.getModifiers(),1939r.x + 1, r.y + r.height/2,1940evt.getXOnScreen(),1941evt.getYOnScreen(),1942evt.getClickCount(), evt.isPopupTrigger(),1943evt.getButton());1944MouseEventAccessor meAccessor = AWTAccessor.getMouseEventAccessor();1945meAccessor.setCausedByTouchEvent(newEvent,1946meAccessor.isCausedByTouchEvent(evt));1947evt = newEvent;1948}1949} else {1950return;1951}19521953if (index >= 0 && SwingUtilities.isLeftMouseButton(evt)) {1954JFileChooser fc = getFileChooser();19551956// For single click, we handle editing file name1957if (evt.getClickCount() == 1 && source instanceof JList) {1958if ((!fc.isMultiSelectionEnabled() || fc.getSelectedFiles().length <= 1)1959&& index >= 0 && listSelectionModel.isSelectedIndex(index)1960&& getEditIndex() == index && editFile == null) {19611962editFileName(index);1963} else {1964if (index >= 0) {1965setEditIndex(index);1966} else {1967resetEditIndex();1968}1969}1970} else if (evt.getClickCount() == 2) {1971// on double click (open or drill down one directory) be1972// sure to clear the edit index1973resetEditIndex();1974}1975}19761977// Forward event to Basic1978if (getDoubleClickListener() != null) {1979getDoubleClickListener().mouseClicked(evt);1980}1981}19821983public void mouseEntered(MouseEvent evt) {1984JComponent source = (JComponent)evt.getSource();1985if (source instanceof JTable) {1986JTable table = (JTable)evt.getSource();19871988TransferHandler th1 = getFileChooser().getTransferHandler();1989TransferHandler th2 = table.getTransferHandler();1990if (th1 != th2) {1991table.setTransferHandler(th1);1992}19931994boolean dragEnabled = getFileChooser().getDragEnabled();1995if (dragEnabled != table.getDragEnabled()) {1996table.setDragEnabled(dragEnabled);1997}1998} else if (source instanceof JList) {1999// Forward event to Basic2000if (getDoubleClickListener() != null) {2001getDoubleClickListener().mouseEntered(evt);2002}2003}2004}20052006public void mouseExited(MouseEvent evt) {2007if (evt.getSource() instanceof JList) {2008// Forward event to Basic2009if (getDoubleClickListener() != null) {2010getDoubleClickListener().mouseExited(evt);2011}2012}2013}20142015public void mousePressed(MouseEvent evt) {2016if (evt.getSource() instanceof JList) {2017// Forward event to Basic2018if (getDoubleClickListener() != null) {2019getDoubleClickListener().mousePressed(evt);2020}2021}2022}20232024public void mouseReleased(MouseEvent evt) {2025if (evt.getSource() instanceof JList) {2026// Forward event to Basic2027if (getDoubleClickListener() != null) {2028getDoubleClickListener().mouseReleased(evt);2029}2030}2031}20322033private MouseListener getDoubleClickListener() {2034// Lazy creation of Basic's listener2035if (doubleClickListener == null && list != null) {2036doubleClickListener =2037fileChooserUIAccessor.createDoubleClickListener(list);2038}2039return doubleClickListener;2040}2041}20422043/**2044* Property to remember whether a directory is currently selected in the UI.2045*2046* @return <code>true</code> iff a directory is currently selected.2047*/2048protected boolean isDirectorySelected() {2049return fileChooserUIAccessor.isDirectorySelected();2050}205120522053/**2054* Property to remember the directory that is currently selected in the UI.2055*2056* @return the value of the <code>directory</code> property2057* @see javax.swing.plaf.basic.BasicFileChooserUI#setDirectory2058*/2059protected File getDirectory() {2060return fileChooserUIAccessor.getDirectory();2061}20622063private <T> T findChildComponent(Container container, Class<T> cls) {2064int n = container.getComponentCount();2065for (int i = 0; i < n; i++) {2066Component comp = container.getComponent(i);2067if (cls.isInstance(comp)) {2068return cls.cast(comp);2069} else if (comp instanceof Container) {2070T c = findChildComponent((Container)comp, cls);2071if (c != null) {2072return c;2073}2074}2075}2076return null;2077}20782079public boolean canWrite(File f) {2080// Return false for non FileSystem files or if file doesn't exist.2081if (!f.exists()) {2082return false;2083}20842085try {2086if (f instanceof ShellFolder) {2087return f.canWrite();2088} else {2089if (usesShellFolder(getFileChooser())) {2090try {2091return ShellFolder.getShellFolder(f).canWrite();2092} catch (FileNotFoundException ex) {2093// File doesn't exist2094return false;2095}2096} else {2097// Ordinary file2098return f.canWrite();2099}2100}2101} catch (SecurityException e) {2102return false;2103}2104}21052106/**2107* Returns true if specified FileChooser should use ShellFolder2108*/2109public static boolean usesShellFolder(JFileChooser chooser) {2110Boolean prop = (Boolean) chooser.getClientProperty("FileChooser.useShellFolder");21112112return prop == null ? chooser.getFileSystemView().equals(FileSystemView.getFileSystemView())2113: prop.booleanValue();2114}21152116// This interface is used to access methods in the FileChooserUI2117// that are not public.2118public interface FileChooserUIAccessor {2119public JFileChooser getFileChooser();2120public BasicDirectoryModel getModel();2121public JPanel createList();2122public JPanel createDetailsView();2123public boolean isDirectorySelected();2124public File getDirectory();2125public Action getApproveSelectionAction();2126public Action getChangeToParentDirectoryAction();2127public Action getNewFolderAction();2128public MouseListener createDoubleClickListener(JList<?> list);2129public ListSelectionListener createListSelectionListener();2130}2131}213221332134