Path: blob/master/src/java.desktop/share/classes/sun/swing/plaf/synth/SynthFileChooserUI.java
41161 views
/*1* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package sun.swing.plaf.synth;2526import javax.swing.plaf.synth.*;27import java.awt.*;28import java.awt.event.*;29import java.beans.*;30import java.io.File;31import java.util.regex.*;3233import javax.swing.*;34import javax.swing.border.*;35import javax.swing.event.*;36import javax.swing.filechooser.*;37import javax.swing.plaf.*;38import javax.swing.plaf.basic.BasicFileChooserUI;3940/**41* Synth FileChooserUI.42*43* Note: This class is abstract. It does not actually create the file chooser GUI.44* <p>45* Note that the classes in the com.sun.java.swing.plaf.synth46* package are not47* part of the core Java APIs. They are a part of Sun's JDK and JRE48* distributions. Although other licensees may choose to distribute49* these classes, developers cannot depend on their availability in50* non-Sun implementations. Additionally this API may change in51* incompatible ways between releases. While this class is public, it52* shoud be considered an implementation detail, and subject to change.53*54* @author Leif Samuelsson55* @author Jeff Dinkins56*/57public abstract class SynthFileChooserUI extends BasicFileChooserUI implements58SynthUI {59private JButton approveButton, cancelButton;6061private SynthStyle style;6263// Some generic FileChooser functions64private Action fileNameCompletionAction = new FileNameCompletionAction();6566private FileFilter actualFileFilter = null;67private GlobFilter globFilter = null;6869public static ComponentUI createUI(JComponent c) {70return new SynthFileChooserUIImpl((JFileChooser)c);71}7273public SynthFileChooserUI(JFileChooser b) {74super(b);75}7677public SynthContext getContext(JComponent c) {78return new SynthContext(c, Region.FILE_CHOOSER, style,79getComponentState(c));80}8182protected SynthContext getContext(JComponent c, int state) {83Region region = SynthLookAndFeel.getRegion(c);84return new SynthContext(c, Region.FILE_CHOOSER, style, state);85}8687private Region getRegion(JComponent c) {88return SynthLookAndFeel.getRegion(c);89}9091private int getComponentState(JComponent c) {92if (c.isEnabled()) {93if (c.isFocusOwner()) {94return ENABLED | FOCUSED;95}96return ENABLED;97}98return DISABLED;99}100101private void updateStyle(JComponent c) {102SynthStyle newStyle = SynthLookAndFeel.getStyleFactory().getStyle(c,103Region.FILE_CHOOSER);104if (newStyle != style) {105if (style != null) {106style.uninstallDefaults(getContext(c, ENABLED));107}108style = newStyle;109SynthContext context = getContext(c, ENABLED);110style.installDefaults(context);111Border border = c.getBorder();112if (border == null || border instanceof UIResource) {113c.setBorder(new UIBorder(style.getInsets(context, null)));114}115116directoryIcon = style.getIcon(context, "FileView.directoryIcon");117fileIcon = style.getIcon(context, "FileView.fileIcon");118computerIcon = style.getIcon(context, "FileView.computerIcon");119hardDriveIcon = style.getIcon(context, "FileView.hardDriveIcon");120floppyDriveIcon = style.getIcon(context, "FileView.floppyDriveIcon");121122newFolderIcon = style.getIcon(context, "FileChooser.newFolderIcon");123upFolderIcon = style.getIcon(context, "FileChooser.upFolderIcon");124homeFolderIcon = style.getIcon(context, "FileChooser.homeFolderIcon");125detailsViewIcon = style.getIcon(context, "FileChooser.detailsViewIcon");126listViewIcon = style.getIcon(context, "FileChooser.listViewIcon");127}128}129130public void installUI(JComponent c) {131super.installUI(c);132SwingUtilities.replaceUIActionMap(c, createActionMap());133}134135public void installComponents(JFileChooser fc) {136SynthContext context = getContext(fc, ENABLED);137138cancelButton = new JButton(cancelButtonText);139cancelButton.setName("SynthFileChooser.cancelButton");140cancelButton.setIcon(context.getStyle().getIcon(context, "FileChooser.cancelIcon"));141cancelButton.setMnemonic(cancelButtonMnemonic);142cancelButton.setToolTipText(cancelButtonToolTipText);143cancelButton.addActionListener(getCancelSelectionAction());144145approveButton = new JButton(getApproveButtonText(fc));146approveButton.setName("SynthFileChooser.approveButton");147approveButton.setIcon(context.getStyle().getIcon(context, "FileChooser.okIcon"));148approveButton.setMnemonic(getApproveButtonMnemonic(fc));149approveButton.setToolTipText(getApproveButtonToolTipText(fc));150approveButton.addActionListener(getApproveSelectionAction());151152}153154public void uninstallComponents(JFileChooser fc) {155fc.removeAll();156}157158protected void installListeners(JFileChooser fc) {159super.installListeners(fc);160161getModel().addListDataListener(new ListDataListener() {162public void contentsChanged(ListDataEvent e) {163// Update the selection after JList has been updated164new DelayedSelectionUpdater();165}166public void intervalAdded(ListDataEvent e) {167new DelayedSelectionUpdater();168}169public void intervalRemoved(ListDataEvent e) {170}171});172173}174175private class DelayedSelectionUpdater implements Runnable {176DelayedSelectionUpdater() {177SwingUtilities.invokeLater(this);178}179180public void run() {181updateFileNameCompletion();182}183}184185protected abstract ActionMap createActionMap();186187188protected void installDefaults(JFileChooser fc) {189super.installDefaults(fc);190updateStyle(fc);191}192193protected void uninstallDefaults(JFileChooser fc) {194super.uninstallDefaults(fc);195196SynthContext context = getContext(getFileChooser(), ENABLED);197style.uninstallDefaults(context);198style = null;199}200201protected void installIcons(JFileChooser fc) {202// The icons are installed in updateStyle, not here203}204205public void update(Graphics g, JComponent c) {206SynthContext context = getContext(c);207208if (c.isOpaque()) {209g.setColor(style.getColor(context, ColorType.BACKGROUND));210g.fillRect(0, 0, c.getWidth(), c.getHeight());211}212213style.getPainter(context).paintFileChooserBackground(context,214g, 0, 0, c.getWidth(), c.getHeight());215paint(context, g);216}217218public void paintBorder(SynthContext context, Graphics g, int x, int y, int w, int h) {219}220221public void paint(Graphics g, JComponent c) {222SynthContext context = getContext(c);223224paint(context, g);225}226227protected void paint(SynthContext context, Graphics g) {228}229230public abstract void setFileName(String fileName);231public abstract String getFileName();232233protected void doSelectedFileChanged(PropertyChangeEvent e) {234}235236protected void doSelectedFilesChanged(PropertyChangeEvent e) {237}238239protected void doDirectoryChanged(PropertyChangeEvent e) {240}241242protected void doAccessoryChanged(PropertyChangeEvent e) {243}244245protected void doFileSelectionModeChanged(PropertyChangeEvent e) {246}247248protected void doMultiSelectionChanged(PropertyChangeEvent e) {249if (!getFileChooser().isMultiSelectionEnabled()) {250getFileChooser().setSelectedFiles(null);251}252}253254protected void doControlButtonsChanged(PropertyChangeEvent e) {255if (getFileChooser().getControlButtonsAreShown()) {256approveButton.setText(getApproveButtonText(getFileChooser()));257approveButton.setToolTipText(getApproveButtonToolTipText(getFileChooser()));258approveButton.setMnemonic(getApproveButtonMnemonic(getFileChooser()));259}260}261262protected void doAncestorChanged(PropertyChangeEvent e) {263}264265public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) {266return new SynthFCPropertyChangeListener();267}268269private class SynthFCPropertyChangeListener implements PropertyChangeListener {270public void propertyChange(PropertyChangeEvent e) {271String prop = e.getPropertyName();272if (prop.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {273doFileSelectionModeChanged(e);274} else if (prop.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {275doSelectedFileChanged(e);276} else if (prop.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) {277doSelectedFilesChanged(e);278} else if (prop.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {279doDirectoryChanged(e);280} else if (prop == JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY) {281doMultiSelectionChanged(e);282} else if (prop == JFileChooser.ACCESSORY_CHANGED_PROPERTY) {283doAccessoryChanged(e);284} else if (prop == JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY ||285prop == JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY ||286prop == JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY ||287prop == JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY) {288doControlButtonsChanged(e);289} else if (prop.equals("componentOrientation")) {290ComponentOrientation o = (ComponentOrientation)e.getNewValue();291JFileChooser cc = (JFileChooser)e.getSource();292if (o != (ComponentOrientation)e.getOldValue()) {293cc.applyComponentOrientation(o);294}295} else if (prop.equals("ancestor")) {296doAncestorChanged(e);297}298}299}300301302/**303* Responds to a File Name completion request (e.g. Tab)304*/305@SuppressWarnings("serial") // JDK-implementation class306private class FileNameCompletionAction extends AbstractAction {307protected FileNameCompletionAction() {308super("fileNameCompletion");309}310311public void actionPerformed(ActionEvent e) {312JFileChooser chooser = getFileChooser();313314String fileName = getFileName();315316if (fileName != null) {317// Remove whitespace from beginning and end of filename318fileName = fileName.trim();319}320321resetGlobFilter();322323if (fileName == null || fileName.isEmpty() ||324(chooser.isMultiSelectionEnabled() && fileName.startsWith("\""))) {325return;326}327328FileFilter currentFilter = chooser.getFileFilter();329if (globFilter == null) {330globFilter = new GlobFilter();331}332try {333globFilter.setPattern(!isGlobPattern(fileName) ? fileName + "*" : fileName);334if (!(currentFilter instanceof GlobFilter)) {335actualFileFilter = currentFilter;336}337chooser.setFileFilter(null);338chooser.setFileFilter(globFilter);339fileNameCompletionString = fileName;340} catch (PatternSyntaxException pse) {341// Not a valid glob pattern. Abandon filter.342}343}344}345346private String fileNameCompletionString;347348private void updateFileNameCompletion() {349if (fileNameCompletionString != null) {350if (fileNameCompletionString.equals(getFileName())) {351File[] files = getModel().getFiles().toArray(new File[0]);352String str = getCommonStartString(files);353if (str != null && str.startsWith(fileNameCompletionString)) {354setFileName(str);355}356fileNameCompletionString = null;357}358}359}360361private String getCommonStartString(File[] files) {362String str = null;363String str2 = null;364int i = 0;365if (files.length == 0) {366return null;367}368while (true) {369for (int f = 0; f < files.length; f++) {370String name = files[f].getName();371if (f == 0) {372if (name.length() == i) {373return str;374}375str2 = name.substring(0, i+1);376}377if (!name.startsWith(str2)) {378return str;379}380}381str = str2;382i++;383}384}385386private void resetGlobFilter() {387if (actualFileFilter != null) {388JFileChooser chooser = getFileChooser();389FileFilter currentFilter = chooser.getFileFilter();390if (currentFilter != null && currentFilter.equals(globFilter)) {391chooser.setFileFilter(actualFileFilter);392chooser.removeChoosableFileFilter(globFilter);393}394actualFileFilter = null;395}396}397398private static boolean isGlobPattern(String fileName) {399return ((File.separatorChar == '\\' && fileName.indexOf('*') >= 0)400|| (File.separatorChar == '/' && (fileName.indexOf('*') >= 0401|| fileName.indexOf('?') >= 0402|| fileName.indexOf('[') >= 0)));403}404405406/* A file filter which accepts file patterns containing407* the special wildcard '*' on windows, plus '?', and '[ ]' on Unix.408*/409class GlobFilter extends FileFilter {410Pattern pattern;411String globPattern;412413public void setPattern(String globPattern) {414char[] gPat = globPattern.toCharArray();415char[] rPat = new char[gPat.length * 2];416boolean isWin32 = (File.separatorChar == '\\');417boolean inBrackets = false;418int j = 0;419420this.globPattern = globPattern;421422if (isWin32) {423// On windows, a pattern ending with *.* is equal to ending with *424int len = gPat.length;425if (globPattern.endsWith("*.*")) {426len -= 2;427}428for (int i = 0; i < len; i++) {429if (gPat[i] == '*') {430rPat[j++] = '.';431}432rPat[j++] = gPat[i];433}434} else {435for (int i = 0; i < gPat.length; i++) {436switch(gPat[i]) {437case '*':438if (!inBrackets) {439rPat[j++] = '.';440}441rPat[j++] = '*';442break;443444case '?':445rPat[j++] = inBrackets ? '?' : '.';446break;447448case '[':449inBrackets = true;450rPat[j++] = gPat[i];451452if (i < gPat.length - 1) {453switch (gPat[i+1]) {454case '!':455case '^':456rPat[j++] = '^';457i++;458break;459460case ']':461rPat[j++] = gPat[++i];462break;463}464}465break;466467case ']':468rPat[j++] = gPat[i];469inBrackets = false;470break;471472case '\\':473if (i == 0 && gPat.length > 1 && gPat[1] == '~') {474rPat[j++] = gPat[++i];475} else {476rPat[j++] = '\\';477if (i < gPat.length - 1 && "*?[]".indexOf(gPat[i+1]) >= 0) {478rPat[j++] = gPat[++i];479} else {480rPat[j++] = '\\';481}482}483break;484485default:486//if ("+()|^$.{}<>".indexOf(gPat[i]) >= 0) {487if (!Character.isLetterOrDigit(gPat[i])) {488rPat[j++] = '\\';489}490rPat[j++] = gPat[i];491break;492}493}494}495this.pattern = Pattern.compile(new String(rPat, 0, j), Pattern.CASE_INSENSITIVE);496}497498public boolean accept(File f) {499if (f == null) {500return false;501}502if (f.isDirectory()) {503return true;504}505return pattern.matcher(f.getName()).matches();506}507508public String getDescription() {509return globPattern;510}511}512513514// *******************************************************515// ************ FileChooser UI PLAF methods **************516// *******************************************************517518519// *****************************520// ***** Directory Actions *****521// *****************************522523public Action getFileNameCompletionAction() {524return fileNameCompletionAction;525}526527528protected JButton getApproveButton(JFileChooser fc) {529return approveButton;530}531532protected JButton getCancelButton(JFileChooser fc) {533return cancelButton;534}535536537// Overload to do nothing. We don't have and icon cache.538public void clearIconCache() { }539540// Copied as SynthBorder is package private in synth541@SuppressWarnings("serial") // JDK-implementation clas542private class UIBorder extends AbstractBorder implements UIResource {543private Insets _insets;544UIBorder(Insets insets) {545if (insets != null) {546_insets = new Insets(insets.top, insets.left, insets.bottom,547insets.right);548}549else {550_insets = null;551}552}553554public void paintBorder(Component c, Graphics g, int x, int y,555int width, int height) {556if (!(c instanceof JComponent)) {557return;558}559JComponent jc = (JComponent)c;560SynthContext context = getContext(jc);561SynthStyle style = context.getStyle();562if (style != null) {563style.getPainter(context).paintFileChooserBorder(564context, g, x, y, width, height);565}566}567568public Insets getBorderInsets(Component c, Insets insets) {569if (insets == null) {570insets = new Insets(0, 0, 0, 0);571}572if (_insets != null) {573insets.top = _insets.top;574insets.bottom = _insets.bottom;575insets.left = _insets.left;576insets.right = _insets.right;577}578else {579insets.top = insets.bottom = insets.right = insets.left = 0;580}581return insets;582}583public boolean isBorderOpaque() {584return false;585}586}587}588589590