< 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 >