| /* |
| * Copyright (c) 2002, 2010, 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. |
| * <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) { |
| } |
| |
| abstract public void setFileName(String fileName); |
| abstract public 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) |
| */ |
| 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.equals("") || |
| (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 |
| 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; |
| } |
| } |
| } |