-
Notifications
You must be signed in to change notification settings - Fork 6.2k
8182043: Access to Windows Large Icons #380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||
| /* | ||||||
| * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. | ||||||
| * Copyright (c) 1998, 2020, 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 | ||||||
|
|
@@ -26,6 +26,7 @@ | |||||
| package javax.swing.filechooser; | ||||||
|
|
||||||
| import java.awt.Image; | ||||||
| import java.awt.image.AbstractMultiResolutionImage; | ||||||
| import java.beans.PropertyChangeListener; | ||||||
| import java.io.File; | ||||||
| import java.io.FileNotFoundException; | ||||||
|
|
@@ -255,6 +256,54 @@ public Icon getSystemIcon(File f) { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Icon for a file, directory, or folder as it would be displayed in | ||||||
| * a system file browser for the requested size. | ||||||
| * | ||||||
| * The default implementation gets information from the | ||||||
| * <code>ShellFolder</code> class. Whenever possible the icon | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Do we continue using |
||||||
| * returned will be a multi-resolution icon image, | ||||||
| * which will allow better scaling with different | ||||||
| * magnification factors. | ||||||
| * | ||||||
| * Example: <pre> | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| * FileSystemView fsv = FileSystemView.getFileSystemView(); | ||||||
| * Icon icon = fsv.getSystemIcon("application.exe", 64); | ||||||
| * JLabel label = new JLabel(icon); | ||||||
| * </pre> | ||||||
| * | ||||||
| * @param f a <code>File</code> object | ||||||
| * @param size width and height of the icon in pixels | ||||||
| * @return an icon as it would be displayed by a native file chooser | ||||||
| * or null if invalid parameters are passed such as pointer to a | ||||||
| * non-existing file. | ||||||
| * @see JFileChooser#getIcon | ||||||
| * @see AbstractMultiResolutionImage | ||||||
| * @since 16 | ||||||
| */ | ||||||
| public Icon getSystemIcon(File f, int size) { | ||||||
| if (f == null) { | ||||||
| return null; | ||||||
| } | ||||||
|
|
||||||
| ShellFolder sf; | ||||||
|
|
||||||
| try { | ||||||
| sf = ShellFolder.getShellFolder(f); | ||||||
| } catch (FileNotFoundException e) { | ||||||
| return null; | ||||||
| } | ||||||
|
|
||||||
| Image img = sf.getIcon(size); | ||||||
|
|
||||||
| if (img != null) { | ||||||
| return new ImageIcon(img, sf.getFolderType()); | ||||||
| } else { | ||||||
| return UIManager.getIcon(f.isDirectory() ? "FileView.directoryIcon" | ||||||
| : "FileView.fileIcon"); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * On Windows, a file can appear in multiple folders, other than its | ||||||
| * parent directory in the filesystem. Folder could for example be the | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||
| /* | ||||||
| * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. | ||||||
| * Copyright (c) 2000, 2020, 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 | ||||||
|
|
@@ -201,6 +201,15 @@ public Image getIcon(boolean getLargeIcon) { | |||||
| return null; | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Returns the icon of the specified size used to display this shell folder. | ||||||
| * | ||||||
| * @param size size of the icon > 0(Valid range: 1 to 256) | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| * @return The icon of the specified size used to display this shell folder | ||||||
| */ | ||||||
| public Image getIcon(int size) { | ||||||
| return null; | ||||||
| } | ||||||
|
|
||||||
| // Static | ||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||
| /* | ||||||
| * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. | ||||||
| * Copyright (c) 2003, 2020, 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 | ||||||
|
|
@@ -74,6 +74,16 @@ | |||||
| @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 MIN_QUALITY_ICON = 16; | ||||||
| static final int MAX_QUALITY_ICON = 256; | ||||||
| private final static int[] ICON_RESOLUTIONS | ||||||
| = {16, 24, 32, 48, 64, 72, 96, 128, 256}; | ||||||
|
|
||||||
| static final int FILE_ICON_ID = 1; | ||||||
| static final int FOLDER_ICON_ID = 4; | ||||||
|
|
||||||
| private static native void initIDs(); | ||||||
|
|
||||||
| static { | ||||||
|
|
@@ -982,7 +992,10 @@ public String getExecutableType() { | |||||
|
|
||||||
| // 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); | ||||||
|
|
||||||
| // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details | ||||||
| private static native boolean hiResIconAvailable(long parentIShellFolder, long relativePIDL); | ||||||
|
|
||||||
| // Returns an icon from the Windows system icon list in the form of an HICON | ||||||
| private static native long getSystemIcon(int iconID); | ||||||
|
|
@@ -1009,20 +1022,17 @@ private long getIShellIcon() { | |||||
| return pIShellIcon; | ||||||
| } | ||||||
|
|
||||||
| private static Image makeIcon(long hIcon, boolean getLargeIcon) { | ||||||
| private static Image makeIcon(long hIcon, int size) { | ||||||
| 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 int iconSize = (int) Math.sqrt(iconBits.length); | ||||||
| final BufferedImage img = | ||||||
| new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); | ||||||
| img.setRGB(0, 0, size, size, iconBits, 0, size); | ||||||
| return size == baseSize | ||||||
| ? img | ||||||
| : new MultiResolutionIconImage(baseSize, img); | ||||||
| new BufferedImage(iconSize, iconSize, BufferedImage.TYPE_INT_ARGB); | ||||||
| img.setRGB(0, 0, iconSize, iconSize, iconBits, 0, iconSize); | ||||||
| return img; | ||||||
|
Comment on lines
+1033
to
+1035
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are cases where the size of the buffered image is different from the requested size. It could affect the layout because it breaks the assumption that the returned image has the requested size but it may be larger. |
||||||
| } | ||||||
| } | ||||||
| return null; | ||||||
|
|
@@ -1034,11 +1044,13 @@ private static Image makeIcon(long hIcon, boolean getLargeIcon) { | |||||
| */ | ||||||
| public Image getIcon(final boolean getLargeIcon) { | ||||||
| Image icon = getLargeIcon ? largeIcon : smallIcon; | ||||||
| int size = getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE; | ||||||
| if (icon == null) { | ||||||
| icon = | ||||||
| invoke(new Callable<Image>() { | ||||||
| public Image call() { | ||||||
| Image newIcon = null; | ||||||
| Image newIcon2 = null; | ||||||
| if (isLink()) { | ||||||
| Win32ShellFolder2 folder = getLinkLocation(false); | ||||||
| if (folder != null && folder.isLibrary()) { | ||||||
|
|
@@ -1063,33 +1075,40 @@ public Image call() { | |||||
| newIcon = imageCache.get(Integer.valueOf(index)); | ||||||
| if (newIcon == null) { | ||||||
| long hIcon = getIcon(getAbsolutePath(), getLargeIcon); | ||||||
| newIcon = makeIcon(hIcon, getLargeIcon); | ||||||
| newIcon = makeIcon(hIcon, getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE); | ||||||
| disposeIcon(hIcon); | ||||||
| if (newIcon != null) { | ||||||
| imageCache.put(Integer.valueOf(index), newIcon); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if (newIcon == null) { | ||||||
| // These are only cached per object | ||||||
| long hIcon = extractIcon(getParentIShellFolder(), | ||||||
| getRelativePIDL(), getLargeIcon, false); | ||||||
| // E_PENDING: loading can take time so get the default | ||||||
| if(hIcon <= 0) { | ||||||
| hIcon = extractIcon(getParentIShellFolder(), | ||||||
| getRelativePIDL(), getLargeIcon, true); | ||||||
| if(hIcon <= 0) { | ||||||
| if (isDirectory()) { | ||||||
| return getShell32Icon(4, getLargeIcon); | ||||||
| if (newIcon != null) { | ||||||
| if (isLink()) { | ||||||
| imageCache = getLargeIcon ? smallLinkedSystemImages | ||||||
| : largeLinkedSystemImages; | ||||||
| } else { | ||||||
| return getShell32Icon(1, getLargeIcon); | ||||||
| imageCache = getLargeIcon ? smallSystemImages : largeSystemImages; | ||||||
| } | ||||||
| newIcon2 = imageCache.get(Integer.valueOf(index)); | ||||||
| if (newIcon2 == null) { | ||||||
| long hIcon = getIcon(getAbsolutePath(), !getLargeIcon); | ||||||
| newIcon2 = makeIcon(hIcon, getLargeIcon ? SMALL_ICON_SIZE | ||||||
| : LARGE_ICON_SIZE); | ||||||
| disposeIcon(hIcon); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if (newIcon2 != null) { | ||||||
| Map<Integer, Image> bothIcons = new HashMap<>(2); | ||||||
| bothIcons.put(getLargeIcon? LARGE_ICON_SIZE : SMALL_ICON_SIZE, newIcon); | ||||||
| bothIcons.put(getLargeIcon? SMALL_ICON_SIZE : LARGE_ICON_SIZE, newIcon2); | ||||||
| newIcon = new MultiResolutionIconImage(getLargeIcon ? LARGE_ICON_SIZE | ||||||
| : SMALL_ICON_SIZE, bothIcons); | ||||||
|
Comment on lines
+1091
to
+1105
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I propose to refactor this code into a separate method which always fetches small and large icon and puts into a corresponding cache. However, I still think there's not much value in getting smaller icon size in addition to larger one. Or does it provide an alternative image for the case where the system scaling is 200% and the window is moved to a monitor with scaling of 100%? |
||||||
| } | ||||||
| } | ||||||
| newIcon = makeIcon(hIcon, getLargeIcon); | ||||||
| disposeIcon(hIcon); | ||||||
| } | ||||||
|
|
||||||
| if (hiResIconAvailable(getParentIShellFolder(), getRelativePIDL()) || newIcon == null) { | ||||||
| newIcon = getIcon(getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE); | ||||||
| } | ||||||
|
|
||||||
| if (newIcon == null) { | ||||||
|
|
@@ -1098,33 +1117,73 @@ public Image call() { | |||||
| return newIcon; | ||||||
| } | ||||||
| }); | ||||||
| if (getLargeIcon) { | ||||||
| largeIcon = icon; | ||||||
| } else { | ||||||
| smallIcon = icon; | ||||||
| } | ||||||
| } | ||||||
| return icon; | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @return The icon image of specified size used to display this shell folder | ||||||
| */ | ||||||
| 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); | ||||||
| } | ||||||
| } | ||||||
| Map<Integer, Image> multiResolutionIcon = new HashMap<>(); | ||||||
| int start = size > MAX_QUALITY_ICON ? ICON_RESOLUTIONS.length - 1 : 0; | ||||||
| int increment = size > MAX_QUALITY_ICON ? -1 : 1; | ||||||
| int end = size > MAX_QUALITY_ICON ? -1 : ICON_RESOLUTIONS.length; | ||||||
| for (int i = start; i != end; i+=increment) { | ||||||
| int s = ICON_RESOLUTIONS[i]; | ||||||
| if (size < MIN_QUALITY_ICON || size > MAX_QUALITY_ICON | ||||||
| || (s >= size/2 && s <= size*2)) { | ||||||
| long hIcon = extractIcon(getParentIShellFolder(), | ||||||
| getRelativePIDL(), s, false); | ||||||
|
|
||||||
| // E_PENDING: loading can take time so get the default | ||||||
| if (hIcon <= 0) { | ||||||
| hIcon = extractIcon(getParentIShellFolder(), | ||||||
| getRelativePIDL(), s, true); | ||||||
| if (hIcon <= 0) { | ||||||
| if (isDirectory()) { | ||||||
| return getShell32Icon(FOLDER_ICON_ID, size); | ||||||
| } else { | ||||||
| return getShell32Icon(FILE_ICON_ID, size); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| newIcon = makeIcon(hIcon, s); | ||||||
| disposeIcon(hIcon); | ||||||
|
|
||||||
| multiResolutionIcon.put(s, newIcon); | ||||||
| if (size < 8 || size > 256) { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| break; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| return new MultiResolutionIconImage(size, multiResolutionIcon); | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * 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, LARGE_ICON_SIZE); | ||||||
| disposeIcon(hIcon); | ||||||
| return icon; | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * 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) { | ||||||
|
|
@@ -1133,7 +1192,7 @@ static Image getShell32Icon(int iconID, boolean getLargeIcon) { | |||||
|
|
||||||
| 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; | ||||||
| } | ||||||
|
|
@@ -1316,13 +1375,17 @@ public List<KnownFolderDefinition> call() throws Exception { | |||||
| } | ||||||
|
|
||||||
| static class MultiResolutionIconImage extends AbstractMultiResolutionImage { | ||||||
|
|
||||||
| final int baseSize; | ||||||
| final Image resolutionVariant; | ||||||
| final Map<Integer, Image> resolutionVariants = new HashMap<>(); | ||||||
|
|
||||||
| public MultiResolutionIconImage(int baseSize, Image resolutionVariant) { | ||||||
| public MultiResolutionIconImage(int baseSize, Map<Integer, Image> resolutionVariants) { | ||||||
| this.baseSize = baseSize; | ||||||
| this.resolutionVariant = resolutionVariant; | ||||||
| this.resolutionVariants.putAll(resolutionVariants); | ||||||
| } | ||||||
|
|
||||||
| public MultiResolutionIconImage(int baseSize, Image image) { | ||||||
| this.baseSize = baseSize; | ||||||
| this.resolutionVariants.put(baseSize, image); | ||||||
| } | ||||||
|
|
||||||
| @Override | ||||||
|
|
@@ -1337,17 +1400,34 @@ public int getHeight(ImageObserver observer) { | |||||
|
|
||||||
| @Override | ||||||
| protected Image getBaseImage() { | ||||||
| return resolutionVariant; | ||||||
| return getResolutionVariant(baseSize, baseSize); | ||||||
| } | ||||||
|
|
||||||
| @Override | ||||||
| public Image getResolutionVariant(double width, double height) { | ||||||
| return resolutionVariant; | ||||||
| int dist = 0; | ||||||
| Image retVal = null; | ||||||
| // We only care about width since we don't support non-rectangular icons | ||||||
| int w = (int) width; | ||||||
| int retindex = 0; | ||||||
| for (Integer i: resolutionVariants.keySet()) { | ||||||
| if (retVal == null || dist > Math.abs(i - w) | ||||||
| || (dist == Math.abs(i - w) && i > retindex)) { | ||||||
| retindex = i; | ||||||
| dist = Math.abs(i - w); | ||||||
| retVal = resolutionVariants.get(i); | ||||||
| if (i == w) { | ||||||
| break; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| return retVal; | ||||||
| } | ||||||
|
|
||||||
| @Override | ||||||
| public List<Image> getResolutionVariants() { | ||||||
| return Arrays.asList(resolutionVariant); | ||||||
| return Collections.unmodifiableList( | ||||||
| new ArrayList<Image>(resolutionVariants.values())); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.