/* * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.swing; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.*; import java.text.DateFormat; import java.text.MessageFormat; import java.util.*; import java.util.List; import java.util.concurrent.Callable; import javax.accessibility.AccessibleContext; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.filechooser.*; import javax.swing.plaf.basic.*; import javax.swing.table.*; import javax.swing.text.*; import sun.awt.shell.*; /** * WARNING: This class is an implementation detail and is only * public so that it can be used by two packages. You should NOT consider * this public API. *
* This component is intended to be used in a subclass of
* javax.swing.plaf.basic.BasicFileChooserUI. It realies heavily on the
* implementation of BasicFileChooserUI, and is intended to be API compatible
* with earlier implementations of MetalFileChooserUI and WindowsFileChooserUI.
*
* @author Leif Samuelsson
*/
public class FilePane extends JPanel implements PropertyChangeListener {
// Constants for actions. These are used for the actions' ACTION_COMMAND_KEY
// and as keys in the action maps for FilePane and the corresponding UI classes
public final static String ACTION_APPROVE_SELECTION = "approveSelection";
public final static String ACTION_CANCEL = "cancelSelection";
public final static String ACTION_EDIT_FILE_NAME = "editFileName";
public final static String ACTION_REFRESH = "refresh";
public final static String ACTION_CHANGE_TO_PARENT_DIRECTORY = "Go Up";
public final static String ACTION_NEW_FOLDER = "New Folder";
public final static String ACTION_VIEW_LIST = "viewTypeList";
public final static String ACTION_VIEW_DETAILS = "viewTypeDetails";
private Action[] actions;
// "enums" for setViewType()
public static final int VIEWTYPE_LIST = 0;
public static final int VIEWTYPE_DETAILS = 1;
private static final int VIEWTYPE_COUNT = 2;
private int viewType = -1;
private JPanel[] viewPanels = new JPanel[VIEWTYPE_COUNT];
private JPanel currentViewPanel;
private String[] viewTypeActionNames;
private String filesListAccessibleName = null;
private String filesDetailsAccessibleName = null;
private JPopupMenu contextMenu;
private JMenu viewMenu;
private String viewMenuLabelText;
private String refreshActionLabelText;
private String newFolderActionLabelText;
private String kiloByteString;
private String megaByteString;
private String gigaByteString;
private String renameErrorTitleText;
private String renameErrorText;
private String renameErrorFileExistsText;
private static final Cursor waitCursor =
Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
private final KeyListener detailsKeyListener = new KeyAdapter() {
private final long timeFactor;
private final StringBuilder typedString = new StringBuilder();
private long lastTime = 1000L;
{
Long l = (Long) UIManager.get("Table.timeFactor");
timeFactor = (l != null) ? l : 1000L;
}
/**
* Moves the keyboard focus to the first element whose prefix matches
* the sequence of alphanumeric keys pressed by the user with delay
* less than value of timeFactor
. Subsequent same key
* presses move the keyboard focus to the next object that starts with
* the same letter until another key is pressed, then it is treated
* as the prefix with appropriate number of the same letters followed
* by first typed another letter.
*/
public void keyTyped(KeyEvent e) {
BasicDirectoryModel model = getModel();
int rowCount = model.getSize();
if (detailsTable == null || rowCount == 0 ||
e.isAltDown() || e.isControlDown() || e.isMetaDown()) {
return;
}
InputMap inputMap = detailsTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
KeyStroke key = KeyStroke.getKeyStrokeForEvent(e);
if (inputMap != null && inputMap.get(key) != null) {
return;
}
int startIndex = detailsTable.getSelectionModel().getLeadSelectionIndex();
if (startIndex < 0) {
startIndex = 0;
}
if (startIndex >= rowCount) {
startIndex = rowCount - 1;
}
char c = e.getKeyChar();
long time = e.getWhen();
if (time - lastTime < timeFactor) {
if (typedString.length() == 1 && typedString.charAt(0) == c) {
// Subsequent same key presses move the keyboard focus to the next
// object that starts with the same letter.
startIndex++;
} else {
typedString.append(c);
}
} else {
startIndex++;
typedString.setLength(0);
typedString.append(c);
}
lastTime = time;
if (startIndex >= rowCount) {
startIndex = 0;
}
// Find next file
int index = getNextMatch(startIndex, rowCount - 1);
if (index < 0 && startIndex > 0) { // wrap
index = getNextMatch(0, startIndex - 1);
}
if (index >= 0) {
detailsTable.getSelectionModel().setSelectionInterval(index, index);
Rectangle cellRect = detailsTable.getCellRect(index,
detailsTable.convertColumnIndexToView(COLUMN_FILENAME), false);
detailsTable.scrollRectToVisible(cellRect);
}
}
private int getNextMatch(int startIndex, int finishIndex) {
BasicDirectoryModel model = getModel();
JFileChooser fileChooser = getFileChooser();
DetailsTableRowSorter rowSorter = getRowSorter();
String prefix = typedString.toString().toLowerCase();
// Search element
for (int index = startIndex; index <= finishIndex; index++) {
File file = (File) model.getElementAt(rowSorter.convertRowIndexToModel(index));
String fileName = fileChooser.getName(file).toLowerCase();
if (fileName.startsWith(prefix)) {
return index;
}
}
return -1;
}
};
private FocusListener editorFocusListener = new FocusAdapter() {
public void focusLost(FocusEvent e) {
if (! e.isTemporary()) {
applyEdit();
}
}
};
private static FocusListener repaintListener = new FocusListener() {
public void focusGained(FocusEvent fe) {
repaintSelection(fe.getSource());
}
public void focusLost(FocusEvent fe) {
repaintSelection(fe.getSource());
}
private void repaintSelection(Object source) {
if (source instanceof JList) {
repaintListSelection((JList)source);
} else if (source instanceof JTable) {
repaintTableSelection((JTable)source);
}
}
private void repaintListSelection(JList list) {
int[] indices = list.getSelectedIndices();
for (int i : indices) {
Rectangle bounds = list.getCellBounds(i, i);
list.repaint(bounds);
}
}
private void repaintTableSelection(JTable table) {
int minRow = table.getSelectionModel().getMinSelectionIndex();
int maxRow = table.getSelectionModel().getMaxSelectionIndex();
if (minRow == -1 || maxRow == -1) {
return;
}
int col0 = table.convertColumnIndexToView(COLUMN_FILENAME);
Rectangle first = table.getCellRect(minRow, col0, false);
Rectangle last = table.getCellRect(maxRow, col0, false);
Rectangle dirty = first.union(last);
table.repaint(dirty);
}
};
private boolean smallIconsView = false;
private Border listViewBorder;
private Color listViewBackground;
private boolean listViewWindowsStyle;
private boolean readOnly;
private boolean fullRowSelection = false;
private ListSelectionModel listSelectionModel;
private JList list;
private JTable detailsTable;
private static final int COLUMN_FILENAME = 0;
// Provides a way to recognize a newly created folder, so it can
// be selected when it appears in the model.
private File newFolderFile;
// Used for accessing methods in the corresponding UI class
private FileChooserUIAccessor fileChooserUIAccessor;
private DetailsTableModel detailsTableModel;
private DetailsTableRowSorter rowSorter;
public FilePane(FileChooserUIAccessor fileChooserUIAccessor) {
super(new BorderLayout());
this.fileChooserUIAccessor = fileChooserUIAccessor;
installDefaults();
createActionMap();
}
public void uninstallUI() {
if (getModel() != null) {
getModel().removePropertyChangeListener(this);
}
}
protected JFileChooser getFileChooser() {
return fileChooserUIAccessor.getFileChooser();
}
protected BasicDirectoryModel getModel() {
return fileChooserUIAccessor.getModel();
}
public int getViewType() {
return viewType;
}
public void setViewType(int viewType) {
if (viewType == this.viewType) {
return;
}
int oldValue = this.viewType;
this.viewType = viewType;
JPanel createdViewPanel = null;
Component newFocusOwner = null;
switch (viewType) {
case VIEWTYPE_LIST:
if (viewPanels[viewType] == null) {
createdViewPanel = fileChooserUIAccessor.createList();
if (createdViewPanel == null) {
createdViewPanel = createList();
}
list = (JList) findChildComponent(createdViewPanel, JList.class);
if (listSelectionModel == null) {
listSelectionModel = list.getSelectionModel();
if (detailsTable != null) {
detailsTable.setSelectionModel(listSelectionModel);
}
} else {
list.setSelectionModel(listSelectionModel);
}
}
list.setLayoutOrientation(JList.VERTICAL_WRAP);
newFocusOwner = list;
break;
case VIEWTYPE_DETAILS:
if (viewPanels[viewType] == null) {
createdViewPanel = fileChooserUIAccessor.createDetailsView();
if (createdViewPanel == null) {
createdViewPanel = createDetailsView();
}
detailsTable = (JTable) findChildComponent(createdViewPanel, JTable.class);
detailsTable.setRowHeight(Math.max(detailsTable.getFont().getSize() + 4, 16 + 1));
if (listSelectionModel != null) {
detailsTable.setSelectionModel(listSelectionModel);
}
}
newFocusOwner = detailsTable;
break;
}
if (createdViewPanel != null) {
viewPanels[viewType] = createdViewPanel;
recursivelySetInheritsPopupMenu(createdViewPanel, true);
}
boolean isFocusOwner = false;
if (currentViewPanel != null) {
Component owner = DefaultKeyboardFocusManager.
getCurrentKeyboardFocusManager().getPermanentFocusOwner();
isFocusOwner = owner == detailsTable || owner == list;
remove(currentViewPanel);
}
currentViewPanel = viewPanels[viewType];
add(currentViewPanel, BorderLayout.CENTER);
if (isFocusOwner && newFocusOwner != null) {
newFocusOwner.requestFocusInWindow();
}
revalidate();
repaint();
updateViewMenu();
firePropertyChange("viewType", oldValue, viewType);
}
class ViewTypeAction extends AbstractAction {
private int viewType;
ViewTypeAction(int viewType) {
super(viewTypeActionNames[viewType]);
this.viewType = viewType;
String cmd;
switch (viewType) {
case VIEWTYPE_LIST: cmd = ACTION_VIEW_LIST; break;
case VIEWTYPE_DETAILS: cmd = ACTION_VIEW_DETAILS; break;
default: cmd = (String)getValue(Action.NAME);
}
putValue(Action.ACTION_COMMAND_KEY, cmd);
}
public void actionPerformed(ActionEvent e) {
setViewType(viewType);
}
}
public Action getViewTypeAction(int viewType) {
return new ViewTypeAction(viewType);
}
private static void recursivelySetInheritsPopupMenu(Container container, boolean b) {
if (container instanceof JComponent) {
((JComponent)container).setInheritsPopupMenu(b);
}
int n = container.getComponentCount();
for (int i = 0; i < n; i++) {
recursivelySetInheritsPopupMenu((Container)container.getComponent(i), b);
}
}
protected void installDefaults() {
Locale l = getFileChooser().getLocale();
listViewBorder = UIManager.getBorder("FileChooser.listViewBorder");
listViewBackground = UIManager.getColor("FileChooser.listViewBackground");
listViewWindowsStyle = UIManager.getBoolean("FileChooser.listViewWindowsStyle");
readOnly = UIManager.getBoolean("FileChooser.readOnly");
// TODO: On windows, get the following localized strings from the OS
viewMenuLabelText =
UIManager.getString("FileChooser.viewMenuLabelText", l);
refreshActionLabelText =
UIManager.getString("FileChooser.refreshActionLabelText", l);
newFolderActionLabelText =
UIManager.getString("FileChooser.newFolderActionLabelText", l);
viewTypeActionNames = new String[VIEWTYPE_COUNT];
viewTypeActionNames[VIEWTYPE_LIST] =
UIManager.getString("FileChooser.listViewActionLabelText", l);
viewTypeActionNames[VIEWTYPE_DETAILS] =
UIManager.getString("FileChooser.detailsViewActionLabelText", l);
kiloByteString = UIManager.getString("FileChooser.fileSizeKiloBytes", l);
megaByteString = UIManager.getString("FileChooser.fileSizeMegaBytes", l);
gigaByteString = UIManager.getString("FileChooser.fileSizeGigaBytes", l);
fullRowSelection = UIManager.getBoolean("FileView.fullRowSelection");
filesListAccessibleName = UIManager.getString("FileChooser.filesListAccessibleName", l);
filesDetailsAccessibleName = UIManager.getString("FileChooser.filesDetailsAccessibleName", l);
renameErrorTitleText = UIManager.getString("FileChooser.renameErrorTitleText", l);
renameErrorText = UIManager.getString("FileChooser.renameErrorText", l);
renameErrorFileExistsText = UIManager.getString("FileChooser.renameErrorFileExistsText", l);
}
/**
* Fetches the command list for the FilePane. These commands
* are useful for binding to events, such as in a keymap.
*
* @return the command list
*/
public Action[] getActions() {
if (actions == null) {
class FilePaneAction extends AbstractAction {
FilePaneAction(String name) {
this(name, name);
}
FilePaneAction(String name, String cmd) {
super(name);
putValue(Action.ACTION_COMMAND_KEY, cmd);
}
public void actionPerformed(ActionEvent e) {
String cmd = (String)getValue(Action.ACTION_COMMAND_KEY);
if (cmd == ACTION_CANCEL) {
if (editFile != null) {
cancelEdit();
} else {
getFileChooser().cancelSelection();
}
} else if (cmd == ACTION_EDIT_FILE_NAME) {
JFileChooser fc = getFileChooser();
int index = listSelectionModel.getMinSelectionIndex();
if (index >= 0 && editFile == null &&
(!fc.isMultiSelectionEnabled() ||
fc.getSelectedFiles().length <= 1)) {
editFileName(index);
}
} else if (cmd == ACTION_REFRESH) {
getFileChooser().rescanCurrentDirectory();
}
}
public boolean isEnabled() {
String cmd = (String)getValue(Action.ACTION_COMMAND_KEY);
if (cmd == ACTION_CANCEL) {
return getFileChooser().isEnabled();
} else if (cmd == ACTION_EDIT_FILE_NAME) {
return !readOnly && getFileChooser().isEnabled();
} else {
return true;
}
}
}
ArrayListListSelectionListener
*/
public ListSelectionListener createListSelectionListener() {
return fileChooserUIAccessor.createListSelectionListener();
}
int lastIndex = -1;
File editFile = null;
private int getEditIndex() {
return lastIndex;
}
private void setEditIndex(int i) {
lastIndex = i;
}
private void resetEditIndex() {
lastIndex = -1;
}
private void cancelEdit() {
if (editFile != null) {
editFile = null;
list.remove(editCell);
repaint();
} else if (detailsTable != null && detailsTable.isEditing()) {
detailsTable.getCellEditor().cancelCellEditing();
}
}
JTextField editCell = null;
/**
* @param index visual index of the file to be edited
*/
private void editFileName(int index) {
JFileChooser chooser = getFileChooser();
File currentDirectory = chooser.getCurrentDirectory();
if (readOnly || !canWrite(currentDirectory)) {
return;
}
ensureIndexIsVisible(index);
switch (viewType) {
case VIEWTYPE_LIST:
editFile = (File)getModel().getElementAt(getRowSorter().convertRowIndexToModel(index));
Rectangle r = list.getCellBounds(index, index);
if (editCell == null) {
editCell = new JTextField();
editCell.setName("Tree.cellEditor");
editCell.addActionListener(new EditActionListener());
editCell.addFocusListener(editorFocusListener);
editCell.setNextFocusableComponent(list);
}
list.add(editCell);
editCell.setText(chooser.getName(editFile));
ComponentOrientation orientation = list.getComponentOrientation();
editCell.setComponentOrientation(orientation);
Icon icon = chooser.getIcon(editFile);
// PENDING - grab padding (4) below from defaults table.
int editX = icon == null ? 20 : icon.getIconWidth() + 4;
if (orientation.isLeftToRight()) {
editCell.setBounds(editX + r.x, r.y, r.width - editX, r.height);
} else {
editCell.setBounds(r.x, r.y, r.width - editX, r.height);
}
editCell.requestFocus();
editCell.selectAll();
break;
case VIEWTYPE_DETAILS:
detailsTable.editCellAt(index, COLUMN_FILENAME);
break;
}
}
class EditActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
applyEdit();
}
}
private void applyEdit() {
if (editFile != null && editFile.exists()) {
JFileChooser chooser = getFileChooser();
String oldDisplayName = chooser.getName(editFile);
String oldFileName = editFile.getName();
String newDisplayName = editCell.getText().trim();
String newFileName;
if (!newDisplayName.equals(oldDisplayName)) {
newFileName = newDisplayName;
//Check if extension is hidden from user
int i1 = oldFileName.length();
int i2 = oldDisplayName.length();
if (i1 > i2 && oldFileName.charAt(i2) == '.') {
newFileName = newDisplayName + oldFileName.substring(i2);
}
// rename
FileSystemView fsv = chooser.getFileSystemView();
File f2 = fsv.createFileObject(editFile.getParentFile(), newFileName);
if (f2.exists()) {
JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorFileExistsText, oldFileName),
renameErrorTitleText, JOptionPane.ERROR_MESSAGE);
} else {
if (getModel().renameFile(editFile, f2)) {
if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
if (chooser.isMultiSelectionEnabled()) {
chooser.setSelectedFiles(new File[]{f2});
} else {
chooser.setSelectedFile(f2);
}
} else {
//Could be because of delay in updating Desktop folder
//chooser.setSelectedFile(null);
}
} else {
JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorText, oldFileName),
renameErrorTitleText, JOptionPane.ERROR_MESSAGE);
}
}
}
}
if (detailsTable != null && detailsTable.isEditing()) {
detailsTable.getCellEditor().stopCellEditing();
}
cancelEdit();
}
protected Action newFolderAction;
public Action getNewFolderAction() {
if (!readOnly && newFolderAction == null) {
newFolderAction = new AbstractAction(newFolderActionLabelText) {
private Action basicNewFolderAction;
// Initializer
{
putValue(Action.ACTION_COMMAND_KEY, FilePane.ACTION_NEW_FOLDER);
File currentDirectory = getFileChooser().getCurrentDirectory();
if (currentDirectory != null) {
setEnabled(canWrite(currentDirectory));
}
}
public void actionPerformed(ActionEvent ev) {
if (basicNewFolderAction == null) {
basicNewFolderAction = fileChooserUIAccessor.getNewFolderAction();
}
JFileChooser fc = getFileChooser();
File oldFile = fc.getSelectedFile();
basicNewFolderAction.actionPerformed(ev);
File newFile = fc.getSelectedFile();
if (newFile != null && !newFile.equals(oldFile) && newFile.isDirectory()) {
newFolderFile = newFile;
}
}
};
}
return newFolderAction;
}
protected class FileRenderer extends DefaultListCellRenderer {
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected,
boolean cellHasFocus) {
if (listViewWindowsStyle && !list.isFocusOwner()) {
isSelected = false;
}
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
File file = (File) value;
String fileName = getFileChooser().getName(file);
setText(fileName);
setFont(list.getFont());
Icon icon = getFileChooser().getIcon(file);
if (icon != null) {
setIcon(icon);
} else {
if (getFileChooser().getFileSystemView().isTraversable(file)) {
setText(fileName+File.separator);
}
}
return this;
}
}
void setFileSelected() {
if (getFileChooser().isMultiSelectionEnabled() && !isDirectorySelected()) {
File[] files = getFileChooser().getSelectedFiles(); // Should be selected
Object[] selectedObjects = list.getSelectedValues(); // Are actually selected
listSelectionModel.setValueIsAdjusting(true);
try {
int lead = listSelectionModel.getLeadSelectionIndex();
int anchor = listSelectionModel.getAnchorSelectionIndex();
Arrays.sort(files);
Arrays.sort(selectedObjects);
int shouldIndex = 0;
int actuallyIndex = 0;
// Remove files that shouldn't be selected and add files which should be selected
// Note: Assume files are already sorted in compareTo order.
while (shouldIndex < files.length &&
actuallyIndex < selectedObjects.length) {
int comparison = files[shouldIndex].compareTo((File)selectedObjects[actuallyIndex]);
if (comparison < 0) {
doSelectFile(files[shouldIndex++]);
} else if (comparison > 0) {
doDeselectFile(selectedObjects[actuallyIndex++]);
} else {
// Do nothing
shouldIndex++;
actuallyIndex++;
}
}
while (shouldIndex < files.length) {
doSelectFile(files[shouldIndex++]);
}
while (actuallyIndex < selectedObjects.length) {
doDeselectFile(selectedObjects[actuallyIndex++]);
}
// restore the anchor and lead
if (listSelectionModel instanceof DefaultListSelectionModel) {
((DefaultListSelectionModel)listSelectionModel).
moveLeadSelectionIndex(lead);
listSelectionModel.setAnchorSelectionIndex(anchor);
}
} finally {
listSelectionModel.setValueIsAdjusting(false);
}
} else {
JFileChooser chooser = getFileChooser();
File f;
if (isDirectorySelected()) {
f = getDirectory();
} else {
f = chooser.getSelectedFile();
}
int i;
if (f != null && (i = getModel().indexOf(f)) >= 0) {
int viewIndex = getRowSorter().convertRowIndexToView(i);
listSelectionModel.setSelectionInterval(viewIndex, viewIndex);
ensureIndexIsVisible(viewIndex);
} else {
clearSelection();
}
}
}
private void doSelectFile(File fileToSelect) {
int index = getModel().indexOf(fileToSelect);
// could be missed in the current directory if it changed
if (index >= 0) {
index = getRowSorter().convertRowIndexToView(index);
listSelectionModel.addSelectionInterval(index, index);
}
}
private void doDeselectFile(Object fileToDeselect) {
int index = getRowSorter().convertRowIndexToView(
getModel().indexOf(fileToDeselect));
listSelectionModel.removeSelectionInterval(index, index);
}
/* The following methods are used by the PropertyChange Listener */
private void doSelectedFileChanged(PropertyChangeEvent e) {
applyEdit();
File f = (File) e.getNewValue();
JFileChooser fc = getFileChooser();
if (f != null
&& ((fc.isFileSelectionEnabled() && !f.isDirectory())
|| (f.isDirectory() && fc.isDirectorySelectionEnabled()))) {
setFileSelected();
}
}
private void doSelectedFilesChanged(PropertyChangeEvent e) {
applyEdit();
File[] files = (File[]) e.getNewValue();
JFileChooser fc = getFileChooser();
if (files != null
&& files.length > 0
&& (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) {
setFileSelected();
}
}
private void doDirectoryChanged(PropertyChangeEvent e) {
getDetailsTableModel().updateColumnInfo();
JFileChooser fc = getFileChooser();
FileSystemView fsv = fc.getFileSystemView();
applyEdit();
resetEditIndex();
ensureIndexIsVisible(0);
File currentDirectory = fc.getCurrentDirectory();
if (currentDirectory != null) {
if (!readOnly) {
getNewFolderAction().setEnabled(canWrite(currentDirectory));
}
fileChooserUIAccessor.getChangeToParentDirectoryAction().setEnabled(!fsv.isRoot(currentDirectory));
}
if (list != null) {
list.clearSelection();
}
}
private void doFilterChanged(PropertyChangeEvent e) {
applyEdit();
resetEditIndex();
clearSelection();
}
private void doFileSelectionModeChanged(PropertyChangeEvent e) {
applyEdit();
resetEditIndex();
clearSelection();
}
private void doMultiSelectionChanged(PropertyChangeEvent e) {
if (getFileChooser().isMultiSelectionEnabled()) {
listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
} else {
listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
clearSelection();
getFileChooser().setSelectedFiles(null);
}
}
/*
* Listen for filechooser property changes, such as
* the selected file changing, or the type of the dialog changing.
*/
public void propertyChange(PropertyChangeEvent e) {
if (viewType == -1) {
setViewType(VIEWTYPE_LIST);
}
String s = e.getPropertyName();
if (s.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
doSelectedFileChanged(e);
} else if (s.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) {
doSelectedFilesChanged(e);
} else if (s.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {
doDirectoryChanged(e);
} else if (s.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) {
doFilterChanged(e);
} else if (s.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {
doFileSelectionModeChanged(e);
} else if (s.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {
doMultiSelectionChanged(e);
} else if (s.equals(JFileChooser.CANCEL_SELECTION)) {
applyEdit();
} else if (s.equals("busy")) {
setCursor((Boolean)e.getNewValue() ? waitCursor : null);
} else if (s.equals("componentOrientation")) {
ComponentOrientation o = (ComponentOrientation)e.getNewValue();
JFileChooser cc = (JFileChooser)e.getSource();
if (o != e.getOldValue()) {
cc.applyComponentOrientation(o);
}
if (detailsTable != null) {
detailsTable.setComponentOrientation(o);
detailsTable.getParent().getParent().setComponentOrientation(o);
}
}
}
private void ensureIndexIsVisible(int i) {
if (i >= 0) {
if (list != null) {
list.ensureIndexIsVisible(i);
}
if (detailsTable != null) {
detailsTable.scrollRectToVisible(detailsTable.getCellRect(i, COLUMN_FILENAME, true));
}
}
}
public void ensureFileIsVisible(JFileChooser fc, File f) {
int modelIndex = getModel().indexOf(f);
if (modelIndex >= 0) {
ensureIndexIsVisible(getRowSorter().convertRowIndexToView(modelIndex));
}
}
public void rescanCurrentDirectory() {
getModel().validateFileCache();
}
public void clearSelection() {
if (listSelectionModel != null) {
listSelectionModel.clearSelection();
if (listSelectionModel instanceof DefaultListSelectionModel) {
((DefaultListSelectionModel)listSelectionModel).moveLeadSelectionIndex(0);
listSelectionModel.setAnchorSelectionIndex(0);
}
}
}
public JMenu getViewMenu() {
if (viewMenu == null) {
viewMenu = new JMenu(viewMenuLabelText);
ButtonGroup viewButtonGroup = new ButtonGroup();
for (int i = 0; i < VIEWTYPE_COUNT; i++) {
JRadioButtonMenuItem mi =
new JRadioButtonMenuItem(new ViewTypeAction(i));
viewButtonGroup.add(mi);
viewMenu.add(mi);
}
updateViewMenu();
}
return viewMenu;
}
private void updateViewMenu() {
if (viewMenu != null) {
Component[] comps = viewMenu.getMenuComponents();
for (Component comp : comps) {
if (comp instanceof JRadioButtonMenuItem) {
JRadioButtonMenuItem mi = (JRadioButtonMenuItem) comp;
if (((ViewTypeAction)mi.getAction()).viewType == viewType) {
mi.setSelected(true);
}
}
}
}
}
public JPopupMenu getComponentPopupMenu() {
JPopupMenu popupMenu = getFileChooser().getComponentPopupMenu();
if (popupMenu != null) {
return popupMenu;
}
JMenu viewMenu = getViewMenu();
if (contextMenu == null) {
contextMenu = new JPopupMenu();
if (viewMenu != null) {
contextMenu.add(viewMenu);
if (listViewWindowsStyle) {
contextMenu.addSeparator();
}
}
ActionMap actionMap = getActionMap();
Action refreshAction = actionMap.get(ACTION_REFRESH);
Action newFolderAction = actionMap.get(ACTION_NEW_FOLDER);
if (refreshAction != null) {
contextMenu.add(refreshAction);
if (listViewWindowsStyle && newFolderAction != null) {
contextMenu.addSeparator();
}
}
if (newFolderAction != null) {
contextMenu.add(newFolderAction);
}
}
if (viewMenu != null) {
viewMenu.getPopupMenu().setInvoker(viewMenu);
}
return contextMenu;
}
private Handler handler;
protected Handler getMouseHandler() {
if (handler == null) {
handler = new Handler();
}
return handler;
}
private class Handler implements MouseListener {
private MouseListener doubleClickListener;
public void mouseClicked(MouseEvent evt) {
JComponent source = (JComponent)evt.getSource();
int index;
if (source instanceof JList) {
index = SwingUtilities2.loc2IndexFileList(list, evt.getPoint());
} else if (source instanceof JTable) {
JTable table = (JTable)source;
Point p = evt.getPoint();
index = table.rowAtPoint(p);
boolean pointOutsidePrefSize =
SwingUtilities2.pointOutsidePrefSize(
table, index, table.columnAtPoint(p), p);
if (pointOutsidePrefSize && !fullRowSelection) {
return;
}
// Translate point from table to list
if (index >= 0 && list != null &&
listSelectionModel.isSelectedIndex(index)) {
// Make a new event with the list as source, placing the
// click in the corresponding list cell.
Rectangle r = list.getCellBounds(index, index);
evt = new MouseEvent(list, evt.getID(),
evt.getWhen(), evt.getModifiers(),
r.x + 1, r.y + r.height/2,
evt.getXOnScreen(),
evt.getYOnScreen(),
evt.getClickCount(), evt.isPopupTrigger(),
evt.getButton());
}
} else {
return;
}
if (index >= 0 && SwingUtilities.isLeftMouseButton(evt)) {
JFileChooser fc = getFileChooser();
// For single click, we handle editing file name
if (evt.getClickCount() == 1 && source instanceof JList) {
if ((!fc.isMultiSelectionEnabled() || fc.getSelectedFiles().length <= 1)
&& index >= 0 && listSelectionModel.isSelectedIndex(index)
&& getEditIndex() == index && editFile == null) {
editFileName(index);
} else {
if (index >= 0) {
setEditIndex(index);
} else {
resetEditIndex();
}
}
} else if (evt.getClickCount() == 2) {
// on double click (open or drill down one directory) be
// sure to clear the edit index
resetEditIndex();
}
}
// Forward event to Basic
if (getDoubleClickListener() != null) {
getDoubleClickListener().mouseClicked(evt);
}
}
public void mouseEntered(MouseEvent evt) {
JComponent source = (JComponent)evt.getSource();
if (source instanceof JTable) {
JTable table = (JTable)evt.getSource();
TransferHandler th1 = getFileChooser().getTransferHandler();
TransferHandler th2 = table.getTransferHandler();
if (th1 != th2) {
table.setTransferHandler(th1);
}
boolean dragEnabled = getFileChooser().getDragEnabled();
if (dragEnabled != table.getDragEnabled()) {
table.setDragEnabled(dragEnabled);
}
} else if (source instanceof JList) {
// Forward event to Basic
if (getDoubleClickListener() != null) {
getDoubleClickListener().mouseEntered(evt);
}
}
}
public void mouseExited(MouseEvent evt) {
if (evt.getSource() instanceof JList) {
// Forward event to Basic
if (getDoubleClickListener() != null) {
getDoubleClickListener().mouseExited(evt);
}
}
}
public void mousePressed(MouseEvent evt) {
if (evt.getSource() instanceof JList) {
// Forward event to Basic
if (getDoubleClickListener() != null) {
getDoubleClickListener().mousePressed(evt);
}
}
}
public void mouseReleased(MouseEvent evt) {
if (evt.getSource() instanceof JList) {
// Forward event to Basic
if (getDoubleClickListener() != null) {
getDoubleClickListener().mouseReleased(evt);
}
}
}
private MouseListener getDoubleClickListener() {
// Lazy creation of Basic's listener
if (doubleClickListener == null && list != null) {
doubleClickListener =
fileChooserUIAccessor.createDoubleClickListener(list);
}
return doubleClickListener;
}
}
/**
* Property to remember whether a directory is currently selected in the UI.
*
* @return true
iff a directory is currently selected.
*/
protected boolean isDirectorySelected() {
return fileChooserUIAccessor.isDirectorySelected();
}
/**
* Property to remember the directory that is currently selected in the UI.
*
* @return the value of the directory
property
* @see javax.swing.plaf.basic.BasicFileChooserUI#setDirectory
*/
protected File getDirectory() {
return fileChooserUIAccessor.getDirectory();
}
private Component findChildComponent(Container container, Class cls) {
int n = container.getComponentCount();
for (int i = 0; i < n; i++) {
Component comp = container.getComponent(i);
if (cls.isInstance(comp)) {
return comp;
} else if (comp instanceof Container) {
Component c = findChildComponent((Container)comp, cls);
if (c != null) {
return c;
}
}
}
return null;
}
public boolean canWrite(File f) {
// Return false for non FileSystem files or if file doesn't exist.
if (!f.exists()) {
return false;
}
if (f instanceof ShellFolder) {
return ((ShellFolder) f).isFileSystem();
} else {
if (usesShellFolder(getFileChooser())) {
try {
return ShellFolder.getShellFolder(f).isFileSystem();
} catch (FileNotFoundException ex) {
// File doesn't exist
return false;
}
} else {
// Ordinary file
return true;
}
}
}
/**
* Returns true if specified FileChooser should use ShellFolder
*/
public static boolean usesShellFolder(JFileChooser chooser) {
Boolean prop = (Boolean) chooser.getClientProperty("FileChooser.useShellFolder");
return prop == null ? chooser.getFileSystemView().equals(FileSystemView.getFileSystemView())
: prop.booleanValue();
}
// This interface is used to access methods in the FileChooserUI
// that are not public.
public interface FileChooserUIAccessor {
public JFileChooser getFileChooser();
public BasicDirectoryModel getModel();
public JPanel createList();
public JPanel createDetailsView();
public boolean isDirectorySelected();
public File getDirectory();
public Action getApproveSelectionAction();
public Action getChangeToParentDirectoryAction();
public Action getNewFolderAction();
public MouseListener createDoubleClickListener(JList list);
public ListSelectionListener createListSelectionListener();
}
}