--- old/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java 2018-07-20 11:46:39.280292200 +0530 +++ new/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java 2018-07-20 11:46:37.797280200 +0530 @@ -45,6 +45,14 @@ import jdk.internal.ref.CleanerFactory; import sun.awt.shell.ShellFolder; +import java.awt.image.BufferedImage; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.RenderingHints; +import java.awt.Transparency; + /** * FileSystemView is JFileChooser's gateway to the * file system. Since the JDK1.1 File API doesn't allow @@ -256,6 +264,146 @@ } /** + * Scaled icon for a file, directory, or folder as it would be displayed in + * a system file browser. Example from Windows: the "M:\" directory + * displays a CD-ROM icon. + * + * The default implementation gets information from the ShellFolder class. + * + * @param f a File object + * @param width width of the icon in pixels to be scaled + * @param height height of the icon in pixels to be scaled + * @return an icon as it would be displayed by a native file chooser + * @exception RuntimeException if the icon failes to be scaled or + * @exception IllegalArgumentException if the icon scaling is out of range + * @see JFileChooser#getIcon + * @since 12 + */ + public Icon getSystemIcon(File f, int width, int height) { + if (f == null) { + return null; + } + + if((width > 256 || width < 1) || (height > 256 || height < 1)) { + System.err.println("Warning: Icon scaling may be distorted"); + throw new IllegalArgumentException("unexpected icon scaling size"); + } + + ShellFolder sf; + try { + sf = getShellFolder(f); + } catch (FileNotFoundException e) { + return null; + } + + int size; + if(width > height) { + size = width; + } else { + size = height; + } + + Image img = sf.getIcon(size); + + // scale the icon in case the width/height does not match the requested + if (img != null) { + if((img.getWidth(null) != width) || (img.getHeight(null) != height)) { + Image scaledImg = scaleIconImage(img, width, height); + + // set the scaled icon + if(scaledImg != null) { + img = scaledImg; + } else { + throw new RuntimeException("unable to scale the icon"); + } + } + } else { + Icon icon = UIManager.getIcon(f.isDirectory() ? "FileView.directoryIcon" : "FileView.fileIcon"); + + if((icon != null) && ((icon.getIconWidth() != width) || (icon.getIconHeight() != height))) { + Image scaledImg = scaleIcon(icon, width, height); + + // set the scaled icon + if(scaledImg != null) { + img = scaledImg; + } else { + throw new RuntimeException("unable to scale the icon"); + } + } + } + + if (img != null) { + return new ImageIcon(img, sf.getFolderType()); + } + + return null; + } + + private static Image scaleIconImage(Image icon, int scaledW, int scaledH) { + if (icon == null) { + return null; + } + + int w = icon.getWidth(null); + int h = icon.getHeight(null); + + GraphicsEnvironment ge = + GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gd.getDefaultConfiguration(); + + // convert icon into image + BufferedImage iconImage = gc.createCompatibleImage(w, h, + Transparency.TRANSLUCENT); + Graphics2D g = iconImage.createGraphics(); + g.drawImage(icon, 0, 0, w, h, null); + g.dispose(); + + // and scale it nicely + BufferedImage scaledImage = gc.createCompatibleImage(scaledW, scaledH, + Transparency.TRANSLUCENT); + g = scaledImage.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(iconImage, 0, 0, scaledW, scaledH, null); + g.dispose(); + + return (Image)scaledImage; + } + + private static Image scaleIcon(Icon icon, int scaledW, int scaledH) { + if (icon == null) { + return null; + } + + int w = icon.getIconWidth(); + int h = icon.getIconHeight(); + + GraphicsEnvironment ge = + GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gd.getDefaultConfiguration(); + + // convert icon into image + BufferedImage iconImage = gc.createCompatibleImage(w, h, + Transparency.TRANSLUCENT); + Graphics2D g = iconImage.createGraphics(); + icon.paintIcon(null, g, 0, 0); + g.dispose(); + + // and scale it nicely + BufferedImage scaledImage = gc.createCompatibleImage(scaledW, scaledH, + Transparency.TRANSLUCENT); + g = scaledImage.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(iconImage, 0, 0, scaledW, scaledH, null); + g.dispose(); + + return (Image)scaledImage; + } + + /** * On Windows, a file can appear in multiple folders, other than its * parent directory in the filesystem. Folder could for example be the * "Desktop" folder which is not the same as file.getParentFile(). --- old/src/java.desktop/share/classes/sun/awt/shell/ShellFolder.java 2018-07-20 11:46:48.217005100 +0530 +++ new/src/java.desktop/share/classes/sun/awt/shell/ShellFolder.java 2018-07-20 11:46:46.757003400 +0530 @@ -199,6 +199,13 @@ return null; } + /** + * @param size size of the icon > 0 + * @return The icon used to display this shell folder + */ + public Image getIcon(int size) { + return null; + } // Static --- old/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp 2018-07-20 11:46:56.667446600 +0530 +++ new/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp 2018-07-20 11:46:55.277433500 +0530 @@ -920,7 +920,7 @@ */ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_extractIcon (JNIEnv* env, jclass cls, jlong pIShellFolderL, jlong relativePIDL, - jboolean getLargeIcon, jboolean getDefaultIcon) + jint size, jboolean getDefaultIcon) { IShellFolder* pIShellFolder = (IShellFolder*)pIShellFolderL; LPITEMIDLIST pidl = (LPITEMIDLIST)relativePIDL; @@ -941,16 +941,7 @@ UINT uFlags = getDefaultIcon ? GIL_DEFAULTICON : GIL_FORSHELL | GIL_ASYNC; hres = pIcon->GetIconLocation(uFlags, szBuf, MAX_PATH, &index, &flags); if (SUCCEEDED(hres)) { - HICON hIconLarge; - hres = pIcon->Extract(szBuf, index, &hIconLarge, &hIcon, (16 << 16) + 32); - if (SUCCEEDED(hres)) { - if (getLargeIcon) { - fn_DestroyIcon((HICON)hIcon); - hIcon = hIconLarge; - } else { - fn_DestroyIcon((HICON)hIconLarge); - } - } + hres = pIcon->Extract(szBuf, index, &hIcon, 0, size); } else if (hres == E_PENDING) { pIcon->Release(); return E_PENDING; --- old/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java 2018-07-20 11:47:05.266882400 +0530 +++ new/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java 2018-07-20 11:47:03.843005600 +0530 @@ -977,7 +977,7 @@ // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details private static native long extractIcon(long parentIShellFolder, long relativePIDL, - boolean getLargeIcon, boolean getDefaultIcon); + int size, boolean getDefaultIcon); // Returns an icon from the Windows system icon list in the form of an HICON private static native long getSystemIcon(int iconID); @@ -1004,20 +1004,19 @@ return pIShellIcon; } - private static Image makeIcon(long hIcon, boolean getLargeIcon) { + private static Image makeIcon(long hIcon, int bsize) { if (hIcon != 0L && hIcon != -1L) { // Get the bits. This has the side effect of setting the imageHash value for this object. final int[] iconBits = getIconBits(hIcon); if (iconBits != null) { // icons are always square final int size = (int) Math.sqrt(iconBits.length); - final int baseSize = getLargeIcon ? 32 : 16; final BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); img.setRGB(0, 0, size, size, iconBits, 0, size); - return size == baseSize + return size == bsize ? img - : new MultiResolutionIconImage(baseSize, img); + : new MultiResolutionIconImage(bsize, img); } } return null; @@ -1029,6 +1028,7 @@ */ public Image getIcon(final boolean getLargeIcon) { Image icon = getLargeIcon ? largeIcon : smallIcon; + int size = getLargeIcon ? 32 : 16; if (icon == null) { icon = invoke(new Callable() { @@ -1058,7 +1058,7 @@ newIcon = imageCache.get(Integer.valueOf(index)); if (newIcon == null) { long hIcon = getIcon(getAbsolutePath(), getLargeIcon); - newIcon = makeIcon(hIcon, getLargeIcon); + newIcon = makeIcon(hIcon, size); disposeIcon(hIcon); if (newIcon != null) { imageCache.put(Integer.valueOf(index), newIcon); @@ -1070,20 +1070,20 @@ if (newIcon == null) { // These are only cached per object long hIcon = extractIcon(getParentIShellFolder(), - getRelativePIDL(), getLargeIcon, false); + getRelativePIDL(), size, false); // E_PENDING: loading can take time so get the default if(hIcon <= 0) { hIcon = extractIcon(getParentIShellFolder(), - getRelativePIDL(), getLargeIcon, true); + getRelativePIDL(), size, true); if(hIcon <= 0) { if (isDirectory()) { - return getShell32Icon(4, getLargeIcon); + return getShell32Icon(4, size); } else { - return getShell32Icon(1, getLargeIcon); + return getShell32Icon(1, size); } } } - newIcon = makeIcon(hIcon, getLargeIcon); + newIcon = makeIcon(hIcon, size); disposeIcon(hIcon); } @@ -1093,21 +1093,46 @@ return newIcon; } }); - if (getLargeIcon) { - largeIcon = icon; - } else { - smallIcon = icon; - } } return icon; } + public Image getIcon(int size) { + return invoke(() -> { + Image newIcon = null; + if (isLink()) { + Win32ShellFolder2 folder = getLinkLocation(false); + if (folder != null && folder.isLibrary()) { + return folder.getIcon(size); + } + } + long hIcon = extractIcon(getParentIShellFolder(), + getRelativePIDL(), size, false); + + // E_PENDING: loading can take time so get the default + if(hIcon <= 0) { + hIcon = extractIcon(getParentIShellFolder(), + getRelativePIDL(), size, true); + if(hIcon <= 0) { + if (isDirectory()) { + return getShell32Icon(4, size); + } else { + return getShell32Icon(1, size); + } + } + } + newIcon = makeIcon(hIcon, size); + disposeIcon(hIcon); + return newIcon; + }); + } + /** * Gets an icon from the Windows system icon list as an {@code Image} */ static Image getSystemIcon(SystemIcon iconType) { long hIcon = getSystemIcon(iconType.getIconID()); - Image icon = makeIcon(hIcon, true); + Image icon = makeIcon(hIcon, 32); disposeIcon(hIcon); return icon; } @@ -1115,11 +1140,8 @@ /** * Gets an icon from the Windows system icon list as an {@code Image} */ - static Image getShell32Icon(int iconID, boolean getLargeIcon) { + static Image getShell32Icon(int iconID, int size) { boolean useVGAColors = true; // Will be ignored on XP and later - - int size = getLargeIcon ? 32 : 16; - Toolkit toolkit = Toolkit.getDefaultToolkit(); String shellIconBPP = (String)toolkit.getDesktopProperty("win.icon.shellIconBPP"); if (shellIconBPP != null) { @@ -1128,7 +1150,7 @@ long hIcon = getIconResource("shell32.dll", iconID, size, size, useVGAColors); if (hIcon != 0) { - Image icon = makeIcon(hIcon, getLargeIcon); + Image icon = makeIcon(hIcon, size); disposeIcon(hIcon); return icon; } --- old/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java 2018-07-20 11:47:13.825218400 +0530 +++ new/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java 2018-07-20 11:47:12.393199700 +0530 @@ -379,7 +379,8 @@ try { int i = Integer.parseInt(name); if (i >= 0) { - return Win32ShellFolder2.getShell32Icon(i, key.startsWith("shell32LargeIcon ")); + return Win32ShellFolder2.getShell32Icon(i, key.startsWith("shell32LargeIcon ")? + 32 : 16); } } catch (NumberFormatException ex) { } --- /dev/null 2018-07-20 11:47:23.000000000 +0530 +++ new/test/jdk/javax/swing/JFileChooser/FileSystemView/SystemIconTest.java 2018-07-20 11:47:20.858700900 +0530 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018, 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. + * + * 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. + */ + +/* + * @test + * @bug 8182043 + * @summary Access to Windows Large Icons + * sun.awt.shell.ShellFolder + * @run main SystemIconTest + */ + +import javax.swing.ImageIcon; +import javax.swing.filechooser.FileSystemView; +import java.io.File; + +public class SystemIconTest { + static final FileSystemView fsv = FileSystemView.getFileSystemView(); + + public static void main(String[] args) { + if(System.getProperty("os.name").toLowerCase().contains("windows")) { + System.out.println("Windows detected: will run sytem icons test"); + testSystemIcon(); + } + + System.out.println("ok"); + } + + static void testSystemIcon() { + String windir = System.getenv("windir"); + testSystemIcon(new File(windir)); + testSystemIcon(new File(windir + "/explorer.exe")); + return; + } + + static void testSystemIcon(File file) { + int[] sizes = new int[] {16, 32, 48, 64, 128}; + for (int size : sizes) { + ImageIcon icon = (ImageIcon)fsv.getSystemIcon(file, size, size); + + //Enable below to see the icon + //JLabel label = new JLabel(icon); + //JOptionPane.showMessageDialog(null, label); + + if (icon.getIconWidth() != size) { + throw new RuntimeException("Wrong icon size " + + icon.getIconWidth() + " when requested " + size); + } + } + } +}