/*
 * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package sun.swing.plaf.synth;

import javax.swing.plaf.synth.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.File;
import java.util.regex.*;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.filechooser.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.BasicFileChooserUI;

Synth FileChooserUI. Note: This class is abstract. It does not actually create the file chooser GUI.

Note that the classes in the com.sun.java.swing.plaf.synth package are not part of the core Java APIs. They are a part of Sun's JDK and JRE distributions. Although other licensees may choose to distribute these classes, developers cannot depend on their availability in non-Sun implementations. Additionally this API may change in incompatible ways between releases. While this class is public, it shoud be considered an implementation detail, and subject to change.

Author:Leif Samuelsson, Jeff Dinkins
/** * Synth FileChooserUI. * * Note: This class is abstract. It does not actually create the file chooser GUI. * <p> * Note that the classes in the com.sun.java.swing.plaf.synth * package are not * part of the core Java APIs. They are a part of Sun's JDK and JRE * distributions. Although other licensees may choose to distribute * these classes, developers cannot depend on their availability in * non-Sun implementations. Additionally this API may change in * incompatible ways between releases. While this class is public, it * shoud be considered an implementation detail, and subject to change. * * @author Leif Samuelsson * @author Jeff Dinkins */
public abstract class SynthFileChooserUI extends BasicFileChooserUI implements SynthUI { private JButton approveButton, cancelButton; private SynthStyle style; // Some generic FileChooser functions private Action fileNameCompletionAction = new FileNameCompletionAction(); private FileFilter actualFileFilter = null; private GlobFilter globFilter = null; public static ComponentUI createUI(JComponent c) { return new SynthFileChooserUIImpl((JFileChooser)c); } public SynthFileChooserUI(JFileChooser b) { super(b); } public SynthContext getContext(JComponent c) { return new SynthContext(c, Region.FILE_CHOOSER, style, getComponentState(c)); } protected SynthContext getContext(JComponent c, int state) { Region region = SynthLookAndFeel.getRegion(c); return new SynthContext(c, Region.FILE_CHOOSER, style, state); } private Region getRegion(JComponent c) { return SynthLookAndFeel.getRegion(c); } private int getComponentState(JComponent c) { if (c.isEnabled()) { if (c.isFocusOwner()) { return ENABLED | FOCUSED; } return ENABLED; } return DISABLED; } private void updateStyle(JComponent c) { SynthStyle newStyle = SynthLookAndFeel.getStyleFactory().getStyle(c, Region.FILE_CHOOSER); if (newStyle != style) { if (style != null) { style.uninstallDefaults(getContext(c, ENABLED)); } style = newStyle; SynthContext context = getContext(c, ENABLED); style.installDefaults(context); Border border = c.getBorder(); if (border == null || border instanceof UIResource) { c.setBorder(new UIBorder(style.getInsets(context, null))); } directoryIcon = style.getIcon(context, "FileView.directoryIcon"); fileIcon = style.getIcon(context, "FileView.fileIcon"); computerIcon = style.getIcon(context, "FileView.computerIcon"); hardDriveIcon = style.getIcon(context, "FileView.hardDriveIcon"); floppyDriveIcon = style.getIcon(context, "FileView.floppyDriveIcon"); newFolderIcon = style.getIcon(context, "FileChooser.newFolderIcon"); upFolderIcon = style.getIcon(context, "FileChooser.upFolderIcon"); homeFolderIcon = style.getIcon(context, "FileChooser.homeFolderIcon"); detailsViewIcon = style.getIcon(context, "FileChooser.detailsViewIcon"); listViewIcon = style.getIcon(context, "FileChooser.listViewIcon"); } } public void installUI(JComponent c) { super.installUI(c); SwingUtilities.replaceUIActionMap(c, createActionMap()); } public void installComponents(JFileChooser fc) { SynthContext context = getContext(fc, ENABLED); cancelButton = new JButton(cancelButtonText); cancelButton.setName("SynthFileChooser.cancelButton"); cancelButton.setIcon(context.getStyle().getIcon(context, "FileChooser.cancelIcon")); cancelButton.setMnemonic(cancelButtonMnemonic); cancelButton.setToolTipText(cancelButtonToolTipText); cancelButton.addActionListener(getCancelSelectionAction()); approveButton = new JButton(getApproveButtonText(fc)); approveButton.setName("SynthFileChooser.approveButton"); approveButton.setIcon(context.getStyle().getIcon(context, "FileChooser.okIcon")); approveButton.setMnemonic(getApproveButtonMnemonic(fc)); approveButton.setToolTipText(getApproveButtonToolTipText(fc)); approveButton.addActionListener(getApproveSelectionAction()); } public void uninstallComponents(JFileChooser fc) { fc.removeAll(); } protected void installListeners(JFileChooser fc) { super.installListeners(fc); getModel().addListDataListener(new ListDataListener() { public void contentsChanged(ListDataEvent e) { // Update the selection after JList has been updated new DelayedSelectionUpdater(); } public void intervalAdded(ListDataEvent e) { new DelayedSelectionUpdater(); } public void intervalRemoved(ListDataEvent e) { } }); } private class DelayedSelectionUpdater implements Runnable { DelayedSelectionUpdater() { SwingUtilities.invokeLater(this); } public void run() { updateFileNameCompletion(); } } protected abstract ActionMap createActionMap(); protected void installDefaults(JFileChooser fc) { super.installDefaults(fc); updateStyle(fc); } protected void uninstallDefaults(JFileChooser fc) { super.uninstallDefaults(fc); SynthContext context = getContext(getFileChooser(), ENABLED); style.uninstallDefaults(context); style = null; } protected void installIcons(JFileChooser fc) { // The icons are installed in updateStyle, not here } public void update(Graphics g, JComponent c) { SynthContext context = getContext(c); if (c.isOpaque()) { g.setColor(style.getColor(context, ColorType.BACKGROUND)); g.fillRect(0, 0, c.getWidth(), c.getHeight()); } style.getPainter(context).paintFileChooserBackground(context, g, 0, 0, c.getWidth(), c.getHeight()); paint(context, g); } public void paintBorder(SynthContext context, Graphics g, int x, int y, int w, int h) { } public void paint(Graphics g, JComponent c) { SynthContext context = getContext(c); paint(context, g); } protected void paint(SynthContext context, Graphics g) { } public abstract void setFileName(String fileName); public abstract String getFileName(); protected void doSelectedFileChanged(PropertyChangeEvent e) { } protected void doSelectedFilesChanged(PropertyChangeEvent e) { } protected void doDirectoryChanged(PropertyChangeEvent e) { } protected void doAccessoryChanged(PropertyChangeEvent e) { } protected void doFileSelectionModeChanged(PropertyChangeEvent e) { } protected void doMultiSelectionChanged(PropertyChangeEvent e) { if (!getFileChooser().isMultiSelectionEnabled()) { getFileChooser().setSelectedFiles(null); } } protected void doControlButtonsChanged(PropertyChangeEvent e) { if (getFileChooser().getControlButtonsAreShown()) { approveButton.setText(getApproveButtonText(getFileChooser())); approveButton.setToolTipText(getApproveButtonToolTipText(getFileChooser())); approveButton.setMnemonic(getApproveButtonMnemonic(getFileChooser())); } } protected void doAncestorChanged(PropertyChangeEvent e) { } public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { return new SynthFCPropertyChangeListener(); } private class SynthFCPropertyChangeListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if (prop.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) { doFileSelectionModeChanged(e); } else if (prop.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) { doSelectedFileChanged(e); } else if (prop.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) { doSelectedFilesChanged(e); } else if (prop.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) { doDirectoryChanged(e); } else if (prop == JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY) { doMultiSelectionChanged(e); } else if (prop == JFileChooser.ACCESSORY_CHANGED_PROPERTY) { doAccessoryChanged(e); } else if (prop == JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY || prop == JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY || prop == JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY || prop == JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY) { doControlButtonsChanged(e); } else if (prop.equals("componentOrientation")) { ComponentOrientation o = (ComponentOrientation)e.getNewValue(); JFileChooser cc = (JFileChooser)e.getSource(); if (o != (ComponentOrientation)e.getOldValue()) { cc.applyComponentOrientation(o); } } else if (prop.equals("ancestor")) { doAncestorChanged(e); } } }
Responds to a File Name completion request (e.g. Tab)
/** * Responds to a File Name completion request (e.g. Tab) */
@SuppressWarnings("serial") // JDK-implementation class private class FileNameCompletionAction extends AbstractAction { protected FileNameCompletionAction() { super("fileNameCompletion"); } public void actionPerformed(ActionEvent e) { JFileChooser chooser = getFileChooser(); String fileName = getFileName(); if (fileName != null) { // Remove whitespace from beginning and end of filename fileName = fileName.trim(); } resetGlobFilter(); if (fileName == null || fileName.isEmpty() || (chooser.isMultiSelectionEnabled() && fileName.startsWith("\""))) { return; } FileFilter currentFilter = chooser.getFileFilter(); if (globFilter == null) { globFilter = new GlobFilter(); } try { globFilter.setPattern(!isGlobPattern(fileName) ? fileName + "*" : fileName); if (!(currentFilter instanceof GlobFilter)) { actualFileFilter = currentFilter; } chooser.setFileFilter(null); chooser.setFileFilter(globFilter); fileNameCompletionString = fileName; } catch (PatternSyntaxException pse) { // Not a valid glob pattern. Abandon filter. } } } private String fileNameCompletionString; private void updateFileNameCompletion() { if (fileNameCompletionString != null) { if (fileNameCompletionString.equals(getFileName())) { File[] files = getModel().getFiles().toArray(new File[0]); String str = getCommonStartString(files); if (str != null && str.startsWith(fileNameCompletionString)) { setFileName(str); } fileNameCompletionString = null; } } } private String getCommonStartString(File[] files) { String str = null; String str2 = null; int i = 0; if (files.length == 0) { return null; } while (true) { for (int f = 0; f < files.length; f++) { String name = files[f].getName(); if (f == 0) { if (name.length() == i) { return str; } str2 = name.substring(0, i+1); } if (!name.startsWith(str2)) { return str; } } str = str2; i++; } } 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; } } private static boolean isGlobPattern(String fileName) { return ((File.separatorChar == '\\' && fileName.indexOf('*') >= 0) || (File.separatorChar == '/' && (fileName.indexOf('*') >= 0 || fileName.indexOf('?') >= 0 || fileName.indexOf('[') >= 0))); } /* A file filter which accepts file patterns containing * the special wildcard '*' on windows, plus '?', and '[ ]' on Unix. */ class GlobFilter extends FileFilter { Pattern pattern; String globPattern; public void setPattern(String globPattern) { char[] gPat = globPattern.toCharArray(); char[] rPat = new char[gPat.length * 2]; boolean isWin32 = (File.separatorChar == '\\'); boolean inBrackets = false; int j = 0; this.globPattern = globPattern; if (isWin32) { // On windows, a pattern ending with *.* is equal to ending with * int len = gPat.length; if (globPattern.endsWith("*.*")) { len -= 2; } for (int i = 0; i < len; i++) { if (gPat[i] == '*') { rPat[j++] = '.'; } rPat[j++] = gPat[i]; } } else { for (int i = 0; i < gPat.length; i++) { switch(gPat[i]) { case '*': if (!inBrackets) { rPat[j++] = '.'; } rPat[j++] = '*'; break; case '?': rPat[j++] = inBrackets ? '?' : '.'; break; case '[': inBrackets = true; rPat[j++] = gPat[i]; if (i < gPat.length - 1) { switch (gPat[i+1]) { case '!': case '^': rPat[j++] = '^'; i++; break; case ']': rPat[j++] = gPat[++i]; break; } } break; case ']': rPat[j++] = gPat[i]; inBrackets = false; break; case '\\': if (i == 0 && gPat.length > 1 && gPat[1] == '~') { rPat[j++] = gPat[++i]; } else { rPat[j++] = '\\'; if (i < gPat.length - 1 && "*?[]".indexOf(gPat[i+1]) >= 0) { rPat[j++] = gPat[++i]; } else { rPat[j++] = '\\'; } } break; default: //if ("+()|^$.{}<>".indexOf(gPat[i]) >= 0) { if (!Character.isLetterOrDigit(gPat[i])) { rPat[j++] = '\\'; } rPat[j++] = gPat[i]; break; } } } this.pattern = Pattern.compile(new String(rPat, 0, j), Pattern.CASE_INSENSITIVE); } public boolean accept(File f) { if (f == null) { return false; } if (f.isDirectory()) { return true; } return pattern.matcher(f.getName()).matches(); } public String getDescription() { return globPattern; } } // ******************************************************* // ************ FileChooser UI PLAF methods ************** // ******************************************************* // ***************************** // ***** Directory Actions ***** // ***************************** public Action getFileNameCompletionAction() { return fileNameCompletionAction; } protected JButton getApproveButton(JFileChooser fc) { return approveButton; } protected JButton getCancelButton(JFileChooser fc) { return cancelButton; } // Overload to do nothing. We don't have and icon cache. public void clearIconCache() { } // Copied as SynthBorder is package private in synth @SuppressWarnings("serial") // JDK-implementation clas private class UIBorder extends AbstractBorder implements UIResource { private Insets _insets; UIBorder(Insets insets) { if (insets != null) { _insets = new Insets(insets.top, insets.left, insets.bottom, insets.right); } else { _insets = null; } } public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { if (!(c instanceof JComponent)) { return; } JComponent jc = (JComponent)c; SynthContext context = getContext(jc); SynthStyle style = context.getStyle(); if (style != null) { style.getPainter(context).paintFileChooserBorder( context, g, x, y, width, height); } } public Insets getBorderInsets(Component c, Insets insets) { if (insets == null) { insets = new Insets(0, 0, 0, 0); } if (_insets != null) { insets.top = _insets.top; insets.bottom = _insets.bottom; insets.left = _insets.left; insets.right = _insets.right; } else { insets.top = insets.bottom = insets.right = insets.left = 0; } return insets; } public boolean isBorderOpaque() { return false; } } }