< prev index next >
src/java.desktop/share/classes/sun/swing/FilePane.java
Print this page
@@ -22,45 +22,63 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.swing;
+import com.sun.java.swing.plaf.windows.WindowsFileChooserUI;
+import com.sun.java.swing.plaf.windows.WindowsFileChooserUI.WindowsFileListUI;
+import static com.sun.java.swing.plaf.windows.WindowsFileChooserUI.WindowsFileListUI.HOVER_CELL_PROPERTY;
+import com.sun.java.swing.plaf.windows.WindowsFileChooserUI.WindowsFileTableUI;
import java.awt.*;
+import static java.awt.Color.BLACK;
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 java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.function.Supplier;
+import java.util.stream.IntStream;
import javax.accessibility.AccessibleContext;
import javax.swing.*;
+import static javax.swing.JList.HORIZONTAL_WRAP;
+import static javax.swing.SwingConstants.BOTTOM;
+import static javax.swing.SwingConstants.CENTER;
+import static javax.swing.SwingConstants.LEFT;
+import static javax.swing.SwingConstants.RIGHT;
import javax.swing.border.*;
-import javax.swing.event.*;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.RowSorterEvent;
+import javax.swing.event.RowSorterListener;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
import javax.swing.filechooser.*;
import javax.swing.plaf.basic.*;
import javax.swing.table.*;
import javax.swing.text.*;
import sun.awt.shell.*;
/**
- * <b>WARNING:</b> 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.
+ * <b>WARNING:</b> 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.
* <p>
* 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
*/
-@SuppressWarnings("serial") // JDK-implementation class
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 static final String ACTION_APPROVE_SELECTION = "approveSelection";
@@ -70,21 +88,89 @@
public static final String ACTION_CHANGE_TO_PARENT_DIRECTORY = "Go Up";
public static final String ACTION_NEW_FOLDER = "New Folder";
public static final String ACTION_VIEW_LIST = "viewTypeList";
public static final String ACTION_VIEW_DETAILS = "viewTypeDetails";
- private Action[] actions;
+ public static enum ViewType {
+ MEDIUM_ICONS("mediumIcons",
+ /* listOrientation: */ HORIZONTAL_WRAP,
+ /* horizTextPos: */ CENTER,
+ /* vertTextPos: */ BOTTOM,
+ /* prefSize: */ new Dimension(125, 75),
+ /* horizAlign: */ CENTER,
+ /* wrapFilename: */ true,
+ /* largeIcons: */ true,
+ /* fileMetaLines: */ false
+ ),
+ SMALL_ICONS("smallIcons",
+ /* listOrientation: */ HORIZONTAL_WRAP,
+ /* horizTextPos: */ RIGHT,
+ /* vertTextPos: */ CENTER,
+ /* prefSize: */ null,
+ /* horizAlign: */ LEFT,
+ /* wrapFilename: */ true,
+ /* largeIcons: */ false,
+ /* fileMetaLines: */ false
+ ),
+ LIST("list",
+ /* listOrientation: */ JList.VERTICAL_WRAP,
+ /* horizTextPos: */ RIGHT,
+ /* vertTextPos: */ CENTER,
+ /* prefSize: */ null,
+ /* horizAlign: */ LEFT,
+ /* wrapFilename: */ false,
+ /* largeIcons: */ false,
+ /* fileMetaLines: */ false
+ ),
+ DETAILS("details", null, RIGHT, CENTER, null, LEFT, false, false, false),
+ TILES("tiles",
+ /* listOrientation: */ JList.HORIZONTAL_WRAP,
+ /* horizTextPos: */ RIGHT,
+ /* vertTextPos: */ CENTER,
+ /* prefSize: */ new Dimension(313, 66),
+ /* horizAlign: */ LEFT,
+ /* wrapFilename: */ false,
+ /* largeIcons: */ true,
+ /* fileMetaLines: */ true
+ );
+
+ private final String l10nKey;
+ private final boolean isTable;
+ private final Integer listOrientation;
+ private final Integer horizTextPos;
+ private final Integer vertTextPos;
+ private final Dimension prefSize;
+ private final Integer horizAlign;
+ private final boolean wrapFilename;
+ private final boolean largeIcons;
+ private final boolean fileMetaLines;
+
+ private ViewType(String l10nKey, Integer listOrientation,
+ Integer horizTextPos, Integer vertTextPos, Dimension prefSize,
+ Integer horizAlign, boolean wrapFilename, boolean largeIcons,
+ boolean fileMetaLines) {
+ this.listOrientation = listOrientation;
+ this.l10nKey = l10nKey;
+ this.isTable = listOrientation == null;
+
+ this.horizTextPos = horizTextPos;
+ this.vertTextPos = vertTextPos;
+ this.prefSize = prefSize;
+ this.horizAlign = horizAlign;
+ this.wrapFilename = wrapFilename;
+ this.largeIcons = largeIcons;
+ this.fileMetaLines = fileMetaLines;
+ }
+
+ }
- // "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 Action[] actions;
- private int viewType = -1;
- private JPanel[] viewPanels = new JPanel[VIEWTYPE_COUNT];
+ private ViewType viewType;
+ private Map<ViewType, JPanel> viewPanels = new EnumMap<>(ViewType.class);
private JPanel currentViewPanel;
- private String[] viewTypeActionNames;
+ private final Map<ViewType, String> viewTypeActionNames = new EnumMap<>(ViewType.class);
private String filesListAccessibleName = null;
private String filesDetailsAccessibleName = null;
private JPopupMenu contextMenu;
@@ -117,16 +203,16 @@
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 <code>timeFactor</code>. 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.
+ * the sequence of alphanumeric keys pressed by the user with delay less
+ * than value of <code>timeFactor</code>. 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();
@@ -307,66 +393,66 @@
protected BasicDirectoryModel getModel() {
return fileChooserUIAccessor.getModel();
}
- public int getViewType() {
+ public ViewType getViewType() {
return viewType;
}
- public void setViewType(int viewType) {
+ public void setViewType(ViewType viewType) {
if (viewType == this.viewType) {
return;
}
- int oldValue = this.viewType;
+ ViewType oldValue = this.viewType;
this.viewType = viewType;
JPanel createdViewPanel = null;
Component newFocusOwner = null;
- switch (viewType) {
- case VIEWTYPE_LIST:
- if (viewPanels[viewType] == null) {
+ if (viewType.isTable) {
+ if (viewPanels.get(viewType) == null) {
+ createdViewPanel = fileChooserUIAccessor.createDetailsView();
+ if (createdViewPanel == null) {
+ createdViewPanel = createDetailsView();
+ }
+
+ detailsTable = (JTable) findChildComponent(createdViewPanel, JTable.class);
+ assert detailsTable != null;
+ detailsTable.setRowHeight(Math.max(detailsTable.getFont().getSize() + 4, 16 + 5));
+ if(listSelectionModel == null) {
+ listSelectionModel = detailsTable.getSelectionModel();
+ if(list != null)
+ list.setSelectionModel(listSelectionModel);
+ }else
+ detailsTable.setSelectionModel(listSelectionModel);
+ }
+ newFocusOwner = detailsTable;
+ } else {
+ if (viewPanels.get(viewType) == null) {
createdViewPanel = fileChooserUIAccessor.createList();
if (createdViewPanel == null) {
createdViewPanel = createList();
}
- list = findChildComponent(createdViewPanel, JList.class);
+ 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);
+ list.setLayoutOrientation(viewType.listOrientation);
newFocusOwner = list;
- break;
-
- case VIEWTYPE_DETAILS:
- if (viewPanels[viewType] == null) {
- createdViewPanel = fileChooserUIAccessor.createDetailsView();
- if (createdViewPanel == null) {
- createdViewPanel = createDetailsView();
- }
-
- detailsTable = 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;
+ viewPanels.put(viewType, createdViewPanel);
recursivelySetInheritsPopupMenu(createdViewPanel, true);
}
boolean isFocusOwner = false;
@@ -377,11 +463,11 @@
isFocusOwner = owner == detailsTable || owner == list;
remove(currentViewPanel);
}
- currentViewPanel = viewPanels[viewType];
+ currentViewPanel = viewPanels.get(viewType);
add(currentViewPanel, BorderLayout.CENTER);
if (isFocusOwner && newFocusOwner != null) {
newFocusOwner.requestFocusInWindow();
}
@@ -390,33 +476,27 @@
repaint();
updateViewMenu();
firePropertyChange("viewType", oldValue, viewType);
}
- @SuppressWarnings("serial") // JDK-implementation class
class ViewTypeAction extends AbstractAction {
- private int viewType;
- ViewTypeAction(int viewType) {
- super(viewTypeActionNames[viewType]);
+ private final ViewType viewType;
+
+ ViewTypeAction(ViewType viewType) {
+ super(viewTypeActionNames.get(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);
+ putValue(Action.ACTION_COMMAND_KEY, viewType.l10nKey);
}
public void actionPerformed(ActionEvent e) {
setViewType(viewType);
}
}
- public Action getViewTypeAction(int viewType) {
+ public Action getViewTypeAction(ViewType viewType) {
return new ViewTypeAction(viewType);
}
private static void recursivelySetInheritsPopupMenu(Container container, boolean b) {
if (container instanceof JComponent) {
@@ -435,23 +515,23 @@
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);
+ viewMenuLabelText
+ = UIManager.getString("FileChooser.viewMenuLabelText", l);
+ refreshActionLabelText
+ = UIManager.getString("FileChooser.refreshActionLabelText", l);
+ newFolderActionLabelText
+ = UIManager.getString("FileChooser.newFolderActionLabelText", l);
+
+ for (ViewType viewType : ViewType.values()) {
+ final String key = "FileChooser." + viewType.l10nKey
+ + "ViewActionLabel.textAndMnemonic";
+ viewTypeActionNames.put(viewType,
+ UIManager.getString(key, l));
+ }
kiloByteString = UIManager.getString("FileChooser.fileSizeKiloBytes", l);
megaByteString = UIManager.getString("FileChooser.fileSizeMegaBytes", l);
gigaByteString = UIManager.getString("FileChooser.fileSizeGigaBytes", l);
fullRowSelection = UIManager.getBoolean("FileView.fullRowSelection");
@@ -463,12 +543,12 @@
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.
+ * 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) {
@@ -505,11 +585,11 @@
getFileChooser().rescanCurrentDirectory();
}
}
public boolean isEnabled() {
- String cmd = (String)getValue(Action.ACTION_COMMAND_KEY);
+ 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 {
@@ -535,15 +615,15 @@
}
action = getNewFolderAction();
if (action != null) {
actionList.add(action);
}
- action = getViewTypeAction(VIEWTYPE_LIST);
+ action = getViewTypeAction(ViewType.LIST);
if (action != null) {
actionList.add(action);
}
- action = getViewTypeAction(VIEWTYPE_DETAILS);
+ action = getViewTypeAction(ViewType.DETAILS);
if (action != null) {
actionList.add(action);
}
actions = actionList.toArray(new Action[actionList.size()]);
}
@@ -553,24 +633,22 @@
protected void createActionMap() {
addActionsToMap(super.getActionMap(), getActions());
}
-
public static void addActionsToMap(ActionMap map, Action[] actions) {
if (map != null && actions != null) {
for (Action a : actions) {
- String cmd = (String)a.getValue(Action.ACTION_COMMAND_KEY);
+ String cmd = (String) a.getValue(Action.ACTION_COMMAND_KEY);
if (cmd == null) {
- cmd = (String)a.getValue(Action.NAME);
+ cmd = (String) a.getValue(Action.NAME);
}
map.put(cmd, a);
}
}
}
-
private void updateListRowCount(JList<?> list) {
if (smallIconsView) {
list.setVisibleRowCount(getModel().getSize() / 3);
} else {
list.setVisibleRowCount(-1);
@@ -578,12 +656,10 @@
}
public JPanel createList() {
JPanel p = new JPanel(new BorderLayout());
final JFileChooser fileChooser = getFileChooser();
-
- @SuppressWarnings("serial") // anonymous class
final JList<Object> list = new JList<Object>() {
public int getNextMatch(String prefix, int startIndex, Position.Bias bias) {
ListModel<?> model = getModel();
int max = model.getSize();
if (prefix == null || startIndex < 0 || startIndex >= max) {
@@ -597,11 +673,18 @@
return i;
}
}
return -1;
}
+
+ {
+ if (listViewWindowsStyle) {
+ setUI(new WindowsFileListUI(getFileChooser()));
+ }
+ }
};
+
list.setCellRenderer(new FileRenderer());
list.setLayoutOrientation(JList.VERTICAL_WRAP);
// 4835633 : tell BasicListUI that this is a file list
list.putClientProperty("List.isFileList", Boolean.TRUE);
@@ -641,11 +724,13 @@
JScrollPane scrollpane = new JScrollPane(list);
if (listViewBackground != null) {
list.setBackground(listViewBackground);
}
- if (listViewBorder != null) {
+ if (listViewWindowsStyle) {
+ scrollpane.setBorder(null);
+ } else if (listViewBorder != null) {
scrollpane.setBorder(listViewBorder);
}
list.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesListAccessibleName);
@@ -654,11 +739,10 @@
}
/**
* This model allows for sorting JList
*/
- @SuppressWarnings("serial") // JDK-implementation class
private class SortableListModel extends AbstractListModel<Object>
implements TableModelListener, RowSorterListener {
public SortableListModel() {
getDetailsTableModel().addTableModelListener(this);
@@ -682,18 +766,18 @@
fireContentsChanged(this, 0, getSize());
}
}
private DetailsTableModel getDetailsTableModel() {
- if(detailsTableModel == null) {
+ if (detailsTableModel == null) {
detailsTableModel = new DetailsTableModel(getFileChooser());
}
return detailsTableModel;
}
- @SuppressWarnings("serial") // JDK-implementation class
class DetailsTableModel extends AbstractTableModel implements ListDataListener {
+
JFileChooser chooser;
BasicDirectoryModel directoryModel;
ShellFolderColumnInfo[] columns;
int[] columnMap;
@@ -716,12 +800,12 @@
}
}
ShellFolderColumnInfo[] allColumns = ShellFolder.getFolderColumns(dir);
- ArrayList<ShellFolderColumnInfo> visibleColumns =
- new ArrayList<ShellFolderColumnInfo>();
+ ArrayList<ShellFolderColumnInfo> visibleColumns
+ = new ArrayList<ShellFolderColumnInfo>();
columnMap = new int[allColumns.length];
for (int i = 0; i < allColumns.length; i++) {
ShellFolderColumnInfo column = allColumns[i];
if (column.isVisible()) {
columnMap[visibleColumns.size()] = i;
@@ -807,18 +891,16 @@
if (FilePane.this.getModel().renameFile(f, f2)) {
if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
// The setSelectedFile method produces a new setValueAt invocation while the JTable
// is editing. Postpone file selection to be sure that edit mode of the JTable
// is completed
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
+ SwingUtilities.invokeLater(() -> {
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);
}
@@ -863,11 +945,10 @@
public ShellFolderColumnInfo[] getColumns() {
return columns;
}
}
-
private void updateDetailsColumnModel(JTable table) {
if (table != null) {
ShellFolderColumnInfo[] columns = detailsTableModel.getColumns();
TableColumnModel columnModel = new DefaultTableColumnModel();
@@ -898,10 +979,12 @@
if (!readOnly && columnModel.getColumnCount() > COLUMN_FILENAME) {
columnModel.getColumn(COLUMN_FILENAME).
setCellEditor(getDetailsTableCellEditor());
}
+ columnModel.getColumn(0).setPreferredWidth(160); // TODO néha 250 körüli (pl. dl mappa)
+
table.setColumnModel(columnModel);
}
}
private DetailsTableRowSorter getRowSorter() {
@@ -913,16 +996,12 @@
private class DetailsTableRowSorter extends TableRowSorter<TableModel> {
public DetailsTableRowSorter() {
SorterModelWrapper modelWrapper = new SorterModelWrapper();
setModelWrapper(modelWrapper);
- modelWrapper.getModel().addTableModelListener(
- new TableModelListener() {
- @Override
- public void tableChanged(TableModelEvent e) {
+ modelWrapper.getModel().addTableModelListener(evt -> {
modelStructureChanged();
- }
});
}
public void updateComparators(ShellFolderColumnInfo [] columns) {
for (int i = 0; i < columns.length; i++) {
@@ -934,15 +1013,13 @@
}
}
@Override
public void sort() {
- ShellFolder.invoke(new Callable<Void>() {
- public Void call() {
+ ShellFolder.invoke(() -> {
DetailsTableRowSorter.super.sort();
return null;
- }
});
}
public void modelStructureChanged() {
super.modelStructureChanged();
@@ -975,10 +1052,11 @@
/**
* This class sorts directories before files, comparing directory to
* directory and file to file using the wrapped comparator.
*/
private class DirectoriesFirstComparatorWrapper implements Comparator<File> {
+
private Comparator<Object> comparator;
private int column;
@SuppressWarnings("unchecked")
public DirectoriesFirstComparatorWrapper(int column, Comparator<?> comparator) {
@@ -1068,25 +1146,47 @@
public Insets getInsets(Insets i) {
// Provide some space between columns
i = super.getInsets(i);
i.left += 4;
i.right += 4;
+ if (listViewWindowsStyle) {
+ i.bottom++;
+ }
return i;
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
- if ((table.convertColumnIndexToModel(column) != COLUMN_FILENAME ||
- (listViewWindowsStyle && !table.isFocusOwner())) &&
- !fullRowSelection) {
+ if ((table.convertColumnIndexToModel(column) != COLUMN_FILENAME
+ || (listViewWindowsStyle && !table.isFocusOwner()))
+ && !fullRowSelection) {
isSelected = false;
}
super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
+ Object hoverCelllValue = table.getClientProperty(WindowsFileListUI.HOVER_CELL_PROPERTY);
+ if(!isSelected)
+ if(hoverCelllValue instanceof Integer && row == (int) hoverCelllValue)
+ setBackground(WindowsFileListUI.ITEM_HOVERED_COLOR);
+ else
+ setBackground(null);
+
+ if(isSelected) {
+ Border border = column == 0 ?
+ WindowsFileTableUI.ITEM_SELECTED_BORDER_LEFT_CELL:
+ column == table.getModel().getColumnCount()-1?
+ WindowsFileTableUI.ITEM_SELECTED_BORDER_RIGHT_CELL:
+ WindowsFileTableUI.ITEM_SELECTED_BORDER_MID_CELL;
+
+ setBorder(border);
+ }
+
+ setForeground(column == 0 ? BLACK : listViewWindowsStyle ?
+ SystemColor.controlDkShadow : new Color(150, 150, 150));
setIcon(null);
int modelColumn = table.convertColumnIndexToModel(column);
ShellFolderColumnInfo columnInfo = detailsTableModel.getColumns()[modelColumn];
@@ -1107,11 +1207,12 @@
text = "";
} else if (value instanceof File) {
File file = (File)value;
text = chooser.getName(file);
- Icon icon = chooser.getIcon(file);
+ Icon icon = new FileIcon(file, () -> chooser.getIcon(file),
+ table, 16, 16);
setIcon(icon);
} else if (value instanceof Long) {
long len = ((Long) value) / 1024L;
if (listViewWindowsStyle) {
@@ -1144,12 +1245,17 @@
public JPanel createDetailsView() {
final JFileChooser chooser = getFileChooser();
JPanel p = new JPanel(new BorderLayout());
- @SuppressWarnings("serial") // anonymous class
final JTable detailsTable = new JTable(getDetailsTableModel()) {
+
+ {
+ if(listViewWindowsStyle)
+ setUI(new WindowsFileChooserUI.WindowsFileTableUI(chooser));
+ }
+
// Handle Escape key events here
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE && getCellEditor() == null) {
// We are not editing, forward to filechooser.
chooser.dispatchEvent(e);
@@ -1171,19 +1277,28 @@
detailsTable.setRowSorter(getRowSorter());
detailsTable.setAutoCreateColumnsFromModel(false);
detailsTable.setComponentOrientation(chooser.getComponentOrientation());
detailsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
detailsTable.setShowGrid(false);
+ detailsTable.putClientProperty("Table.isFileList", Boolean.TRUE);
detailsTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
detailsTable.addKeyListener(detailsKeyListener);
+ if(listViewWindowsStyle) {
+ detailsTable.setSelectionBackground(WindowsFileListUI.ITEM_SELECTED_COLOR);
+ }
+ detailsTable.getSelectionModel().setSelectionMode(
+ getFileChooser().isMultiSelectionEnabled()?
+ ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:
+ ListSelectionModel.SINGLE_SELECTION);
- Font font = list.getFont();
- detailsTable.setFont(font);
- detailsTable.setIntercellSpacing(new Dimension(0, 0));
+ detailsTable.setIntercellSpacing(new Dimension(0,
+ listViewWindowsStyle ? -1 : 0));
TableCellRenderer headerRenderer =
new AlignableTableHeaderRenderer(detailsTable.getTableHeader().getDefaultRenderer());
+ if(listViewWindowsStyle)
+ detailsTable.getTableHeader().setBackground(Color.white);
detailsTable.getTableHeader().setDefaultRenderer(headerRenderer);
TableCellRenderer cellRenderer = new DetailsTableCellRenderer(chooser);
detailsTable.setDefaultRenderer(Object.class, cellRenderer);
// So that drag can be started on a mouse press
@@ -1192,13 +1307,10 @@
detailsTable.addMouseListener(getMouseHandler());
// No need to addListSelectionListener because selections are forwarded
// to our JList.
- // 4835633 : tell BasicTableUI that this is a file list
- detailsTable.putClientProperty("Table.isFileList", Boolean.TRUE);
-
if (listViewWindowsStyle) {
detailsTable.addFocusListener(repaintListener);
}
// TAB/SHIFT-TAB should transfer focus and ENTER should select an item.
@@ -1213,22 +1325,14 @@
detailsTable.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
null);
JScrollPane scrollpane = new JScrollPane(detailsTable);
scrollpane.setComponentOrientation(chooser.getComponentOrientation());
+ if(listViewWindowsStyle)
+ scrollpane.putClientProperty("ScrollPaneLayout.vsbAtTop", "true");
LookAndFeel.installColors(scrollpane.getViewport(), "Table.background", "Table.foreground");
- // Adjust width of first column so the table fills the viewport when
- // first displayed (temporary listener).
- scrollpane.addComponentListener(new ComponentAdapter() {
- public void componentResized(ComponentEvent e) {
- JScrollPane sp = (JScrollPane)e.getComponent();
- fixNameColumnWidth(sp.getViewport().getSize().width);
- sp.removeComponentListener(this);
- }
- });
-
// 4835633.
// If the mouse is pressed in the area below the Details view table, the
// event is not dispatched to the Table MouseListener but to the
// scrollpane. Listen for that here so we can clear the selection.
scrollpane.addMouseListener(new MouseAdapter() {
@@ -1244,13 +1348,10 @@
}
}
}
});
- detailsTable.setForeground(list.getForeground());
- detailsTable.setBackground(list.getBackground());
-
if (listViewBorder != null) {
scrollpane.setBorder(listViewBorder);
}
p.add(scrollpane, BorderLayout.CENTER);
@@ -1273,33 +1374,27 @@
boolean hasFocus, int row, int column) {
Component c = wrappedRenderer.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
+ if(!listViewWindowsStyle) {
int modelColumn = table.convertColumnIndexToModel(column);
ShellFolderColumnInfo columnInfo = detailsTableModel.getColumns()[modelColumn];
Integer alignment = columnInfo.getAlignment();
if (alignment == null) {
alignment = SwingConstants.CENTER;
}
if (c instanceof JLabel) {
((JLabel) c).setHorizontalAlignment(alignment);
}
+ }
return c;
}
}
- private void fixNameColumnWidth(int viewWidth) {
- TableColumn nameCol = detailsTable.getColumnModel().getColumn(COLUMN_FILENAME);
- int tableWidth = detailsTable.getPreferredSize().width;
-
- if (tableWidth < viewWidth) {
- nameCol.setPreferredWidth(nameCol.getPreferredWidth() + viewWidth - tableWidth);
- }
- }
private class DelayedSelectionUpdater implements Runnable {
File editFile;
DelayedSelectionUpdater() {
@@ -1361,23 +1456,23 @@
JTextField editCell = null;
/**
* @param index visual index of the file to be edited
*/
- @SuppressWarnings("deprecation")
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));
+ if (viewType.isTable) {
+ detailsTable.editCellAt(index, COLUMN_FILENAME);
+ } else {
+ 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());
@@ -1399,15 +1494,10 @@
} 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 {
@@ -1464,11 +1554,10 @@
cancelEdit();
}
protected Action newFolderAction;
- @SuppressWarnings("serial") // anonymous class inside
public Action getNewFolderAction() {
if (!readOnly && newFolderAction == null) {
newFolderAction = new AbstractAction(newFolderActionLabelText) {
private Action basicNewFolderAction;
@@ -1497,46 +1586,126 @@
};
}
return newFolderAction;
}
- @SuppressWarnings("serial") // JDK-implementation class
protected class FileRenderer extends DefaultListCellRenderer {
- public Component getListCellRendererComponent(JList<?> list, Object value,
+ // when the user selects an item then switches window, the item should
+ // appear with gray background
+ private static final String LAST_SELECTED_PROPERTY = "FilePane.FileRenderer.lastSelected";
+
+ // TODO az új ViewType enumot a többi lafban is használni kéne majd
+
+ public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected,
boolean cellHasFocus) {
+ assert viewType != null;
if (listViewWindowsStyle && !list.isFocusOwner()) {
isSelected = false;
}
- super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ if (listViewWindowsStyle) {
+ setOpaque(true);
+ setBackground(null);
+ } else {
+ super.getListCellRendererComponent(list, "", 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);
+ Boolean isTraversable = getFileChooser().getFileSystemView().isTraversable(file);
+ setIcon(new FileIcon(file, () -> {
+ Icon icon = null;
+ if (viewType.largeIcons) {
+ try {
+ icon = new ImageIcon(ShellFolder.getShellFolder(file).getIcon(true));
+ } catch (FileNotFoundException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ if (icon == null) {
+ icon = getFileChooser().getIcon(file);
+ }
+ return icon;
+ }, list, viewType.largeIcons ? 24 : 16, viewType.largeIcons ? 24 : 16));
+ if (isTraversable) {
+ setText(fileName + File.separator);
+ }
+
+ if (viewType.fileMetaLines) {
+ String sizeString = "";
+
+ if (!isTraversable) {
+ try {
+ sizeString += "<br>" + ShellFolder.
+ getShellFolder(file).length();
+ } catch (Exception ex) {
+ }
+ }
+
+ setText("<html>" + getText() + "<br><font color=gray>"
+ + getFileChooser().getTypeDescription(file)
+ + sizeString);
+ }
+
+ setAlignmentX(RIGHT_ALIGNMENT);
+ setHorizontalAlignment(viewType.horizAlign);
+
+ setHorizontalTextPosition(viewType.horizTextPos);
+ setVerticalTextPosition(viewType.vertTextPos);
+
+ setPreferredSize(viewType.prefSize);
+
+ if (listViewWindowsStyle) {
+ boolean isHovered = false;
+
+ {
+ Object propertyValue = list.getClientProperty(HOVER_CELL_PROPERTY);
+ if (propertyValue != null) {
+ int hover = (int) propertyValue;
+ if (hover == index) {
+ isHovered = true;
+ }
+ }
+ }
+
+ setBorder(new EmptyBorder(1, 1, 3, 1));
+ if (isSelected||editFile == file) {
+ setBackground(WindowsFileListUI.ITEM_SELECTED_COLOR);
+ if (isHovered) {
+ setBorder(new CompoundBorder(
+ WindowsFileListUI.ITEM_SELECTED_BORDER,
+ new EmptyBorder(0, 0, 2, 0)
+ ));
+ }
+ list.putClientProperty(LAST_SELECTED_PROPERTY, value);
+ } else if(list.getClientProperty(LAST_SELECTED_PROPERTY) == value) {
+ setBackground(WindowsFileListUI.ITEM_SELECTED_UNFOCUSED_COLOR);
+ }else if (isHovered) {
+ setBackground(WindowsFileListUI.ITEM_HOVERED_COLOR);
} else {
- if (getFileChooser().getFileSystemView().isTraversable(file)) {
- setText(fileName+File.separator);
+ setBackground(null);
}
}
return this;
}
}
-
- @SuppressWarnings("deprecation")
void setFileSelected() {
if (getFileChooser().isMultiSelectionEnabled() && !isDirectorySelected()) {
File[] files = getFileChooser().getSelectedFiles(); // Should be selected
- Object[] selectedObjects = list.getSelectedValues(); // Are actually selected
+ Object[] selectedObjects = getViewType().isTable?
+ IntStream.of(detailsTable.getSelectedRows()).
+ mapToObj(i->getModel().getElementAt(i)).
+ toArray():
+ list.getSelectedValues(); // Are actually selected
listSelectionModel.setValueIsAdjusting(true);
try {
int lead = listSelectionModel.getLeadSelectionIndex();
int anchor = listSelectionModel.getAnchorSelectionIndex();
@@ -1686,12 +1855,13 @@
/*
* 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);
+ if (viewType == null) {
+ setViewType(ViewType.LIST); // BasicFileChooserUI.Handler needs a list to work
+ setViewType(ViewType.DETAILS);
}
String s = e.getPropertyName();
if (s.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
doSelectedFileChanged(e);
@@ -1757,13 +1927,13 @@
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));
+ for (ViewType viewType : ViewType.values()) {
+ JRadioButtonMenuItem mi
+ = new JRadioButtonMenuItem(new ViewTypeAction(viewType));
viewButtonGroup.add(mi);
viewMenu.add(mi);
}
updateViewMenu();
}
@@ -1931,10 +2101,12 @@
}
}
public void mousePressed(MouseEvent evt) {
if (evt.getSource() instanceof JList) {
+ list.putClientProperty(FileRenderer.LAST_SELECTED_PROPERTY, null);
+
// Forward event to Basic
if (getDoubleClickListener() != null) {
getDoubleClickListener().mousePressed(evt);
}
}
@@ -1949,11 +2121,11 @@
}
}
private MouseListener getDoubleClickListener() {
// Lazy creation of Basic's listener
- if (doubleClickListener == null && list != null) {
+ if (doubleClickListener == null) {
doubleClickListener =
fileChooserUIAccessor.createDoubleClickListener(list);
}
return doubleClickListener;
}
@@ -2030,10 +2202,78 @@
return prop == null ? chooser.getFileSystemView().equals(FileSystemView.getFileSystemView())
: prop.booleanValue();
}
+ /**
+ * An Icon implementation which loads the real icon in background, so we
+ * don't need to block the EDT to load an icon of a file. After the icon
+ * is loaded, the table or list will be refreshed.
+ */
+ public static class FileIcon implements Icon {
+
+ private static final Executor BACKGROUND_LOADER_THREAD =
+ Executors.newSingleThreadExecutor(runnable->{
+ Thread result = new Thread(runnable, "File Icon Loader");
+ result.setDaemon(true);
+ return result;
+ });
+
+ private static final Map<File, Icon> CACHE = new ConcurrentHashMap<>();
+ private static final Map<Object, FileIcon> FILEICON_CACHE = new WeakHashMap<>();
+
+ private final File file;
+ private final Supplier<Icon> supplier;
+ private final Component component;
+ private final int width, height;
+
+ private volatile boolean loading;
+
+ public FileIcon(File file, Supplier<Icon> supplier, Component component,
+ int width, int height) {
+ this.file = file;
+ this.supplier = supplier;
+ this.component = component;
+ this.width = width;
+ this.height = height;
+ }
+
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ if (CACHE.containsKey(file) &&
+ CACHE.get(file).getIconWidth()==width&&
+ CACHE.get(file).getIconHeight() == height) {
+ CACHE.get(file).paintIcon(c, g, x, y);
+ } else if (!loading) {
+ loading = true;
+ BACKGROUND_LOADER_THREAD.execute(() -> {
+ Icon icon;
+ try {
+ icon = supplier.get();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ icon = null;
+ }
+ CACHE.put(file, icon);
+ loading = false;
+ EventQueue.invokeLater(component::repaint);
+ });
+ }
+ }
+
+ @Override
+ public int getIconWidth() {
+ return width;
+ }
+
+ @Override
+ public int getIconHeight() {
+ return height;
+ }
+
+ }
+
// This interface is used to access methods in the FileChooserUI
// that are not public.
public interface FileChooserUIAccessor {
public JFileChooser getFileChooser();
public BasicDirectoryModel getModel();
< prev index next >