package org.nbstudio.cachefilechooser; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Insets; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Box; import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileSystemView; import javax.swing.plaf.metal.MetalFileChooserUI; import org.nbstudio.cachefilesystem.CacheFileSystemView; import org.nbstudio.cachefilesystem.CacheRootFile; import sun.awt.shell.ShellFolder; import sun.swing.FilePane; import sun.swing.SwingUtilities2; /** * * @author daimor */ public class CacheFileChooserUI extends MetalFileChooserUI { private Handler handler; private static final Insets shrinkwrap = new Insets(0, 0, 0, 0); private static final Dimension hstrut5 = new Dimension(5, 1); private boolean usesSingleFilePane; private JButton macFilesButton; private JButton intFilesButton; private JButton basFilesButton; private JButton clsFilesButton; private JButton cspFilesButton; private JButton mvbFilesButton; private ImageIcon macFilesIcon; private ImageIcon clsFilesIcon; private ImageIcon intFilesIcon; private ImageIcon basFilesIcon; private ImageIcon cspFilesIcon; private ImageIcon mvbFilesIcon; private String readonlyFlagText = null; private String showsystemFlagText = null; private String showgeneratedFlagText = null; private boolean readonlyFlag = false; private Action approveSelectionAction = new ApproveSelectionAction(); private CacheFileChooser fileChooser; public CacheFileChooserUI(CacheFileChooser fileChooser) { super(fileChooser); this.fileChooser = fileChooser; } @Override public CacheFileChooser getFileChooser() { return fileChooser; } @Override protected void installIcons(JFileChooser fc) { super.installIcons(fc); macFilesIcon = new ImageIcon(getClass().getResource("/resources/macFilesIcon.gif")); intFilesIcon = new ImageIcon(getClass().getResource("/resources/intFilesIcon.gif")); basFilesIcon = new ImageIcon(getClass().getResource("/resources/basFilesIcon.gif")); clsFilesIcon = new ImageIcon(getClass().getResource("/resources/clsFilesIcon.gif")); cspFilesIcon = new ImageIcon(getClass().getResource("/resources/cspFilesIcon.gif")); mvbFilesIcon = new ImageIcon(getClass().getResource("/resources/mvbFilesIcon.gif")); } @Override protected void installStrings(JFileChooser fc) { super.installStrings(fc); readonlyFlagText = "Open as read-only"; showsystemFlagText = "Include System Items"; showgeneratedFlagText = "Show Generated"; } @Override public void installComponents(final JFileChooser fileChooser) { super.installComponents(fileChooser); CacheFileSystemView fsv = (CacheFileSystemView) fileChooser.getFileSystemView(); JComponent south = null; JComponent topButtonPanel = null; BorderLayout layout = (BorderLayout) fileChooser.getLayout(); for (Component child : fileChooser.getComponents()) { if (BorderLayout.SOUTH == layout.getConstraints(child)) { south = (JComponent) child; } // search btn's panel if (BorderLayout.NORTH == layout.getConstraints(child)) { JComponent topPanel = (JComponent) child; BorderLayout topPanelLayout = ((BorderLayout) topPanel.getLayout()); for (Component childTP : topPanel.getComponents()) { if (BorderLayout.AFTER_LINE_ENDS == topPanelLayout.getConstraints(childTP)) { topButtonPanel = (JComponent) childTP; } } } } if (south != null) { JPanel flagsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); JCheckBox readonlyFlag = new JCheckBox(readonlyFlagText); readonlyFlag.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { setReadOnlyFlag(e.getStateChange() == ItemEvent.SELECTED); } }); flagsPanel.add(readonlyFlag); JCheckBox showsystemFlag = new JCheckBox(showsystemFlagText, fsv.getShowSystemFiles()); showsystemFlag.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { boolean showsystemFiles = (e.getStateChange() == ItemEvent.SELECTED); CacheFileSystemView fsv = (CacheFileSystemView) fileChooser.getFileSystemView(); fsv.setShowSystemFiles(showsystemFiles); rescanCurrentDirectory(fileChooser); } }); flagsPanel.add(showsystemFlag); JCheckBox showgeneratedFlag = new JCheckBox(showgeneratedFlagText, fsv.getShowGeneratedFiles()); showgeneratedFlag.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { boolean showGeneratedFiles = (e.getStateChange() == ItemEvent.SELECTED); CacheFileSystemView fsv = (CacheFileSystemView) fileChooser.getFileSystemView(); fsv.setShowGeneratedFiles(showGeneratedFiles); rescanCurrentDirectory(fileChooser); } }); flagsPanel.add(showgeneratedFlag); south.add(flagsPanel, BorderLayout.NORTH); /// not supported yet readonlyFlag.setEnabled(false); } if (topButtonPanel != null) { topButtonPanel.add(Box.createRigidArea(hstrut5)); ButtonGroup filterButtonGroup = new ButtonGroup(); macFilesButton = new JButton(macFilesIcon); macFilesButton.setMargin(shrinkwrap); macFilesButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); macFilesButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); macFilesButton.addActionListener(new filterBtnAction(1)); topButtonPanel.add(macFilesButton); filterButtonGroup.add(macFilesButton); intFilesButton = new JButton(intFilesIcon); intFilesButton.setMargin(shrinkwrap); intFilesButton.addActionListener(new filterBtnAction(2)); topButtonPanel.add(intFilesButton); filterButtonGroup.add(intFilesButton); basFilesButton = new JButton(basFilesIcon); basFilesButton.setMargin(shrinkwrap); basFilesButton.addActionListener(new filterBtnAction(3)); topButtonPanel.add(basFilesButton); filterButtonGroup.add(basFilesButton); clsFilesButton = new JButton(clsFilesIcon); clsFilesButton.setMargin(shrinkwrap); clsFilesButton.addActionListener(new filterBtnAction(4)); topButtonPanel.add(clsFilesButton); filterButtonGroup.add(clsFilesButton); cspFilesButton = new JButton(cspFilesIcon); cspFilesButton.setMargin(shrinkwrap); cspFilesButton.addActionListener(new filterBtnAction(5)); topButtonPanel.add(cspFilesButton); filterButtonGroup.add(cspFilesButton); mvbFilesButton = new JButton(mvbFilesIcon); mvbFilesButton.setMargin(shrinkwrap); mvbFilesButton.addActionListener(new filterBtnAction(6)); topButtonPanel.add(mvbFilesButton); filterButtonGroup.add(mvbFilesButton); // To-Do: not supported yet cspFilesButton.setEnabled(false); mvbFilesButton.setEnabled(false); } } private Handler getHandler() { if (handler == null) { handler = new Handler(); } return handler; } @Override protected MouseListener createDoubleClickListener(JFileChooser fc, JList list) { return new Handler(list); } private class Handler implements MouseListener, ListSelectionListener { JList list; Handler() { } Handler(JList list) { this.list = list; } @Override public void mouseClicked(MouseEvent evt) { // Note: we can't depend on evt.getSource() because of backward // compatability if (list != null && SwingUtilities.isLeftMouseButton(evt) && (evt.getClickCount() % 2 == 0)) { int index = SwingUtilities2.loc2IndexFileList(list, evt.getPoint()); if (index >= 0) { CacheRootFile f = (CacheRootFile) list.getModel().getElementAt(index); // try { // // Strip trailing ".." // f = ShellFolder.getNormalizedFile(f); // } catch (IOException ex) { // // That's ok, we'll use f as is // } if (f.getName().toLowerCase().endsWith(".prj")) { getFileChooser().setSelectedFile(f); getFileChooser().approveSelection(); } else if (getFileChooser().isTraversable(f)) { list.clearSelection(); changeDirectory(f); } else { getFileChooser().approveSelection(); } } } } @Override public void mouseEntered(MouseEvent evt) { if (list != null) { TransferHandler th1 = getFileChooser().getTransferHandler(); TransferHandler th2 = list.getTransferHandler(); if (th1 != th2) { list.setTransferHandler(th1); } if (getFileChooser().getDragEnabled() != list.getDragEnabled()) { list.setDragEnabled(getFileChooser().getDragEnabled()); } } } @Override public void mouseExited(MouseEvent evt) { } @Override public void mousePressed(MouseEvent evt) { } @Override public void mouseReleased(MouseEvent evt) { } @Override public void valueChanged(ListSelectionEvent evt) { if (!evt.getValueIsAdjusting()) { JFileChooser chooser = getFileChooser(); FileSystemView fsv = chooser.getFileSystemView(); JList list = (JList) evt.getSource(); int fsm = chooser.getFileSelectionMode(); boolean useSetDirectory = usesSingleFilePane && (fsm == JFileChooser.FILES_ONLY); if (chooser.isMultiSelectionEnabled()) { File[] files = null; Object[] objects = list.getSelectedValues(); if (objects != null) { if (objects.length == 1 && ((CacheRootFile) objects[0]).isDirectory() && chooser.isTraversable(((CacheRootFile) objects[0])) && (useSetDirectory || !fsv.isFileSystem(((CacheRootFile) objects[0])))) { setDirectorySelected(true); setDirectory(((CacheRootFile) objects[0])); } else { ArrayList<CacheRootFile> fList = new ArrayList<>(objects.length); for (Object object : objects) { CacheRootFile f = (CacheRootFile) object; boolean isDir = f.isDirectory(); if ((chooser.isFileSelectionEnabled() && !isDir) || (chooser.isDirectorySelectionEnabled() && fsv.isFileSystem(f) && isDir)) { fList.add(f); } } if (fList.size() > 0) { files = fList.toArray(new File[fList.size()]); } setDirectorySelected(false); } } chooser.setSelectedFiles(files); } else { CacheRootFile file = (CacheRootFile) list.getSelectedValue(); if (file != null && file.isDirectory() && chooser.isTraversable(file) && (useSetDirectory || !fsv.isFileSystem(file))) { setDirectorySelected(true); setDirectory(file); if (usesSingleFilePane) { chooser.setSelectedFile(null); } } else { setDirectorySelected(false); if (file != null) { chooser.setSelectedFile(file); } } } } } } @Override protected void installDefaults(JFileChooser fc) { super.installDefaults(fc); usesSingleFilePane = UIManager.getBoolean("FileChooser.usesSingleFilePane"); } private void changeDirectory(File dir) { JFileChooser fc = getFileChooser(); // Traverse shortcuts on Windows if (null != dir && FilePane.usesShellFolder(fc)) { try { ShellFolder shellFolder = ShellFolder.getShellFolder(dir); if (shellFolder.isLink()) { File linkedTo = shellFolder.getLinkLocation(); // If linkedTo is null we try to use dir if (linkedTo != null) { if (fc.isTraversable(linkedTo)) { dir = linkedTo; } else { return; } } else { dir = shellFolder; } } } catch (FileNotFoundException ex) { return; } } fc.setCurrentDirectory(dir); if (fc.getFileSelectionMode() == JFileChooser.FILES_AND_DIRECTORIES && fc.getFileSystemView().isFileSystem(dir)) { setFileName(dir.getAbsolutePath()); } } protected class filterBtnAction extends AbstractAction { private final int type; private final String extFile; private final CacheFileNameExtensionFilter fileFilter; protected filterBtnAction(int type) { super("filterBtnAction"); this.type = type; switch (type) { case 1: this.extFile = "mac"; break; case 2: this.extFile = "int"; break; case 3: this.extFile = "bas"; break; case 4: this.extFile = "cls"; break; case 5: this.extFile = "csp"; break; case 6: this.extFile = "mvb"; break; default: this.extFile = null; break; } FileFilter filter = null; if (extFile != null) { CacheFileChooser fileChooser = (CacheFileChooser) getFileChooser(); FileFilter[] filters = fileChooser.getChoosableFileFilters(); for (FileFilter fileFilter : filters) { if (fileFilter instanceof CacheFileNameExtensionFilter) { String[] extensions = ((CacheFileNameExtensionFilter) fileFilter).getExtensions(); if (Arrays.asList(extensions).contains(extFile)) { filter = fileFilter; } } } } if (filter != null) { fileFilter = (CacheFileNameExtensionFilter) filter; } else { fileFilter = null; } } @Override public void actionPerformed(ActionEvent e) { CacheFileChooser fileChooser = (CacheFileChooser) getFileChooser(); if (fileFilter != null) { fileChooser.setFileFilter(fileFilter); } } } public void setReadOnlyFlag(boolean readonlyFlag) { this.readonlyFlag = readonlyFlag; } public boolean getReadOnlyFlag() { return readonlyFlag; } @Override public Action getApproveSelectionAction() { return approveSelectionAction; } /** * Responds to an Open or Save request */ protected class ApproveSelectionAction extends AbstractAction { protected ApproveSelectionAction() { super(FilePane.ACTION_APPROVE_SELECTION); } @Override public void actionPerformed(ActionEvent e) { CacheFileChooser chooser = getFileChooser(); if (isDirectorySelected()) { File dir = getDirectory(); if (dir != null) { if (dir.getName().matches(".*\\.prj$")) { chooser.setSelectedFile(dir); chooser.approveSelection(); } else { try { // Strip trailing ".." dir = ShellFolder.getNormalizedFile(dir); } catch (IOException ex) { // Ok, use f as is } changeDirectory(dir); } return; } } String filename = getFileName(); CacheFileSystemView fs = (CacheFileSystemView) chooser.getFileSystemView(); CacheRootFile dir = (CacheRootFile) chooser.getCurrentDirectory(); if (filename != null) { // Remove whitespaces from end of filename int i = filename.length() - 1; while (i >= 0 && filename.charAt(i) <= ' ') { i--; } filename = filename.substring(0, i + 1); } if (filename == null || filename.length() == 0) { // no file selected, multiple selection off, therefore cancel the approve action resetGlobFilter(); return; } CacheRootFile selectedFile = null; CacheRootFile[] selectedFiles = null; // Unix: Resolve '~' to user's home directory if (File.separatorChar == '/') { if (filename.startsWith("~/")) { filename = System.getProperty("user.home") + filename.substring(1); } else if (filename.equals("~")) { filename = System.getProperty("user.home"); } } if (chooser.isMultiSelectionEnabled() && filename.length() > 1 && filename.charAt(0) == '"' && filename.charAt(filename.length() - 1) == '"') { List<CacheRootFile> fList = new ArrayList<>(); String[] files = filename.substring(1, filename.length() - 1).split("\" \""); // Optimize searching files by names in "children" array Arrays.sort(files); File[] children = null; int childIndex = 0; for (String str : files) { File file = fs.createFileObject(str); if (!file.isAbsolute()) { if (children == null) { children = fs.getFiles(dir, false); Arrays.sort(children); } for (int k = 0; k < children.length; k++) { int l = (childIndex + k) % children.length; if (children[l].getName().equals(str)) { file = children[l]; childIndex = l + 1; break; } } } fList.add((CacheRootFile) file); } if (!fList.isEmpty()) { selectedFiles = fList.toArray(new CacheRootFile[fList.size()]); } resetGlobFilter(); } else { selectedFile = fs.createFileObject(dir, filename); if (!selectedFile.isAbsolute()) { selectedFile = (CacheRootFile) fs.getChild(dir, filename); } // check for wildcard pattern FileFilter currentFilter = chooser.getFileFilter(); // if (!selectedFile.exists() && isGlobPattern(filename)) { // changeDirectory(selectedFile.getParentFile()); // if (globFilter == null) { // globFilter = new BasicFileChooserUI.GlobFilter(); // } // try { // globFilter.setPattern(selectedFile.getName()); // if (!(currentFilter instanceof BasicFileChooserUI.GlobFilter)) { // actualFileFilter = currentFilter; // } // chooser.setFileFilter(null); // chooser.setFileFilter(globFilter); // return; // } catch (PatternSyntaxException pse) { // // Not a valid glob pattern. Abandon filter. // } // } resetGlobFilter(); // Check for directory change action boolean isDir = (selectedFile != null && selectedFile.isDirectory()); boolean isTrav = (selectedFile != null && chooser.isTraversable(selectedFile)); boolean isDirSelEnabled = chooser.isDirectorySelectionEnabled(); boolean isFileSelEnabled = chooser.isFileSelectionEnabled(); boolean isCtrl = (e != null && (e.getModifiers() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0); if (isDir && isTrav && (isCtrl || !isDirSelEnabled)) { changeDirectory(selectedFile); return; } else if ((isDir || !isFileSelEnabled) && (!isDir || !isDirSelEnabled) && (!isDirSelEnabled || selectedFile.exists())) { selectedFile = null; } } if (selectedFiles != null || selectedFile != null) { if (selectedFiles != null || chooser.isMultiSelectionEnabled()) { if (selectedFiles == null) { selectedFiles = new CacheRootFile[]{selectedFile}; } chooser.setSelectedFiles(selectedFiles); // Do it again. This is a fix for bug 4949273 to force the // selected value in case the ListSelectionModel clears it // for non-existing file names. chooser.setSelectedFiles(selectedFiles); } else { chooser.setSelectedFile(selectedFile); } chooser.approveSelection(); } else { if (chooser.isMultiSelectionEnabled()) { chooser.setSelectedFiles(null); } else { chooser.setSelectedFile(null); } chooser.cancelSelection(); } } } private void resetGlobFilter() { // if (actualFileFilter != null) { // JFileChooser chooser = getFileChooser(); // FileFilter currentFilter = chooser.getFileFilter(); // if (currentFilter != null && currentFilter.equals(globFilter)) { // chooser.setFileFilter(actualFileFilter); // chooser.removeChoosableFileFilter(globFilter); // } // actualFileFilter = null; // } } }