--- old/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java 2017-09-26 13:52:44.429664600 -0700 +++ new/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java 2017-09-26 13:52:43.949663900 -0700 @@ -74,6 +74,20 @@ UIManager.getDefaults().getBoolean("FileChooser.useSystemExtensionHiding"); /** + * An icon image query option that requests the small file icon. + * + * @since 10 + */ + public static final int FILE_ICON_SMALL = -1; + + /** + * An icon image query option that requests the large file icon. + * + * @since 10 + */ + public static final int FILE_ICON_LARGE = -2; + + /** * Returns the file system view. * @return the file system view */ @@ -257,6 +271,48 @@ } /** + * Returns icon image if it is associated with the specified file or + * {@code null} otherwise. Depending on the passed {@code size} different + * sizes of file icon may be queried. If value of the {@code size} argument + * equals to {@code #FILE_ICON_SMALL} or {@code #FILE_ICON_LARGE} then + * the small or large file icon variant is returned correspondingly. + * For any positive size value the exact file icon size is queried. + * + * @param file a file + * @param size size to query + * @return file icon + * @throws NullPointerException if {@code file} equals {@code null} + * @see #FILE_ICON_SMALL + * @see #FILE_ICON_LARGE + * @since 10 + */ + public ImageIcon getSystemIcon(File file, int size) { + if (file == null) { + throw new NullPointerException("file is null"); + } + ShellFolder sf; + try { + sf = ShellFolder.getShellFolder(file); + } catch (FileNotFoundException e) { + return null; + } + Image img = null; + if (size == FILE_ICON_SMALL) { + img = sf.getIcon(false); + } else if (size == FILE_ICON_LARGE) { + img = sf.getIcon(true); + } else if (size > 0) { + img = sf.getIcon(size); + } else { + throw new IllegalArgumentException("Wrong size value +" + size); + } + if (img != null) { + return new ImageIcon(img, sf.getFolderType()); + } + return null; + } + + /** * 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 2017-09-26 13:52:47.275672800 -0700 +++ new/src/java.desktop/share/classes/sun/awt/shell/ShellFolder.java 2017-09-26 13:52:46.815672200 -0700 @@ -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 2017-09-26 13:52:49.903680800 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp 2017-09-26 13:52:49.445679200 -0700 @@ -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 sz, 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); - } - } + pIcon->Extract(szBuf, index, &hIcon, 0, sz); } else if (hres == E_PENDING) { pIcon->Release(); return E_PENDING; --- old/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java 2017-09-26 13:52:52.574686900 -0700 +++ new/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java 2017-09-26 13:52:52.109685900 -0700 @@ -74,6 +74,12 @@ @SuppressWarnings("serial") // JDK-implementation class final class Win32ShellFolder2 extends ShellFolder { + static final int SMALL_ICON_SIZE = 16; + static final int LARGE_ICON_SIZE = 32; + + static final int FILE_ICON_ID = 1; + static final int FOLDER_ICON_ID = 4; + private static native void initIDs(); static { @@ -977,7 +983,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,30 +1010,58 @@ 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; } + 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(FOLDER_ICON_ID, size); + } else { + return getShell32Icon(FILE_ICON_ID, size); + } + } + } + newIcon = makeIcon(hIcon, size); + disposeIcon(hIcon); + return newIcon; + }); + } /** * @return The icon image used to display this shell folder */ public Image getIcon(final boolean getLargeIcon) { + int size = getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE; Image icon = getLargeIcon ? largeIcon : smallIcon; if (icon == null) { icon = @@ -1058,7 +1092,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,34 +1104,28 @@ 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(FOLDER_ICON_ID, + size); } else { - return getShell32Icon(1, getLargeIcon); + return getShell32Icon(FILE_ICON_ID, + size); } } } - newIcon = makeIcon(hIcon, getLargeIcon); + newIcon = makeIcon(hIcon, size); disposeIcon(hIcon); } - if (newIcon == null) { - newIcon = Win32ShellFolder2.super.getIcon(getLargeIcon); - } return newIcon; } }); - if (getLargeIcon) { - largeIcon = icon; - } else { - smallIcon = icon; - } } return icon; } @@ -1107,7 +1135,7 @@ */ static Image getSystemIcon(SystemIcon iconType) { long hIcon = getSystemIcon(iconType.getIconID()); - Image icon = makeIcon(hIcon, true); + Image icon = makeIcon(hIcon, LARGE_ICON_SIZE); disposeIcon(hIcon); return icon; } @@ -1115,11 +1143,9 @@ /** * 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 +1154,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 2017-09-26 13:52:55.243691100 -0700 +++ new/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java 2017-09-26 13:52:54.783690400 -0700 @@ -126,9 +126,9 @@ new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); img.setRGB(0, 0, size, size, iconBits, 0, size); - STANDARD_VIEW_BUTTONS[iconIndex] = (size == 16) + STANDARD_VIEW_BUTTONS[iconIndex] = (size == SMALL_ICON_SIZE) ? img - : new MultiResolutionIconImage(16, img); + : new MultiResolutionIconImage(SMALL_ICON_SIZE, img); } return STANDARD_VIEW_BUTTONS[iconIndex]; @@ -379,7 +379,9 @@ try { int i = Integer.parseInt(name); if (i >= 0) { - return Win32ShellFolder2.getShell32Icon(i, key.startsWith("shell32LargeIcon ")); + return Win32ShellFolder2.getShell32Icon(i, + key.startsWith("shell32LargeIcon ") ? + LARGE_ICON_SIZE : SMALL_ICON_SIZE); } } catch (NumberFormatException ex) { } --- old/test/javax/swing/JFileChooser/ShellFolderQueries/ShellFolderQueriesTest.java 2017-09-26 13:52:57.899697400 -0700 +++ new/test/javax/swing/JFileChooser/ShellFolderQueries/ShellFolderQueriesTest.java 2017-09-26 13:52:57.429695600 -0700 @@ -23,7 +23,7 @@ /* * @test - * @bug 8081722 + * @bug 8081722 8182043 * @summary Provide public API for file hierarchy provided by * sun.awt.shell.ShellFolder * @author Semyon Sadetsky @@ -31,6 +31,7 @@ */ +import javax.swing.*; import javax.swing.filechooser.FileSystemView; import java.io.File; import java.io.FileNotFoundException; @@ -50,9 +51,11 @@ public static void main(String[] args) throws Exception { if(System.getProperty("os.name").toLowerCase().contains("windows")) { - System.out.println("Windows detected: will run shortcut test"); + System.out.println("Windows detected: will run shortcut " + + "and sytem icons tests"); testGet(); testLink(); + testSystemIcon(); } else { testGet(); } @@ -119,4 +122,33 @@ } } } + + static void testSystemIcon() { + try { + fsv.getSystemIcon(null, FileSystemView.FILE_ICON_SMALL); + } catch (NullPointerException e) { + String windir = System.getenv("windir"); + testSystemIcon(new File(windir)); + testSystemIcon(new File(windir + "/explorer.exe")); + return; + } + throw new RuntimeException("NPE was not thrown"); + } + + static void testSystemIcon(File file) { + ImageIcon icon = fsv.getSystemIcon(file, FileSystemView.FILE_ICON_SMALL); + System.out.println("Small icon size " + icon.getIconWidth()); + + icon = fsv.getSystemIcon(file, FileSystemView.FILE_ICON_LARGE); + System.out.println("Large icon size " + icon.getIconWidth()); + + int[] sizes = new int[] {16, 24, 32, 48, 64, 128, 50}; + for (int size : sizes) { + icon = fsv.getSystemIcon(file, size); + if (icon.getIconWidth() != size) { + throw new RuntimeException("Wrong icon size " + + icon.getIconWidth() + " when requested " + size); + } + } + } }